xref: /netbsd-src/libexec/rexecd/rexecd.c (revision 0c4ddb1599a0bea866fde8522a74cfbd2f68cd1b)
1*0c4ddb15Slukem /*	$NetBSD: rexecd.c,v 1.26 2008/07/20 01:09:07 lukem Exp $	*/
2da5c78b3Smrg 
361f28255Scgd /*
4da5c78b3Smrg  * Copyright (c) 1983, 1993
5da5c78b3Smrg  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
158e6ab883Sagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
32da5c78b3Smrg #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
34*0c4ddb15Slukem __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35*0c4ddb15Slukem  The Regents of the University of California.  All rights reserved.");
36da5c78b3Smrg #if 0
37da5c78b3Smrg static char sccsid[] = "from: @(#)rexecd.c	8.1 (Berkeley) 6/4/93";
38da5c78b3Smrg #else
39*0c4ddb15Slukem __RCSID("$NetBSD: rexecd.c,v 1.26 2008/07/20 01:09:07 lukem Exp $");
40da5c78b3Smrg #endif
4161f28255Scgd #endif /* not lint */
4261f28255Scgd 
4361f28255Scgd #include <sys/ioctl.h>
442e7a44f2Schristos #include <sys/param.h>
4561f28255Scgd #include <sys/socket.h>
46fc0b820cSmrg #include <sys/syslog.h>
4761f28255Scgd #include <sys/time.h>
48da5c78b3Smrg 
4961f28255Scgd #include <netinet/in.h>
50da5c78b3Smrg 
51fc0b820cSmrg #include <err.h>
5261f28255Scgd #include <errno.h>
53da5c78b3Smrg #include <netdb.h>
54da5c78b3Smrg #include <paths.h>
552e7a44f2Schristos #include <poll.h>
56da5c78b3Smrg #include <pwd.h>
57da5c78b3Smrg #include <signal.h>
58bf840df2Swiz #include <stdarg.h>
5961f28255Scgd #include <stdio.h>
6061f28255Scgd #include <stdlib.h>
6161f28255Scgd #include <string.h>
62da5c78b3Smrg #include <unistd.h>
6361f28255Scgd 
6462136d56Schristos #ifdef USE_PAM
6562136d56Schristos #include <security/pam_appl.h>
6662136d56Schristos #include <security/openpam.h>
6762136d56Schristos #endif
6861f28255Scgd 
6962136d56Schristos int main(int, char *[]);
7062136d56Schristos static void rexecd_errx(int, const char *, ...)
7162136d56Schristos      __attribute__((__noreturn__, __format__(__printf__, 2, 3)));
728b0f9554Sperry static void doit(struct sockaddr *) __dead;
7362136d56Schristos static void getstr(char *, int, const char *);
748b0f9554Sperry static void usage(void) __dead;
7562136d56Schristos 
7662136d56Schristos #ifdef USE_PAM
7762136d56Schristos static pam_handle_t *pamh;
7862136d56Schristos static struct pam_conv pamc = {
7962136d56Schristos 	openpam_nullconv,
8062136d56Schristos 	NULL
8162136d56Schristos };
8262136d56Schristos static int pam_flags = PAM_SILENT|PAM_DISALLOW_NULL_AUTHTOK;
8362136d56Schristos static int pam_err;
8462136d56Schristos #define pam_ok(err) ((pam_err = (err)) == PAM_SUCCESS)
8562136d56Schristos #endif
8662136d56Schristos 
872e7a44f2Schristos extern char	**environ;
882e7a44f2Schristos static int	dolog;
8962136d56Schristos #ifndef USE_PAM
9062136d56Schristos static char	username[32 + 1] = "USER=";
9162136d56Schristos static char	logname[32 + 3 + 1] = "LOGNAME=";
9262136d56Schristos static char	homedir[PATH_MAX + 1] = "HOME=";
9362136d56Schristos static char	shell[PATH_MAX + 1] = "SHELL=";
9462136d56Schristos static char	path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH=";
9562136d56Schristos static char	*envinit[] = { homedir, shell, path, username, logname, 0 };
9662136d56Schristos #endif
97fc0b820cSmrg 
9861f28255Scgd /*
9961f28255Scgd  * remote execute server:
10061f28255Scgd  *	username\0
10161f28255Scgd  *	password\0
10261f28255Scgd  *	command\0
10361f28255Scgd  *	data
10461f28255Scgd  */
105da5c78b3Smrg int
main(int argc,char * argv[])1063596f57fSginsbach main(int argc, char *argv[])
10761f28255Scgd {
108abec5636Sitojun 	struct sockaddr_storage from;
10962136d56Schristos 	socklen_t fromlen;
11062136d56Schristos 	int ch;
11161f28255Scgd 
112fc0b820cSmrg 	while ((ch = getopt(argc, argv, "l")) != -1)
113fc0b820cSmrg 		switch (ch) {
114fc0b820cSmrg 		case 'l':
1157ec31d73Sthorpej 			dolog = 1;
116fc0b820cSmrg 			openlog("rexecd", LOG_PID, LOG_DAEMON);
117fc0b820cSmrg 			break;
118fc0b820cSmrg 		default:
11962136d56Schristos 			usage();
12061f28255Scgd 		}
121fc0b820cSmrg 
122fc0b820cSmrg 	fromlen = sizeof(from);
12362136d56Schristos 	if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0)
1242e7a44f2Schristos 		err(EXIT_FAILURE, "getpeername");
125fc0b820cSmrg 
12662136d56Schristos 	if (((struct sockaddr *)&from)->sa_family == AF_INET6 &&
12762136d56Schristos 	    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) {
12862136d56Schristos 		char hbuf[NI_MAXHOST];
12962136d56Schristos 		if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf,
13062136d56Schristos 		    sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
13162136d56Schristos 			(void)strlcpy(hbuf, "invalid", sizeof(hbuf));
13262136d56Schristos 		}
13362136d56Schristos 		if (dolog)
13462136d56Schristos 			syslog(LOG_ERR,
13562136d56Schristos 			    "malformed \"from\" address (v4 mapped, %s)",
13662136d56Schristos 			    hbuf);
1372e7a44f2Schristos 		return EXIT_FAILURE;
13862136d56Schristos 	}
13962136d56Schristos 
14062136d56Schristos 	doit((struct sockaddr *)&from);
14161f28255Scgd }
14261f28255Scgd 
143da5c78b3Smrg void
doit(struct sockaddr * fromp)14462136d56Schristos doit(struct sockaddr *fromp)
14561f28255Scgd {
146fc0b820cSmrg 	struct pollfd fds[2];
14762136d56Schristos 	char cmdbuf[NCARGS + 1];
1485dd823abSmycroft 	const char *cp;
14961f28255Scgd 	char user[16], pass[16];
150fc0b820cSmrg 	char buf[BUFSIZ], sig;
1512e7a44f2Schristos 	struct passwd *pwd, pwres;
152da5c78b3Smrg 	int s = -1; /* XXX gcc */
153fc0b820cSmrg 	int pv[2], pid, cc;
15461f28255Scgd 	int one = 1;
155fc0b820cSmrg 	in_port_t port;
15662136d56Schristos 	char hostname[2 * MAXHOSTNAMELEN + 1];
15762136d56Schristos 	char pbuf[NI_MAXSERV];
15862136d56Schristos 	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
15962136d56Schristos #ifndef USE_PAM
16062136d56Schristos 	char *namep;
16162136d56Schristos #endif
1622e7a44f2Schristos 	char pwbuf[1024];
16361f28255Scgd 
16461f28255Scgd 	(void)signal(SIGINT, SIG_DFL);
16561f28255Scgd 	(void)signal(SIGQUIT, SIG_DFL);
16661f28255Scgd 	(void)signal(SIGTERM, SIG_DFL);
16762136d56Schristos 	(void)dup2(STDIN_FILENO, STDOUT_FILENO);
16862136d56Schristos 	(void)dup2(STDIN_FILENO, STDERR_FILENO);
16962136d56Schristos 
17062136d56Schristos 	if (getnameinfo(fromp, fromp->sa_len, hostname, sizeof(hostname),
17162136d56Schristos 	    pbuf, sizeof(pbuf), niflags) != 0) {
17262136d56Schristos 		if (dolog)
17362136d56Schristos 			syslog(LOG_ERR, "malformed \"from\" address (af %d)",
17462136d56Schristos 			       fromp->sa_family);
1752e7a44f2Schristos 		exit(EXIT_FAILURE);
17662136d56Schristos 	}
17762136d56Schristos 
17861f28255Scgd 	(void)alarm(60);
17961f28255Scgd 	port = 0;
18061f28255Scgd 	for (;;) {
18161f28255Scgd 		char c;
18262136d56Schristos 		if (read(STDIN_FILENO, &c, 1) != 1) {
1837ec31d73Sthorpej 			if (dolog)
18462136d56Schristos 				syslog(LOG_ERR, "initial read failed");
1852e7a44f2Schristos 			exit(EXIT_FAILURE);
186fc0b820cSmrg 		}
18761f28255Scgd 		if (c == 0)
18861f28255Scgd 			break;
18961f28255Scgd 		port = port * 10 + c - '0';
19061f28255Scgd 	}
19161f28255Scgd 	if (port != 0) {
192abec5636Sitojun 		s = socket(fromp->sa_family, SOCK_STREAM, 0);
193fc0b820cSmrg 		if (s < 0) {
1947ec31d73Sthorpej 			if (dolog)
195fc0b820cSmrg 				syslog(LOG_ERR, "socket: %m");
1962e7a44f2Schristos 			exit(EXIT_FAILURE);
197fc0b820cSmrg 		}
198abec5636Sitojun 		(void)alarm(60);
199abec5636Sitojun 		switch (fromp->sa_family) {
200abec5636Sitojun 		case AF_INET:
201abec5636Sitojun 			((struct sockaddr_in *)fromp)->sin_port = htons(port);
202abec5636Sitojun 			break;
203abec5636Sitojun 		case AF_INET6:
204abec5636Sitojun 			((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
205abec5636Sitojun 			break;
206abec5636Sitojun 		default:
207abec5636Sitojun 			syslog(LOG_ERR, "unsupported address family");
2082e7a44f2Schristos 			exit(EXIT_FAILURE);
209fc0b820cSmrg 		}
210abec5636Sitojun 		if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) {
2117ec31d73Sthorpej 			if (dolog)
212fc0b820cSmrg 				syslog(LOG_ERR, "connect: %m");
2132e7a44f2Schristos 			exit(EXIT_FAILURE);
214fc0b820cSmrg 		}
21561f28255Scgd 		(void)alarm(0);
21661f28255Scgd 	}
21762136d56Schristos 	(void)alarm(60);
21861f28255Scgd 	getstr(user, sizeof(user), "username");
21961f28255Scgd 	getstr(pass, sizeof(pass), "password");
22061f28255Scgd 	getstr(cmdbuf, sizeof(cmdbuf), "command");
22162136d56Schristos 	(void)alarm(0);
222cce62d09Schristos 	if (getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
223cce62d09Schristos 	    pwd == NULL) {
2247ec31d73Sthorpej 		if (dolog)
225fc0b820cSmrg 			syslog(LOG_ERR, "no such user %s", user);
2262e7a44f2Schristos 		rexecd_errx(EXIT_FAILURE, "Login incorrect.");
22761f28255Scgd 	}
22862136d56Schristos #ifdef USE_PAM
22962136d56Schristos 	if (!pam_ok(pam_start("rexecd", user, &pamc, &pamh)) ||
23062136d56Schristos 	    !pam_ok(pam_set_item(pamh, PAM_RHOST, hostname)) ||
23162136d56Schristos 	    !pam_ok(pam_set_item(pamh, PAM_AUTHTOK, pass))) {
23262136d56Schristos 		if (dolog)
23362136d56Schristos 			syslog(LOG_ERR, "PAM ERROR %s@%s (%s)", user,
23462136d56Schristos 			   hostname, pam_strerror(pamh, pam_err));
2352e7a44f2Schristos 		rexecd_errx(EXIT_FAILURE, "Try again.");
23662136d56Schristos 	}
23762136d56Schristos 	if (!pam_ok(pam_authenticate(pamh, pam_flags)) ||
23862136d56Schristos 	    !pam_ok(pam_acct_mgmt(pamh, pam_flags))) {
23962136d56Schristos 		if (dolog)
24062136d56Schristos 			syslog(LOG_ERR, "LOGIN REFUSED for %s@%s (%s)", user,
24162136d56Schristos 			   hostname, pam_strerror(pamh, pam_err));
2422e7a44f2Schristos 		rexecd_errx(EXIT_FAILURE, "Password incorrect.");
24362136d56Schristos 	}
24462136d56Schristos #else
24561f28255Scgd 	if (*pwd->pw_passwd != '\0') {
24661f28255Scgd 		namep = crypt(pass, pwd->pw_passwd);
24762136d56Schristos 		if (strcmp(namep, pwd->pw_passwd) != 0) {
2487ec31d73Sthorpej 			if (dolog)
249fc0b820cSmrg 				syslog(LOG_ERR, "incorrect password for %s",
250fc0b820cSmrg 				    user);
2512e7a44f2Schristos 			rexecd_errx(EXIT_FAILURE,
2522e7a44f2Schristos 				    "Password incorrect.");/* XXX: wrong! */
25361f28255Scgd 		}
254fc0b820cSmrg 	} else
255fc0b820cSmrg 		(void)crypt("dummy password", "PA");    /* must always crypt */
25662136d56Schristos #endif
25761f28255Scgd 	if (chdir(pwd->pw_dir) < 0) {
2587ec31d73Sthorpej 		if (dolog)
259fc0b820cSmrg 			syslog(LOG_ERR, "%s does not exist for %s", pwd->pw_dir,
260fc0b820cSmrg 			       user);
2612e7a44f2Schristos 		rexecd_errx(EXIT_FAILURE, "No remote directory.");
26261f28255Scgd 	}
26362136d56Schristos 
26462136d56Schristos 	if (dolog)
26562136d56Schristos 		syslog(LOG_INFO, "login from %s as %s", hostname, user);
2663596f57fSginsbach 	(void)write(STDERR_FILENO, "\0", 1);
26761f28255Scgd 	if (port) {
268fc0b820cSmrg 		if (pipe(pv) < 0 || (pid = fork()) == -1) {
2697ec31d73Sthorpej 			if (dolog)
270fc0b820cSmrg 				syslog(LOG_ERR,"pipe or fork failed for %s: %m",
271fc0b820cSmrg 				    user);
2722e7a44f2Schristos 			rexecd_errx(EXIT_FAILURE, "Try again.");
27361f28255Scgd 		}
27461f28255Scgd 		if (pid) {
27562136d56Schristos 			/* parent */
27662136d56Schristos #ifdef USE_PAM
27762136d56Schristos 			(void)pam_end(pamh, pam_err);
27862136d56Schristos #endif
2793596f57fSginsbach 			(void)close(STDIN_FILENO);
2803596f57fSginsbach 			(void)close(STDOUT_FILENO);
2813596f57fSginsbach 			(void)close(STDERR_FILENO);
282fc0b820cSmrg 			(void)close(pv[1]);
283fc0b820cSmrg 			fds[0].fd = s;
284fc0b820cSmrg 			fds[1].fd = pv[0];
285fc0b820cSmrg 			fds[0].events = fds[1].events = POLLIN;
286fc0b820cSmrg 			if (ioctl(pv[1], FIONBIO, (char *)&one) < 0)
28714dbdf55Swiz 				_exit(1);
28861f28255Scgd 			/* should set s nbio! */
28961f28255Scgd 			do {
290fc0b820cSmrg 				if (poll(fds, 2, 0) < 0) {
2912e7a44f2Schristos 					(void)close(s);
2922e7a44f2Schristos 					(void)close(pv[0]);
29314dbdf55Swiz 					_exit(1);
294fc0b820cSmrg 				}
295fc0b820cSmrg 				if (fds[0].revents & POLLIN) {
29661f28255Scgd 					if (read(s, &sig, 1) <= 0)
297fc0b820cSmrg 						fds[0].events = 0;
29861f28255Scgd 					else
2992e7a44f2Schristos 						(void)killpg(pid, sig);
30061f28255Scgd 				}
301fc0b820cSmrg 				if (fds[1].revents & POLLIN) {
30261f28255Scgd 					cc = read(pv[0], buf, sizeof (buf));
30361f28255Scgd 					if (cc <= 0) {
3042e7a44f2Schristos 						(void)shutdown(s, SHUT_RDWR);
305fc0b820cSmrg 						fds[1].events = 0;
30661f28255Scgd 					} else
30761f28255Scgd 						(void)write(s, buf, cc);
30861f28255Scgd 				}
309fc0b820cSmrg 			} while ((fds[0].events | fds[1].events) & POLLIN);
310fc0b820cSmrg 			_exit(0);
31161f28255Scgd 		}
31262136d56Schristos 		/* child */
313fc0b820cSmrg 		(void)close(s);
314fc0b820cSmrg 		(void)close(pv[0]);
31562136d56Schristos 		if (dup2(pv[1], STDERR_FILENO) < 0) {
3167ec31d73Sthorpej 			if (dolog)
317fc0b820cSmrg 				syslog(LOG_ERR, "dup2 failed for %s", user);
3182e7a44f2Schristos 			rexecd_errx(EXIT_FAILURE, "Try again.");
319fc0b820cSmrg 		}
32061f28255Scgd 	}
32161f28255Scgd 	if (*pwd->pw_shell == '\0')
32262136d56Schristos 		pwd->pw_shell = __UNCONST(_PATH_BSHELL);
32341880acaSdsl 	if (setsid() < 0 ||
32441880acaSdsl 	    setlogin(pwd->pw_name) < 0 ||
325fc0b820cSmrg 	    initgroups(pwd->pw_name, pwd->pw_gid) < 0 ||
32662136d56Schristos #ifdef USE_PAM
32762136d56Schristos 	    setgid((gid_t)pwd->pw_gid) < 0) {
32862136d56Schristos #else
329fc0b820cSmrg 	    setgid((gid_t)pwd->pw_gid) < 0 ||
330fc0b820cSmrg 	    setuid((uid_t)pwd->pw_uid) < 0) {
33162136d56Schristos #endif
3322e7a44f2Schristos 		rexecd_errx(EXIT_FAILURE, "Try again.");
3337ec31d73Sthorpej 		if (dolog)
334fc0b820cSmrg 			syslog(LOG_ERR, "could not set permissions for %s: %m",
335fc0b820cSmrg 			    user);
3362e7a44f2Schristos 		exit(EXIT_FAILURE);
337fc0b820cSmrg 	}
33862136d56Schristos #ifdef USE_PAM
33962136d56Schristos 	if (!pam_ok(pam_setcred(pamh, PAM_ESTABLISH_CRED)))
34062136d56Schristos 		syslog(LOG_ERR, "pam_setcred() failed: %s",
34162136d56Schristos 		       pam_strerror(pamh, pam_err));
34262136d56Schristos 	(void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
34362136d56Schristos 	(void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
34462136d56Schristos 	(void)pam_setenv(pamh, "USER", pwd->pw_name, 1);
34562136d56Schristos 	(void)pam_setenv(pamh, "LOGNAME", pwd->pw_name, 1);
34662136d56Schristos 	(void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
34762136d56Schristos 	environ = pam_getenvlist(pamh);
34862136d56Schristos 	(void)pam_end(pamh, pam_err);
34962136d56Schristos 	if (setuid((uid_t)pwd->pw_uid) < 0) {
35062136d56Schristos                 if (dolog)
35162136d56Schristos                         syslog(LOG_ERR, "could not set uid for %s: %m",
35262136d56Schristos                             user);
3532e7a44f2Schristos                 rexecd_errx(EXIT_FAILURE, "Try again.");
35462136d56Schristos         }
35562136d56Schristos #else
3563e6b7820Sitojun 	(void)strlcat(path, _PATH_DEFPATH, sizeof(path));
35761f28255Scgd 	environ = envinit;
35862136d56Schristos 	(void)strlcat(homedir, pwd->pw_dir, sizeof(homedir));
35962136d56Schristos 	(void)strlcat(shell, pwd->pw_shell, sizeof(shell));
36062136d56Schristos 	(void)strlcat(username, pwd->pw_name, sizeof(username));
36162136d56Schristos 	(void)strlcat(logname, pwd->pw_name, sizeof(logname));
36262136d56Schristos #endif
36362136d56Schristos 
364da5c78b3Smrg 	cp = strrchr(pwd->pw_shell, '/');
36561f28255Scgd 	if (cp)
36661f28255Scgd 		cp++;
36761f28255Scgd 	else
36861f28255Scgd 		cp = pwd->pw_shell;
3697ec31d73Sthorpej 	if (dolog)
370fc0b820cSmrg 		syslog(LOG_INFO, "running command for %s: %s", user, cmdbuf);
3714bc6feceSmrg 	(void)execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL);
3727ec31d73Sthorpej 	if (dolog)
373fc0b820cSmrg 		syslog(LOG_ERR, "execl failed for %s: %m", user);
3742e7a44f2Schristos 	err(EXIT_FAILURE, "%s", pwd->pw_shell);
37561f28255Scgd }
37661f28255Scgd 
377da5c78b3Smrg void
37862136d56Schristos rexecd_errx(int ex, const char *fmt, ...)
37961f28255Scgd {
38061f28255Scgd 	char buf[BUFSIZ];
381da5c78b3Smrg 	va_list ap;
38262136d56Schristos 	ssize_t len;
383da5c78b3Smrg 
384da5c78b3Smrg 	va_start(ap, fmt);
38561f28255Scgd 	buf[0] = 1;
38662136d56Schristos 	len = vsnprintf(buf + 1, sizeof(buf) - 1, fmt, ap) + 1;
38762136d56Schristos 	buf[len++] = '\n';
38862136d56Schristos 	(void)write(STDERR_FILENO, buf, len);
3894c999163Swiz 	va_end(ap);
39062136d56Schristos 	exit(ex);
39161f28255Scgd }
39261f28255Scgd 
393da5c78b3Smrg void
39462136d56Schristos getstr(char *buf, int cnt, const char *emsg)
39561f28255Scgd {
39661f28255Scgd 	char c;
39761f28255Scgd 
39861f28255Scgd 	do {
3993596f57fSginsbach 		if (read(STDIN_FILENO, &c, 1) != 1)
4002e7a44f2Schristos 			exit(EXIT_FAILURE);
40161f28255Scgd 		*buf++ = c;
40262136d56Schristos 		if (--cnt == 0)
4032e7a44f2Schristos 			rexecd_errx(EXIT_FAILURE, "%s too long", emsg);
40461f28255Scgd 	} while (c != 0);
40561f28255Scgd }
40662136d56Schristos 
40762136d56Schristos static void
40862136d56Schristos usage(void)
40962136d56Schristos {
41062136d56Schristos 	(void)fprintf(stderr, "Usage: %s [-l]\n", getprogname());
4112e7a44f2Schristos 	exit(EXIT_FAILURE);
41262136d56Schristos }
413