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