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