xref: /openbsd-src/usr.bin/rdist/common.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: common.c,v 1.26 2011/04/24 02:23:57 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1983 Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "defs.h"
33 
34 /*
35  * Things common to both the client and server.
36  */
37 
38 #if	defined(NEED_UTIME_H)
39 #include <utime.h>
40 #endif	/* defined(NEED_UTIME_H) */
41 #include <sys/wait.h>
42 #include <sys/socket.h>
43 
44 /*
45  * Variables common to both client and server
46  */
47 char			host[MAXHOSTNAMELEN];	/* Name of this host */
48 UID_T			userid = (UID_T)-1;	/* User's UID */
49 GID_T			groupid = (GID_T)-1;	/* User's GID */
50 char		       *homedir = NULL;		/* User's $HOME */
51 char		       *locuser = NULL;		/* Local User's name */
52 int			isserver = FALSE;	/* We're the server */
53 int     		amchild = 0;		/* This PID is a child */
54 int			do_fork = 1;		/* Fork child process */
55 char		       *currenthost = NULL;	/* Current client hostname */
56 char		       *progname = NULL;	/* Name of this program */
57 int			rem_r = -1;		/* Client file descriptor */
58 int			rem_w = -1;		/* Client file descriptor */
59 struct passwd	       *pw = NULL;		/* Local user's pwd entry */
60 volatile sig_atomic_t 	contimedout = FALSE;	/* Connection timed out */
61 int			proto_version = -1;	/* Protocol version */
62 int			rtimeout = RTIMEOUT;	/* Response time out */
63 jmp_buf			finish_jmpbuf;		/* Finish() jmp buffer */
64 int			setjmp_ok = FALSE;	/* setjmp()/longjmp() status */
65 char		      **realargv;		/* Real main() argv */
66 int			realargc;		/* Real main() argc */
67 opt_t			options = 0;		/* Global install options */
68 char			defowner[64] = "bin";	/* Default owner */
69 char			defgroup[64] = "bin";	/* Default group */
70 
71 static int sendcmdmsg(int, char *, size_t);
72 static ssize_t remread(int, u_char *, size_t);
73 static int remmore(void);
74 
75 /*
76  * Front end to write() that handles partial write() requests.
77  */
78 WRITE_RETURN_T
79 xwrite(int fd, void *buf, WRITE_AMT_T len)
80 {
81     	WRITE_AMT_T nleft = len;
82 	WRITE_RETURN_T nwritten;
83 	char *ptr = buf;
84 
85 	while (nleft > 0) {
86 	    	if ((nwritten = write(fd, ptr, nleft)) <= 0) {
87 			return nwritten;
88 	    	}
89 	    	nleft -= nwritten;
90 	    	ptr += nwritten;
91 	}
92 
93 	return len;
94 }
95 
96 /*
97  * Do run-time initialization
98  */
99 int
100 init(int argc, char **argv, char **envp)
101 {
102 	int i;
103 
104 	/*
105 	 * Save a copy of our argc and argv before setargs() overwrites them
106 	 */
107 	realargc = argc;
108 	realargv = (char **) xmalloc(sizeof(char *) * (argc+1));
109 	for (i = 0; i < argc; i++)
110 		realargv[i] = xstrdup(argv[i]);
111 
112 #if	defined(SETARGS)
113 	setargs_settup(argc, argv, envp);
114 #endif	/* SETARGS */
115 
116 	pw = getpwuid(userid = getuid());
117 	if (pw == NULL) {
118 		error("Your user id (%u) is not known to this system.",
119 		      getuid());
120 		return(-1);
121 	}
122 
123 	debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n",
124 		 userid, pw->pw_name, pw->pw_dir);
125 	homedir = xstrdup(pw->pw_dir);
126 	locuser = xstrdup(pw->pw_name);
127 	groupid = pw->pw_gid;
128 	gethostname(host, sizeof(host));
129 #if 0
130 	if ((cp = strchr(host, '.')) != NULL)
131 	    	*cp = CNULL;
132 #endif
133 
134 	/*
135 	 * If we're not root, disable paranoid ownership checks
136 	 * since normal users cannot chown() files.
137 	 */
138 	if (!isserver && userid != 0) {
139 		FLAG_ON(options, DO_NOCHKOWNER);
140 		FLAG_ON(options, DO_NOCHKGROUP);
141 	}
142 
143 	return(0);
144 }
145 
146 /*
147  * Finish things up before ending.
148  */
149 void
150 finish(void)
151 {
152 	extern jmp_buf finish_jmpbuf;
153 
154 	debugmsg(DM_CALL,
155 		 "finish() called: do_fork = %d amchild = %d isserver = %d",
156 		 do_fork, amchild, isserver);
157 	cleanup(0);
158 
159 	/*
160 	 * There's no valid finish_jmpbuf for the rdist master parent.
161 	 */
162 	if (!do_fork || amchild || isserver) {
163 
164 		if (!setjmp_ok) {
165 #ifdef DEBUG_SETJMP
166 			error("attemping longjmp() without target");
167 			abort();
168 #else
169 			exit(1);
170 #endif
171 		}
172 
173 		longjmp(finish_jmpbuf, 1);
174 		/*NOTREACHED*/
175 		error("Unexpected failure of longjmp() in finish()");
176 		exit(2);
177 	} else
178 		exit(1);
179 }
180 
181 /*
182  * Handle lost connections
183  */
184 void
185 lostconn(void)
186 {
187 	/* Prevent looping */
188 	(void) signal(SIGPIPE, SIG_IGN);
189 
190 	rem_r = rem_w = -1;	/* Ensure we don't try to send to server */
191 	checkhostname();
192 	error("Lost connection to %s",
193 	      (currenthost) ? currenthost : "(unknown)");
194 
195 	finish();
196 }
197 
198 /*
199  * General signal handler
200  */
201 void
202 sighandler(int sig)
203 {
204 	int save_errno = errno;
205 
206 	/* XXX signal race */
207 	debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
208 
209 	switch (sig) {
210 	case SIGALRM:
211 		contimedout = TRUE;
212 		/* XXX signal race */
213 		checkhostname();
214 		error("Response time out");
215 		finish();
216 		break;
217 
218 	case SIGPIPE:
219 		/* XXX signal race */
220 		lostconn();
221 		break;
222 
223 	case SIGFPE:
224 		debug = !debug;
225 		break;
226 
227 	case SIGHUP:
228 	case SIGINT:
229 	case SIGQUIT:
230 	case SIGTERM:
231 		/* XXX signal race */
232 		finish();
233 		break;
234 
235 	default:
236 		/* XXX signal race */
237 		fatalerr("No signal handler defined for signal %d.", sig);
238 	}
239 	errno = save_errno;
240 }
241 
242 /*
243  * Function to actually send the command char and message to the
244  * remote host.
245  */
246 static int
247 sendcmdmsg(int cmd, char *msg, size_t msgsize)
248 {
249 	int len;
250 
251 	if (rem_w < 0)
252 		return(-1);
253 
254 	/*
255 	 * All commands except C_NONE should have a newline
256 	 */
257 	if (cmd != C_NONE && !strchr(msg + 1, '\n'))
258 		(void) strlcat(msg + 1, "\n", msgsize - 1);
259 
260 	if (cmd == C_NONE)
261 		len = strlen(msg);
262 	else {
263 		len = strlen(msg + 1) + 1;
264 		msg[0] = cmd;
265 	}
266 
267 	debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
268 		 cmd, cmd,
269 		 (cmd == C_NONE) ? len-1 : len-2,
270 		 (cmd == C_NONE) ? msg : msg + 1);
271 
272 	return(!(xwrite(rem_w, msg, len) == len));
273 }
274 
275 /*
276  * Send a command message to the remote host.
277  * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
278  * The fmt and arg? arguments are optional.
279  */
280 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
281 /*
282  * Stdarg frontend to sendcmdmsg()
283  */
284 int
285 sendcmd(char cmd, char *fmt, ...)
286 {
287 	static char buf[BUFSIZ];
288 	va_list args;
289 
290 	va_start(args, fmt);
291 	if (fmt)
292 		(void) vsnprintf(buf + (cmd != C_NONE),
293 				 sizeof(buf) - (cmd != C_NONE), fmt, args);
294 	else
295 		buf[1] = CNULL;
296 	va_end(args);
297 
298 	return(sendcmdmsg(cmd, buf, sizeof(buf)));
299 }
300 #endif	/* ARG_TYPE == ARG_STDARG */
301 
302 #if	defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
303 /*
304  * Varargs frontend to sendcmdmsg()
305  */
306 int
307 sendcmd(va_alist)
308 	va_dcl
309 {
310 	static char buf[BUFSIZ];
311 	va_list args;
312 	char cmd;
313 	char *fmt;
314 
315 	va_start(args);
316 	/* XXX The "int" is necessary as a workaround for broken varargs */
317 	cmd = (char) va_arg(args, int);
318 	fmt = va_arg(args, char *);
319 	if (fmt)
320 		(void) vsnprintf(buf + (cmd != C_NONE),
321 				 sizeof(buf) - (cmd != C_NONE), fmt, args);
322 	else
323 		buf[1] = CNULL;
324 	va_end(args);
325 
326 	return(sendcmdmsg(cmd, buf, sizeof(buf)));
327 }
328 #endif	/* ARG_TYPE == ARG_VARARGS */
329 
330 /*
331  * Internal variables and routines for reading lines from the remote.
332  */
333 static u_char rembuf[BUFSIZ];
334 static u_char *remptr;
335 static ssize_t remleft;
336 
337 #define remc() (--remleft < 0 ? remmore() : *remptr++)
338 
339 /*
340  * Back end to remote read()
341  */
342 static ssize_t
343 remread(int fd, u_char *buf, size_t bufsiz)
344 {
345 	return(read(fd, (char *)buf, bufsiz));
346 }
347 
348 static int
349 remmore(void)
350 {
351 	(void) signal(SIGALRM, sighandler);
352 	(void) alarm(rtimeout);
353 
354 	remleft = remread(rem_r, rembuf, sizeof(rembuf));
355 
356 	(void) alarm(0);
357 
358 	if (remleft < 0)
359 		return (-2);	/* error */
360 	if (remleft == 0)
361 		return (-1);	/* EOF */
362 	remptr = rembuf;
363 	remleft--;
364 	return (*remptr++);
365 }
366 
367 /*
368  * Read an input line from the remote.  Return the number of bytes
369  * stored (equivalent to strlen(p)).  If `cleanup' is set, EOF at
370  * the beginning of a line is returned as EOF (-1); other EOFs, or
371  * errors, call cleanup() or lostconn().  In other words, unless
372  * the third argument is nonzero, this routine never returns failure.
373  */
374 int
375 remline(u_char *buffer, int space, int doclean)
376 {
377 	int c, left = space;
378 	u_char *p = buffer;
379 
380 	if (rem_r < 0) {
381 		error("Cannot read remote input: Remote descriptor not open.");
382 		return(-1);
383 	}
384 
385 	while (left > 0) {
386 		if ((c = remc()) < -1) {	/* error */
387 			if (doclean) {
388 				finish();
389 				/*NOTREACHED*/
390 			}
391 			lostconn();
392 			/*NOTREACHED*/
393 		}
394 		if (c == -1) {			/* got EOF */
395 			if (doclean) {
396 				if (left == space)
397 					return (-1);/* signal proper EOF */
398 				finish();	/* improper EOF */
399 				/*NOTREACHED*/
400 			}
401 			lostconn();
402 			/*NOTREACHED*/
403 		}
404 		if (c == '\n') {
405 			*p = CNULL;
406 
407 			if (debug) {
408 				static char mbuf[BUFSIZ];
409 
410 				(void) snprintf(mbuf, sizeof(mbuf),
411 					"<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
412 					       buffer[0], buffer[0],
413 					       buffer + 1);
414 
415 				debugmsg(DM_PROTO, "%s", mbuf);
416 			}
417 
418 			return (space - left);
419 		}
420 		*p++ = c;
421 		left--;
422 	}
423 
424 	/* this will probably blow the entire session */
425 	error("remote input line too long");
426 	p[-1] = CNULL;		/* truncate */
427 	return (space);
428 }
429 
430 /*
431  * Non-line-oriented remote read.
432  */
433 ssize_t
434 readrem(char *p, ssize_t space)
435 {
436 	if (remleft <= 0) {
437 		/*
438 		 * Set remote time out alarm.
439 		 */
440 		(void) signal(SIGALRM, sighandler);
441 		(void) alarm(rtimeout);
442 
443 		remleft = remread(rem_r, rembuf, sizeof(rembuf));
444 
445 		(void) alarm(0);
446 		remptr = rembuf;
447 	}
448 
449 	if (remleft <= 0)
450 		return (remleft);
451 	if (remleft < space)
452 		space = remleft;
453 
454 	memcpy(p, remptr, space);
455 
456 	remptr += space;
457 	remleft -= space;
458 
459 	return (space);
460 }
461 
462 /*
463  * Get the user name for the uid.
464  */
465 char *
466 getusername(UID_T uid, char *file, opt_t opts)
467 {
468 	static char buf[100];
469 	static UID_T lastuid = (UID_T)-1;
470 	struct passwd *pwd = NULL;
471 
472 	/*
473 	 * The value of opts may have changed so we always
474 	 * do the opts check.
475 	 */
476   	if (IS_ON(opts, DO_NUMCHKOWNER)) {
477 		(void) snprintf(buf, sizeof(buf), ":%u", uid);
478 		return(buf);
479   	}
480 
481 	/*
482 	 * Try to avoid getpwuid() call.
483 	 */
484 	if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
485 		return(buf);
486 
487 	lastuid = uid;
488 
489 	if ((pwd = getpwuid(uid)) == NULL) {
490 		if (IS_ON(opts, DO_DEFOWNER) && !isserver)
491 			(void) strlcpy(buf, defowner, sizeof(buf));
492 		else {
493 			message(MT_WARNING,
494 				"%s: No password entry for uid %u", file, uid);
495 			(void) snprintf(buf, sizeof(buf), ":%u", uid);
496 		}
497 	} else {
498 		(void) strlcpy(buf, pwd->pw_name, sizeof(buf));
499 	}
500 
501 	return(buf);
502 }
503 
504 /*
505  * Get the group name for the gid.
506  */
507 char *
508 getgroupname(GID_T gid, char *file, opt_t opts)
509 {
510 	static char buf[100];
511 	static GID_T lastgid = (GID_T)-1;
512 	struct group *grp = NULL;
513 
514 	/*
515 	 * The value of opts may have changed so we always
516 	 * do the opts check.
517 	 */
518   	if (IS_ON(opts, DO_NUMCHKGROUP)) {
519 		(void) snprintf(buf, sizeof(buf), ":%u", gid);
520 		return(buf);
521   	}
522 
523 	/*
524 	 * Try to avoid getgrgid() call.
525 	 */
526 	if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
527 		return(buf);
528 
529 	lastgid = gid;
530 
531 	if ((grp = (struct group *)getgrgid(gid)) == NULL) {
532 		if (IS_ON(opts, DO_DEFGROUP) && !isserver)
533 			(void) strlcpy(buf, defgroup, sizeof(buf));
534 		else {
535 			message(MT_WARNING, "%s: No name for group %u",
536 				file, gid);
537 			(void) snprintf(buf, sizeof(buf), ":%u", gid);
538 		}
539 	} else
540 		(void) strlcpy(buf, grp->gr_name, sizeof(buf));
541 
542 	return(buf);
543 }
544 
545 /*
546  * Read a response from the remote host.
547  */
548 int
549 response(void)
550 {
551 	static u_char resp[BUFSIZ];
552 	u_char *s;
553 	int n;
554 
555 	debugmsg(DM_CALL, "response() start\n");
556 
557 	n = remline(s = resp, sizeof(resp), 0);
558 
559 	n--;
560 	switch (*s++) {
561         case C_ACK:
562 		debugmsg(DM_PROTO, "received ACK\n");
563 		return(0);
564 	case C_LOGMSG:
565 		if (n > 0) {
566 			message(MT_CHANGE, "%s", s);
567 			return(1);
568 		}
569 		debugmsg(DM_PROTO, "received EMPTY logmsg\n");
570 		return(0);
571 	case C_NOTEMSG:
572 		if (s)
573 			message(MT_NOTICE, "%s", s);
574 		return(response());
575 
576 	default:
577 		s--;
578 		n++;
579 		/* fall into... */
580 
581 	case C_ERRMSG:	/* Normal error message */
582 		if (s)
583 			message(MT_NERROR, "%s", s);
584 		return(-1);
585 
586 	case C_FERRMSG:	/* Fatal error message */
587 		if (s)
588 			message(MT_FERROR, "%s", s);
589 		finish();
590 		return(-1);
591 	}
592 	/*NOTREACHED*/
593 }
594 
595 /*
596  * This should be in expand.c but the other routines call other modules
597  * that we don't want to load in.
598  *
599  * Expand file names beginning with `~' into the
600  * user's home directory path name. Return a pointer in buf to the
601  * part corresponding to `file'.
602  */
603 char *
604 exptilde(char *ebuf, char *file, size_t ebufsize)
605 {
606 	char *pw_dir, *rest;
607 	size_t len;
608 	extern char *homedir;
609 
610 	if (*file != '~') {
611 notilde:
612 		(void) strlcpy(ebuf, file, ebufsize);
613 		return(ebuf);
614 	}
615 	if (*++file == CNULL) {
616 		pw_dir = homedir;
617 		rest = NULL;
618 	} else if (*file == '/') {
619 		pw_dir = homedir;
620 		rest = file;
621 	} else {
622 		rest = file;
623 		while (*rest && *rest != '/')
624 			rest++;
625 		if (*rest == '/')
626 			*rest = CNULL;
627 		else
628 			rest = NULL;
629 		if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
630 			if ((pw = getpwnam(file)) == NULL) {
631 				error("%s: unknown user name", file);
632 				if (rest != NULL)
633 					*rest = '/';
634 				return(NULL);
635 			}
636 		}
637 		if (rest != NULL)
638 			*rest = '/';
639 		pw_dir = pw->pw_dir;
640 	}
641 	if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
642 		goto notilde;
643 	pw_dir = ebuf + len;
644 	if (rest != NULL) {
645 		pw_dir++;
646 		if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
647 			goto notilde;
648 	}
649 	return(pw_dir);
650 }
651 
652 #if	defined(DIRECT_RCMD)
653 /*
654  * Set our effective user id to the user running us.
655  * This should be the uid we do most of our work as.
656  */
657 int
658 becomeuser(void)
659 {
660 	int r = 0;
661 
662 #if	defined(HAVE_SAVED_IDS)
663 	r = seteuid(userid);
664 #else
665 	r = setreuid(0, userid);
666 #endif	/* HAVE_SAVED_IDS */
667 
668 	if (r < 0)
669 		error("becomeuser %u failed: %s (ruid = %u euid = %u)",
670 		      userid, SYSERR, getuid(), geteuid());
671 
672 	return(r);
673 }
674 #endif	/* DIRECT_RCMD */
675 
676 #if	defined(DIRECT_RCMD)
677 /*
678  * Set our effective user id to "root" (uid = 0)
679  */
680 int
681 becomeroot(void)
682 {
683 	int r = 0;
684 
685 #if	defined(HAVE_SAVED_IDS)
686 	r = seteuid(0);
687 #else
688 	r = setreuid(userid, 0);
689 #endif	/* HAVE_SAVED_IDS */
690 
691 	if (r < 0)
692 		error("becomeroot failed: %s (ruid = %u euid = %u)",
693 		      SYSERR, getuid(), geteuid());
694 
695 	return(r);
696 }
697 #endif	/* DIRECT_RCMD */
698 
699 /*
700  * Set access and modify times of a given file
701  */
702 int
703 setfiletime(char *file, time_t atime, time_t mtime)
704 {
705 #if	SETFTIME_TYPE == SETFTIME_UTIMES
706 	struct timeval tv[2];
707 
708 	if (atime != 0 && mtime != 0) {
709 		tv[0].tv_sec = atime;
710 		tv[1].tv_sec = mtime;
711 		tv[0].tv_usec = tv[1].tv_usec = (time_t) 0;
712 		return(utimes(file, tv));
713 	} else	/* Set to current time */
714 		return(utimes(file, NULL));
715 
716 #endif	/* SETFTIME_UTIMES */
717 
718 #if	SETFTIME_TYPE == SETFTIME_UTIME
719 	struct utimbuf utbuf;
720 
721 	if (atime != 0 && mtime != 0) {
722 		utbuf.actime = atime;
723 		utbuf.modtime = mtime;
724 		return(utime(file, &utbuf));
725 	} else	/* Set to current time */
726 		return(utime(file, NULL));
727 #endif	/* SETFTIME_UTIME */
728 
729 #if	!defined(SETFTIME_TYPE)
730 	There is no "SETFTIME_TYPE" defined!
731 #endif	/* SETFTIME_TYPE */
732 }
733 
734 /*
735  * Get version info
736  */
737 char *
738 getversion(void)
739 {
740 	static char buff[BUFSIZ];
741 
742 	(void) snprintf(buff, sizeof(buff),
743 	"Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
744 		       DISTVERSION, PATCHLEVEL, DISTSTATUS,
745 		       VERSION, DISTVERSION, PATCHLEVEL);
746 
747 	return(buff);
748 }
749 
750 /*
751  * Execute a shell command to handle special cases.
752  * This is now common to both server and client
753  */
754 void
755 runcommand(char *cmd)
756 {
757 	ssize_t nread;
758 	pid_t pid, wpid;
759 	char *cp, *s;
760 	char sbuf[BUFSIZ], buf[BUFSIZ];
761 	int fd[2], status;
762 
763 	if (pipe(fd) < 0) {
764 		error("pipe of %s failed: %s", cmd, SYSERR);
765 		return;
766 	}
767 
768 	if ((pid = fork()) == 0) {
769 		/*
770 		 * Return everything the shell commands print.
771 		 */
772 		(void) close(0);
773 		(void) close(1);
774 		(void) close(2);
775 		(void) open(_PATH_DEVNULL, O_RDONLY);
776 		(void) dup(fd[PIPE_WRITE]);
777 		(void) dup(fd[PIPE_WRITE]);
778 		(void) close(fd[PIPE_READ]);
779 		(void) close(fd[PIPE_WRITE]);
780 		(void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
781 		_exit(127);
782 	}
783 	(void) close(fd[PIPE_WRITE]);
784 	s = sbuf;
785 	*s++ = C_LOGMSG;
786 	while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
787 		cp = buf;
788 		do {
789 			*s++ = *cp++;
790 			if (cp[-1] != '\n') {
791 				if (s < (char *) &sbuf[sizeof(sbuf)-1])
792 					continue;
793 				*s++ = '\n';
794 			}
795 			/*
796 			 * Throw away blank lines.
797 			 */
798 			if (s == &sbuf[2]) {
799 				s--;
800 				continue;
801 			}
802 			if (isserver)
803 				(void) xwrite(rem_w, sbuf, s - sbuf);
804 			else {
805 				*s = CNULL;
806 				message(MT_INFO, "%s", sbuf+1);
807 			}
808 			s = &sbuf[1];
809 		} while (--nread);
810 	}
811 	if (s > (char *) &sbuf[1]) {
812 		*s++ = '\n';
813 		if (isserver)
814 			(void) xwrite(rem_w, sbuf, s - sbuf);
815 		else {
816 			*s = CNULL;
817 			message(MT_INFO, "%s", sbuf+1);
818 		}
819 	}
820 	while ((wpid = wait(&status)) != pid && wpid != -1)
821 		;
822 	if (wpid == -1)
823 		status = -1;
824 	(void) close(fd[PIPE_READ]);
825 	if (status)
826 		error("shell returned %d", status);
827 	else if (isserver)
828 		ack();
829 }
830 
831 /*
832  * Malloc with error checking
833  */
834 char *
835 xmalloc(size_t amt)
836 {
837 	char *ptr;
838 
839 	if ((ptr = (char *)malloc(amt)) == NULL)
840 		fatalerr("Cannot malloc %zu bytes of memory.", amt);
841 
842 	return(ptr);
843 }
844 
845 /*
846  * realloc with error checking
847  */
848 char *
849 xrealloc(char *baseptr, size_t amt)
850 {
851 	char *new;
852 
853 	if ((new = (char *)realloc(baseptr, amt)) == NULL)
854 		fatalerr("Cannot realloc %zu bytes of memory.", amt);
855 
856 	return(new);
857 }
858 
859 /*
860  * calloc with error checking
861  */
862 char *
863 xcalloc(size_t num, size_t esize)
864 {
865 	char *ptr;
866 
867 	if ((ptr = (char *)calloc(num, esize)) == NULL)
868 		fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
869 		      num, esize, num * esize);
870 
871 	return(ptr);
872 }
873 
874 /*
875  * Strdup with error checking
876  */
877 char *
878 xstrdup(const char *str)
879 {
880 	size_t len = strlen(str) + 1;
881 	char *nstr = (char *) malloc(len);
882 
883 	if (nstr == NULL)
884 		fatalerr("Cannot malloc %zu bytes of memory.", len);
885 
886 	return(memcpy(nstr, str, len));
887 }
888 
889 /*
890  * Private version of basename()
891  */
892 char *
893 xbasename(char *path)
894 {
895 	char *cp;
896 
897 	if ((cp = strrchr(path, '/')) != NULL)
898 		return(cp+1);
899 	else
900 		return(path);
901 }
902 
903 /*
904  * Take a colon (':') separated path to a file and
905  * search until a component of that path is found and
906  * return the found file name.
907  */
908 char *
909 searchpath(char *path)
910 {
911 	char *file;
912 	char *space;
913 	int found;
914 	struct stat statbuf;
915 
916 	for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) {
917 		if ((space = strchr(file, ' ')) != NULL)
918 			*space = CNULL;
919 		found = stat(file, &statbuf) == 0;
920 		if (space)
921 			*space = ' ';		/* Put back what we zapped */
922 	}
923 	return (file);
924 }
925 
926 /*
927  * Set line buffering.
928  */
929 int
930 mysetlinebuf(FILE *fp)
931 {
932 #if	SETBUF_TYPE == SETBUF_SETLINEBUF
933 	return(setlinebuf(fp));
934 #endif	/* SETBUF_SETLINEBUF */
935 #if	SETBUF_TYPE == SETBUF_SETVBUF
936 	return(setvbuf(stdout, NULL, _IOLBF, BUFSIZ));
937 #endif	/* SETBUF_SETVBUF */
938 #if	!defined(SETBUF_TYPE)
939 	No SETBUF_TYPE is defined!
940 #endif	/* SETBUF_TYPE */
941 }
942