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