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