xref: /titanic_50/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * Copyright 1998-2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1983 Regents of the University of California.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that the above copyright notice and this paragraph are
12  * duplicated in all such forms and that any documentation,
13  * advertising materials, and other materials related to such
14  * distribution and use acknowledge that the software was developed
15  * by the University of California, Berkeley.  The name of the
16  * University may not be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  */
19 #pragma ident	"%Z%%M%	%I%	%E% SMI"
20 
21 #include "defs.h"
22 #include <signal.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <ctype.h>
27 #include <krb5defs.h>
28 
29 /*
30  * If we want to write *to* the client rdist program, *from* the server
31  * side (server-side child `rdist -Server' process exec'ed off of in.rshd),
32  * we write to stdout/stderr, since there is a pipe connecting stdout/stderr
33  * to the outside world (which is why we use `wrem' and not `rem').
34  */
35 int wrem = 1;
36 
37 #define	ack() 	(void) write(wrem, "\0\n", 2)
38 #define	err() 	(void) write(wrem, "\1\n", 2)
39 
40 /*
41  * Set when a desread() is reqd. in response()
42  */
43 
44 struct	linkbuf *ihead;		/* list of files with more than one link */
45 char	buf[RDIST_BUFSIZ];	/* general purpose buffer */
46 char	source[RDIST_BUFSIZ];	/* base source directory name */
47 char	destination[RDIST_BUFSIZ];	/* base destination directory name */
48 char	target[RDIST_BUFSIZ];	/* target/source directory name */
49 char	*tp;			/* pointer to end of target name */
50 char	*Tdest;			/* pointer to last T dest */
51 int	catname;		/* cat name to target name */
52 char	*stp[32];		/* stack of saved tp's for directories */
53 int	oumask;			/* old umask for creating files */
54 
55 extern	FILE *lfp;		/* log file for mailing changes */
56 
57 void	cleanup();
58 struct	linkbuf *savelink();
59 char	*strsub();
60 
61 /*
62  * Server routine to read requests and process them.
63  * Commands are:
64  *	Tname	- Transmit file if out of date
65  *	Vname	- Verify if file out of date or not
66  *	Qname	- Query if file exists. Return mtime & size if it does.
67  */
68 server()
69 {
70 	char cmdbuf[RDIST_BUFSIZ];
71 	register char *cp;
72 
73 	signal(SIGHUP, cleanup);
74 	signal(SIGINT, cleanup);
75 	signal(SIGQUIT, cleanup);
76 	signal(SIGTERM, cleanup);
77 	signal(SIGPIPE, cleanup);
78 
79 	rem = 0;
80 	oumask = umask(0);
81 
82 	(void) sprintf(buf, "V%d\n", VERSION);
83 	(void) write(wrem, buf, strlen(buf));
84 
85 	for (;;) {
86 		cp = cmdbuf;
87 		if (read(rem, cp, 1) <= 0)
88 			return;
89 		if (*cp++ == '\n') {
90 			error("server: expected control record\n");
91 			continue;
92 		}
93 		do {
94 			if (read(rem, cp, 1) != 1)
95 				cleanup();
96 		} while (*cp++ != '\n' && cp < &cmdbuf[RDIST_BUFSIZ]);
97 		*--cp = '\0';
98 		cp = cmdbuf;
99 		switch (*cp++) {
100 		case 'T':  /* init target file/directory name */
101 			catname = 1;	/* target should be directory */
102 			goto dotarget;
103 
104 		case 't':  /* init target file/directory name */
105 			catname = 0;
106 		dotarget:
107 			if (exptilde(target, sizeof (target), cp) == NULL)
108 				continue;
109 			tp = target;
110 			while (*tp)
111 				tp++;
112 			ack();
113 			continue;
114 
115 		case 'R':  /* Transfer a regular file. */
116 			recvf(cp, S_IFREG);
117 			continue;
118 
119 		case 'D':  /* Transfer a directory. */
120 			recvf(cp, S_IFDIR);
121 			continue;
122 
123 		case 'K':  /* Transfer symbolic link. */
124 			recvf(cp, S_IFLNK);
125 			continue;
126 
127 		case 'k':  /* Transfer hard link. */
128 			hardlink(cp);
129 			continue;
130 
131 		case 'E':  /* End. (of directory) */
132 			*tp = '\0';
133 			if (catname <= 0) {
134 				error("server: too many 'E's\n");
135 				continue;
136 			}
137 			tp = stp[--catname];
138 			*tp = '\0';
139 			ack();
140 			continue;
141 
142 		case 'C':  /* Clean. Cleanup a directory */
143 			clean(cp);
144 			continue;
145 
146 		case 'Q':  /* Query. Does the file/directory exist? */
147 			query(cp);
148 			continue;
149 
150 		case 'S':  /* Special. Execute commands */
151 			dospecial(cp);
152 			continue;
153 
154 #ifdef notdef
155 		/*
156 		 * These entries are reserved but not currently used.
157 		 * The intent is to allow remote hosts to have master copies.
158 		 * Currently, only the host rdist runs on can have masters.
159 		 */
160 		case 'X':  /* start a new list of files to exclude */
161 			except = bp = NULL;
162 		case 'x':  /* add name to list of files to exclude */
163 			if (*cp == '\0') {
164 				ack();
165 				continue;
166 			}
167 			if (*cp == '~') {
168 				if (exptilde(buf, sizeof (buf), cp) == NULL)
169 					continue;
170 				cp = buf;
171 			}
172 			if (bp == NULL)
173 				except = bp = expand(makeblock(NAME, cp),
174 						E_VARS);
175 			else
176 				bp->b_next = expand(makeblock(NAME, cp),
177 						E_VARS);
178 			while (bp->b_next != NULL)
179 				bp = bp->b_next;
180 			ack();
181 			continue;
182 
183 		case 'I':  /* Install. Transfer file if out of date. */
184 			opts = 0;
185 			while (*cp >= '0' && *cp <= '7')
186 				opts = (opts << 3) | (*cp++ - '0');
187 			if (*cp++ != ' ') {
188 				error("server: options not delimited\n");
189 				return;
190 			}
191 			install(cp, opts);
192 			continue;
193 
194 		case 'L':  /* Log. save message in log file */
195 			log(lfp, cp);
196 			continue;
197 #endif
198 
199 		case '\1':
200 			nerrs++;
201 			continue;
202 
203 		case '\2':
204 			return;
205 
206 		default:
207 			error("server: unknown command '%s'\n", cp);
208 		case '\0':
209 			continue;
210 		}
211 	}
212 }
213 
214 /*
215  * Update the file(s) if they are different.
216  * destdir = 1 if destination should be a directory
217  * (i.e., more than one source is being copied to the same destination).
218  */
219 install(src, dest, destdir, opts)
220 	char *src, *dest;
221 	int destdir, opts;
222 {
223 	char *rname;
224 	char destcopy[RDIST_BUFSIZ];
225 
226 	if (dest == NULL) {
227 		opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
228 		dest = src;
229 	}
230 
231 	if (nflag || debug) {
232 		printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
233 			opts & WHOLE ? " -w" : "",
234 			opts & YOUNGER ? " -y" : "",
235 			opts & COMPARE ? " -b" : "",
236 			opts & REMOVE ? " -R" : "", src, dest);
237 		if (nflag)
238 			return;
239 	}
240 
241 	rname = exptilde(target, sizeof (target), src);
242 	if (rname == NULL)
243 		return;
244 	tp = target;
245 	while (*tp)
246 		tp++;
247 	/*
248 	 * If we are renaming a directory and we want to preserve
249 	 * the directory heirarchy (-w), we must strip off the leading
250 	 * directory name and preserve the rest.
251 	 */
252 	if (opts & WHOLE) {
253 		while (*rname == '/')
254 			rname++;
255 		destdir = 1;
256 	} else {
257 		rname = rindex(target, '/');
258 		if (rname == NULL)
259 			rname = target;
260 		else
261 			rname++;
262 	}
263 	if (debug)
264 		printf("target = %s, rname = %s\n", target, rname);
265 	/*
266 	 * Pass the destination file/directory name to remote.
267 	 */
268 	if (snprintf(buf, sizeof (buf), "%c%s\n", destdir ? 'T' : 't', dest) >=
269 	    sizeof (buf)) {
270 		error("%s: Name too long\n", dest);
271 		return;
272 	}
273 	if (debug)
274 		printf("buf = %s", buf);
275 	(void) deswrite(rem, buf, strlen(buf), 0);
276 
277 	if (response() < 0)
278 		return;
279 
280 	strcpy(source, src);
281 	if (destdir) {
282 		strcpy(destcopy, dest);
283 		Tdest = destcopy;
284 		strcpy(destination, rname);
285 	} else {
286 		strcpy(destination, dest);
287 	}
288 	sendf(rname, opts);
289 	Tdest = 0;
290 }
291 
292 #define	protoname()	(pw ? pw->pw_name : user)
293 #define	protogroup()	(gr ? gr->gr_name : group)
294 /*
295  * Transfer the file or directory in target[].
296  * rname is the name of the file on the remote host.
297  */
298 sendf(rname, opts)
299 	char *rname;
300 	int opts;
301 {
302 	register struct subcmd *sc;
303 	struct stat stb;
304 	int sizerr, f, u, len;
305 	off_t i;
306 	DIR *d;
307 	struct dirent *dp;
308 	char *otp, *cp;
309 	extern struct subcmd *subcmds;
310 	static char user[15], group[15];
311 
312 	if (debug)
313 		printf("sendf(%s, %x%s)\n", rname, opts, printb(opts, OBITS));
314 
315 	if (except(target))
316 		return;
317 	if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
318 		error("%s: %s\n", target, strerror(errno));
319 		return;
320 	}
321 	if (index(rname, '\n')) {
322 		error("file name '%s' contains an embedded newline - "
323 		    "can't update\n", rname);
324 		return;
325 	}
326 	if ((u = update(rname, opts, &stb)) == 0) {
327 		if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
328 			(void) savelink(&stb, opts);
329 		return;
330 	}
331 
332 	if (pw == NULL || pw->pw_uid != stb.st_uid)
333 		if ((pw = getpwuid(stb.st_uid)) == NULL) {
334 			log(lfp, "%s: no password entry for uid %d \n",
335 				target, stb.st_uid);
336 			pw = NULL;
337 			sprintf(user, ":%d", stb.st_uid);
338 		}
339 	if (gr == NULL || gr->gr_gid != stb.st_gid)
340 		if ((gr = getgrgid(stb.st_gid)) == NULL) {
341 			log(lfp, "%s: no name for group %d\n",
342 				target, stb.st_gid);
343 			gr = NULL;
344 			sprintf(group, ":%d", stb.st_gid);
345 		}
346 	if (u == 1) {
347 		if (opts & VERIFY) {
348 			log(lfp, "need to install: %s\n", target);
349 			goto dospecial;
350 		}
351 		log(lfp, "installing: %s\n", target);
352 		opts &= ~(COMPARE|REMOVE);
353 	}
354 
355 	switch (stb.st_mode & S_IFMT) {
356 	case S_IFDIR:
357 		if ((d = opendir(target)) == NULL) {
358 			error("%s: %s\n", target, strerror(errno));
359 			return;
360 		}
361 		if (snprintf(buf, sizeof (buf), "D%o %04o 0 0 %s %s %s\n",
362 		    opts, stb.st_mode & 07777, protoname(), protogroup(),
363 		    rname) >= sizeof (buf)) {
364 			error("%s: Name too long\n", rname);
365 			closedir(d);
366 			return;
367 		}
368 		if (debug)
369 			printf("buf = %s", buf);
370 		(void) deswrite(rem, buf, strlen(buf), 0);
371 		if (response() < 0) {
372 			closedir(d);
373 			return;
374 		}
375 
376 		if (opts & REMOVE)
377 			rmchk(opts);
378 
379 		otp = tp;
380 		len = tp - target;
381 		while (dp = readdir(d)) {
382 			if ((strcmp(dp->d_name, ".") == 0)||
383 			    (strcmp(dp->d_name, "..") == 0))
384 				continue;
385 			if ((int)(len + 1 + strlen(dp->d_name)) >=
386 			    (int)(RDIST_BUFSIZ - 1)) {
387 				error("%.*s/%s: Name too long\n", len, target,
388 					dp->d_name);
389 				continue;
390 			}
391 			tp = otp;
392 			*tp++ = '/';
393 			cp = dp->d_name;
394 			while (*tp++ = *cp++)
395 				;
396 			tp--;
397 			sendf(dp->d_name, opts);
398 		}
399 		closedir(d);
400 		(void) deswrite(rem, "E\n", 2, 0);
401 		(void) response();
402 		tp = otp;
403 		*tp = '\0';
404 		return;
405 
406 	case S_IFLNK:
407 		if (u != 1)
408 			opts |= COMPARE;
409 		if (stb.st_nlink > 1) {
410 			struct linkbuf *lp;
411 
412 			if ((lp = savelink(&stb, opts)) != NULL) {
413 				/* install link */
414 				if (*lp->target == 0)
415 					len = snprintf(buf, sizeof (buf),
416 					    "k%o %s %s\n", opts, lp->pathname,
417 					    rname);
418 				else
419 					len = snprintf(buf, sizeof (buf),
420 					    "k%o %s/%s %s\n", opts, lp->target,
421 					    lp->pathname, rname);
422 				if (len >= sizeof (buf)) {
423 					error("%s: Name too long\n", rname);
424 					return;
425 				}
426 				if (debug)
427 					printf("buf = %s", buf);
428 				(void) deswrite(rem, buf, strlen(buf), 0);
429 				(void) response();
430 				return;
431 			}
432 		}
433 		(void) snprintf(buf, sizeof (buf), "K%o %o %ld %ld %s %s %s\n",
434 		    opts, stb.st_mode & 07777, stb.st_size, stb.st_mtime,
435 		    protoname(), protogroup(), rname);
436 		if (debug)
437 			printf("buf = %s", buf);
438 		(void) deswrite(rem, buf, strlen(buf), 0);
439 		if (response() < 0)
440 			return;
441 		sizerr = (readlink(target, buf, RDIST_BUFSIZ) != stb.st_size);
442 		(void) deswrite(rem, buf, stb.st_size, 0);
443 		if (debug)
444 			printf("readlink = %.*s\n", (int)stb.st_size, buf);
445 		goto done;
446 
447 	case S_IFREG:
448 		break;
449 
450 	default:
451 		error("%s: not a file or directory\n", target);
452 		return;
453 	}
454 
455 	if (u == 2) {
456 		if (opts & VERIFY) {
457 			log(lfp, "need to update: %s\n", target);
458 			goto dospecial;
459 		}
460 		log(lfp, "updating: %s\n", target);
461 	}
462 
463 	if (stb.st_nlink > 1) {
464 		struct linkbuf *lp;
465 
466 		if ((lp = savelink(&stb, opts)) != NULL) {
467 			/* install link */
468 			if (*lp->target == 0)
469 				len = snprintf(buf, sizeof (buf), "k%o %s %s\n",
470 				    opts, lp->pathname, rname);
471 			else
472 				len = snprintf(buf, sizeof (buf),
473 				    "k%o %s/%s %s\n", opts, lp->target,
474 				    lp->pathname, rname);
475 			if (len >= sizeof (buf)) {
476 				error("%s: Name too long\n", rname);
477 				return;
478 			}
479 			if (debug)
480 				printf("buf = %s", buf);
481 			(void) deswrite(rem, buf, strlen(buf), 0);
482 			(void) response();
483 			return;
484 		}
485 	}
486 
487 	if ((f = open(target, 0)) < 0) {
488 		error("%s: %s\n", target, strerror(errno));
489 		return;
490 	}
491 	(void) snprintf(buf, sizeof (buf), "R%o %o %ld %ld %s %s %s\n", opts,
492 		stb.st_mode & 07777, stb.st_size, stb.st_mtime,
493 		protoname(), protogroup(), rname);
494 	if (debug)
495 		printf("buf = %s", buf);
496 	(void) deswrite(rem, buf, strlen(buf), 0);
497 
498 	if (response() < 0) {
499 		(void) close(f);
500 		return;
501 	}
502 
503 	sizerr = 0;
504 
505 	for (i = 0; i < stb.st_size; i += RDIST_BUFSIZ) {
506 		int amt = RDIST_BUFSIZ;
507 		if (i + amt > stb.st_size)
508 			amt = stb.st_size - i;
509 		if (sizerr == 0 && read(f, buf, amt) != amt)
510 			sizerr = 1;
511 		(void) deswrite(rem, buf, amt, 0);
512 	}
513 	(void) close(f);
514 done:
515 	if (sizerr) {
516 		error("%s: file changed size\n", target);
517 		(void) deswrite(rem, "\1\n", 2, 0);
518 	} else
519 		(void) deswrite(rem, "\0\n", 2, 0);
520 	f = response();
521 
522 	if (f < 0 || f == 0 && (opts & COMPARE))
523 		return;
524 dospecial:
525 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
526 		if (sc->sc_type != SPECIAL)
527 			continue;
528 		if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
529 			continue;
530 		log(lfp, "special \"%s\"\n", sc->sc_name);
531 		if (opts & VERIFY)
532 			continue;
533 		(void) snprintf(buf, sizeof (buf), "SFILE=%s;%s\n", target,
534 		    sc->sc_name);
535 		if (debug)
536 			printf("buf = %s", buf);
537 		(void) deswrite(rem, buf, strlen(buf), 0);
538 		while (response() > 0)
539 			;
540 	}
541 }
542 
543 struct linkbuf *
544 savelink(stp, opts)
545 	struct stat *stp;
546 	int opts;
547 {
548 	struct linkbuf *lp;
549 
550 	for (lp = ihead; lp != NULL; lp = lp->nextp)
551 		if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
552 			lp->count--;
553 			return (lp);
554 		}
555 	lp = (struct linkbuf *)malloc(sizeof (*lp));
556 	if (lp == NULL)
557 		log(lfp, "out of memory, link information lost\n");
558 	else {
559 		lp->nextp = ihead;
560 		ihead = lp;
561 		lp->inum = stp->st_ino;
562 		lp->devnum = stp->st_dev;
563 		lp->count = stp->st_nlink - 1;
564 		strcpy(lp->pathname,
565 		    opts & WHOLE ?
566 		    target : strsub(source, destination, target));
567 		if (Tdest)
568 			strcpy(lp->target, Tdest);
569 		else
570 			*lp->target = 0;
571 	}
572 	return (NULL);
573 }
574 
575 /*
576  * Check to see if file needs to be updated on the remote machine.
577  * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
578  * and 3 if comparing binaries to determine if out of date.
579  */
580 update(rname, opts, stp)
581 	char *rname;
582 	int opts;
583 	struct stat *stp;
584 {
585 	register char *cp, *s;
586 	register off_t size;
587 	register time_t mtime;
588 
589 	if (debug)
590 		printf("update(%s, %x%s, %x)\n", rname, opts,
591 			printb(opts, OBITS), stp);
592 
593 	/*
594 	 * Check to see if the file exists on the remote machine.
595 	 */
596 	if (snprintf(buf, sizeof (buf), "Q%s\n", rname) >= sizeof (buf)) {
597 		error("%s: Name too long\n", rname);
598 		return (0);
599 	}
600 	if (debug)
601 		printf("buf = %s", buf);
602 	(void) deswrite(rem, buf, strlen(buf), 0);
603 again:
604 	cp = s = buf;
605 more:
606 	do {
607 		if (desread(rem, cp, 1, 0) != 1)
608 			lostconn();
609 	} while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
610 
611 	if (cp <  &buf[RDIST_BUFSIZ])
612 		*cp = '\0';
613 	if (debug) {
614 		printf("update reply:  ");
615 		switch (*s) {
616 			case 'Y':
617 			case 'N':
618 				putchar(*s);
619 				break;
620 			default:
621 				if (iscntrl(*s)) {
622 					putchar('^');
623 					putchar('A' + *s - 1);
624 				} else
625 					printf("%#x", *s & 0xff);
626 				break;
627 		}
628 		printf("%s", &s[1]);
629 	}
630 
631 	switch (*s++) {
632 	case 'Y':
633 		break;
634 
635 	case 'N':  /* file doesn't exist so install it */
636 		return (1);
637 
638 	case '\1':
639 		nerrs++;
640 		if (*s != '\n') {
641 			if (!iamremote) {
642 				fflush(stdout);
643 				(void) write(2, s, cp - s);
644 			}
645 			if (lfp != NULL)
646 				(void) fwrite(s, 1, cp - s, lfp);
647 		}
648 		if (cp == &buf[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
649 			/* preserve status code */
650 			cp = s;
651 			s = buf;
652 			goto more;
653 		}
654 		return (0);
655 
656 	case '\3':
657 		*--cp = '\0';
658 		if (lfp != NULL)
659 			log(lfp, "update: note: %s\n", s);
660 		goto again;
661 
662 	default:
663 		*--cp = '\0';
664 		error("update: unexpected response '%s'\n", s);
665 		return (0);
666 	}
667 
668 	if (*s == '\n')
669 		return (2);
670 
671 	if (opts & COMPARE)
672 		return (3);
673 
674 	size = 0;
675 	while (isdigit(*s))
676 		size = size * 10 + (*s++ - '0');
677 	if (*s++ != ' ') {
678 		error("update: size not delimited\n");
679 		return (0);
680 	}
681 	mtime = 0;
682 	while (isdigit(*s))
683 		mtime = mtime * 10 + (*s++ - '0');
684 	if (*s != '\n') {
685 		error("update: mtime not delimited\n");
686 		return (0);
687 	}
688 	/*
689 	 * File needs to be updated?
690 	 */
691 	if (opts & YOUNGER) {
692 		if (stp->st_mtime == mtime)
693 			return (0);
694 		if (stp->st_mtime < mtime) {
695 			log(lfp, "Warning: %s: remote copy is newer\n", target);
696 			return (0);
697 		}
698 	} else if (stp->st_mtime == mtime && stp->st_size == size)
699 		return (0);
700 	return (2);
701 }
702 
703 /*
704  * Query. Check to see if file exists. Return one of the following:
705  *	N\n		- doesn't exist
706  *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
707  *	Y\n		- exists and its a directory or symbolic link
708  *	^Aerror message\n
709  */
710 query(name)
711 	char *name;
712 {
713 	struct stat stb;
714 
715 	if (catname) {
716 		if (sizeof (target) - (tp - target) >= strlen(name) + 2) {
717 			(void) sprintf(tp, "/%s", name);
718 		} else {
719 			error("%.*s/%s: Name too long\n", tp - target,
720 			    target, name);
721 			return;
722 		}
723 	}
724 
725 	if (lstat(target, &stb) < 0) {
726 		if (errno == ENOENT)
727 			(void) write(wrem, "N\n", 2);
728 		else
729 			error("%s:%s: %s\n", host, target, strerror(errno));
730 		*tp = '\0';
731 		return;
732 	}
733 
734 	switch (stb.st_mode & S_IFMT) {
735 	case S_IFREG:
736 		(void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
737 		(void) write(wrem, buf, strlen(buf));
738 		break;
739 
740 	case S_IFLNK:
741 	case S_IFDIR:
742 		(void) write(wrem, "Y\n", 2);
743 		break;
744 
745 	default:
746 		error("%s: not a file or directory\n", name);
747 		break;
748 	}
749 	*tp = '\0';
750 }
751 
752 recvf(cmd, type)
753 	char *cmd;
754 	int type;
755 {
756 	register char *cp;
757 	int f, mode, opts, wrerr, olderrno;
758 	off_t i, size;
759 	time_t mtime;
760 	struct stat stb;
761 	struct timeval tvp[2];
762 	char *owner, *group;
763 	char new[RDIST_BUFSIZ];
764 	extern char *tmpname;
765 
766 	cp = cmd;
767 	opts = 0;
768 	while (*cp >= '0' && *cp <= '7')
769 		opts = (opts << 3) | (*cp++ - '0');
770 	if (*cp++ != ' ') {
771 		error("recvf: options not delimited\n");
772 		return;
773 	}
774 	mode = 0;
775 	while (*cp >= '0' && *cp <= '7')
776 		mode = (mode << 3) | (*cp++ - '0');
777 	if (*cp++ != ' ') {
778 		error("recvf: mode not delimited\n");
779 		return;
780 	}
781 	size = 0;
782 	while (isdigit(*cp))
783 		size = size * 10 + (*cp++ - '0');
784 	if (*cp++ != ' ') {
785 		error("recvf: size not delimited\n");
786 		return;
787 	}
788 	mtime = 0;
789 	while (isdigit(*cp))
790 		mtime = mtime * 10 + (*cp++ - '0');
791 	if (*cp++ != ' ') {
792 		error("recvf: mtime not delimited\n");
793 		return;
794 	}
795 	owner = cp;
796 	while (*cp && *cp != ' ')
797 		cp++;
798 	if (*cp != ' ') {
799 		error("recvf: owner name not delimited\n");
800 		return;
801 	}
802 	*cp++ = '\0';
803 	group = cp;
804 	while (*cp && *cp != ' ')
805 		cp++;
806 	if (*cp != ' ') {
807 		error("recvf: group name not delimited\n");
808 		return;
809 	}
810 	*cp++ = '\0';
811 
812 	if (type == S_IFDIR) {
813 		int	isdot;
814 
815 		if (strcmp(cp, ".") == 0)
816 			isdot = 1;
817 		else
818 			isdot = 0;
819 		if (catname >= sizeof (stp) / sizeof (stp[0])) {
820 			error("%s:%s: too many directory levels\n",
821 				host, target);
822 			return;
823 		}
824 		stp[catname] = tp;
825 		if (catname++) {
826 			*tp++ = '/';
827 			while (*tp++ = *cp++)
828 				;
829 			tp--;
830 		}
831 		if (opts & VERIFY) {
832 			ack();
833 			return;
834 		}
835 		if (lstat(target, &stb) == 0) {
836 			if (ISDIR(stb.st_mode)) {
837 				if ((stb.st_mode & 07777) == mode) {
838 					ack();
839 					return;
840 				}
841 				sendrem("%s: Warning: remote mode %o != "
842 				    "local mode %o", target,
843 				    stb.st_mode & 07777, mode);
844 				return;
845 			}
846 			errno = ENOTDIR;
847 		} else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
848 		    chkparent(target) == 0 &&
849 		    (isdot == 1 || mkdir(target, mode) == 0))) {
850 			if (chog(target, owner, group, mode) == 0)
851 				ack();
852 			return;
853 		}
854 		error("%s:%s: %s\n", host, target, strerror(errno));
855 		tp = stp[--catname];
856 		*tp = '\0';
857 		return;
858 	}
859 
860 	if (catname) {
861 		if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
862 			(void) sprintf(tp, "/%s", cp);
863 		} else {
864 			error("%.*s/%s: Name too long\n", tp - target,
865 			    target, cp);
866 			return;
867 		}
868 	}
869 	cp = rindex(target, '/');
870 	if (cp == NULL)
871 		strcpy(new, tmpname);
872 	else if (cp == target)
873 		(void) sprintf(new, "/%s", tmpname);
874 	else {
875 		*cp = '\0';
876 		(void) sprintf(new, "%s/%s", target, tmpname);
877 		*cp = '/';
878 	}
879 
880 	if (type == S_IFLNK) {
881 		int j;
882 
883 		ack();
884 		cp = buf;
885 		for (i = 0; i < size; i += j) {
886 			if ((j = read(rem, cp, size - i)) <= 0)
887 				cleanup();
888 			cp += j;
889 		}
890 		*cp = '\0';
891 		if (response() < 0) {
892 			err();
893 			return;
894 		}
895 		if (symlink(buf, new) < 0) {
896 			if (errno != ENOENT || chkparent(new) < 0 ||
897 			    symlink(buf, new) < 0)
898 				goto badn;
899 		}
900 		mode &= 0777;
901 		if (opts & COMPARE) {
902 			char tbuf[MAXPATHLEN];
903 
904 			if ((i = readlink(target, tbuf, MAXPATHLEN)) >= 0 &&
905 			    i == size && strncmp(buf, tbuf, size) == 0) {
906 				(void) unlink(new);
907 				ack();
908 				return;
909 			}
910 			if (opts & VERIFY)
911 				goto differ;
912 		}
913 		goto fixup;
914 	}
915 
916 	if ((f = creat(new, mode & ~06000)) < 0) {
917 		if (errno != ENOENT || chkparent(new) < 0 ||
918 		    (f = creat(new, mode & ~06000)) < 0)
919 			goto badn;
920 	}
921 
922 	ack();
923 	wrerr = 0;
924 	for (i = 0; i < size; i += RDIST_BUFSIZ) {
925 		int amt = RDIST_BUFSIZ;
926 
927 		cp = buf;
928 		if (i + amt > size)
929 			amt = size - i;
930 		do {
931 			int j = read(rem, cp, amt);
932 			if (j <= 0) {
933 				(void) close(f);
934 				(void) unlink(new);
935 				cleanup();
936 			}
937 			amt -= j;
938 			cp += j;
939 		} while (amt > 0);
940 		amt = RDIST_BUFSIZ;
941 		if (i + amt > size)
942 			amt = size - i;
943 		if (wrerr == 0 && write(f, buf, amt) != amt) {
944 			olderrno = errno;
945 			wrerr++;
946 		}
947 	}
948 	(void) close(f);
949 
950 	if (response() < 0) {
951 		err();
952 		(void) unlink(new);
953 		return;
954 	}
955 	if (wrerr) {
956 		error("%s:%s: %s\n", host, new, strerror(olderrno));
957 		(void) unlink(new);
958 		return;
959 	}
960 	if (opts & COMPARE) {
961 		FILE *f1, *f2;
962 		int c;
963 
964 		if ((f1 = fopen(target, "r")) == NULL)
965 			goto badt;
966 		if ((f2 = fopen(new, "r")) == NULL) {
967 		badn:
968 			error("%s:%s: %s\n", host, new, strerror(errno));
969 			(void) unlink(new);
970 			return;
971 		}
972 		while ((c = getc(f1)) == getc(f2))
973 			if (c == EOF) {
974 				(void) fclose(f1);
975 				(void) fclose(f2);
976 				(void) unlink(new);
977 				ack();
978 				return;
979 			}
980 		(void) fclose(f1);
981 		(void) fclose(f2);
982 		if (opts & VERIFY) {
983 		differ:
984 			(void) unlink(new);
985 			sendrem("need to update: %s", target);
986 			return;
987 		}
988 	}
989 
990 	/*
991 	 * Set last modified time.  For type == S_IFDIR, the lstat above filled
992 	 * in stb.  Otherwise, do it now.
993 	 */
994 	if (type != S_IFDIR)
995 		(void) lstat(new, &stb);
996 	tvp[0].tv_sec = stb.st_atime;	/* old atime from target */
997 	tvp[0].tv_usec = 0;
998 	tvp[1].tv_sec = mtime;
999 	tvp[1].tv_usec = 0;
1000 	if (utimes(new, tvp) < 0) {
1001 		note("%s:utimes failed %s: %s", host, new, strerror(errno));
1002 	}
1003 	if (chog(new, owner, group, mode) < 0) {
1004 		(void) unlink(new);
1005 		return;
1006 	}
1007 fixup:
1008 	if (rename(new, target) < 0) {
1009 badt:
1010 		error("%s:%s: %s\n", host, target, strerror(errno));
1011 		(void) unlink(new);
1012 		return;
1013 	}
1014 	if (opts & COMPARE) {
1015 		sendrem("updated %s", target);
1016 	} else
1017 		ack();
1018 }
1019 
1020 /*
1021  * Creat a hard link to existing file.
1022  */
1023 hardlink(cmd)
1024 	char *cmd;
1025 {
1026 	register char *cp;
1027 	struct stat stb;
1028 	char *oldname;
1029 	int opts, exists = 0;
1030 	char oldnamebuf[RDIST_BUFSIZ];
1031 
1032 	cp = cmd;
1033 	opts = 0;
1034 	while (*cp >= '0' && *cp <= '7')
1035 		opts = (opts << 3) | (*cp++ - '0');
1036 	if (*cp++ != ' ') {
1037 		error("hardlink: options not delimited\n");
1038 		return;
1039 	}
1040 	oldname = cp;
1041 	while (*cp && *cp != ' ')
1042 		cp++;
1043 	if (*cp != ' ') {
1044 		error("hardlink: oldname name not delimited\n");
1045 		return;
1046 	}
1047 	*cp++ = '\0';
1048 
1049 	if (catname) {
1050 		if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
1051 			(void) sprintf(tp, "/%s", cp);
1052 		} else {
1053 			error("%.*s/%s: Name too long\n", tp - target,
1054 			    target, cp);
1055 			return;
1056 		}
1057 	}
1058 	if (lstat(target, &stb) == 0) {
1059 		int mode = stb.st_mode & S_IFMT;
1060 		if (mode != S_IFREG && mode != S_IFLNK) {
1061 			error("%s:%s: not a regular file\n", host, target);
1062 			return;
1063 		}
1064 		exists = 1;
1065 	}
1066 	if (chkparent(target) < 0) {
1067 		error("%s:%s: %s (no parent)\n",
1068 			host, target, strerror(errno));
1069 		return;
1070 	}
1071 	if (opts & VERIFY) {
1072 		struct stat nstb;
1073 
1074 		if (exists && lstat(oldname, &nstb) == 0 &&
1075 		    nstb.st_mode == stb.st_mode &&
1076 		    nstb.st_ino == stb.st_ino &&
1077 		    nstb.st_dev == stb.st_dev) {
1078 			ack();
1079 			return;
1080 		} else {
1081 			sendrem("need to update: %s", target);
1082 			return;
1083 		}
1084 	}
1085 	if (exists && (unlink(target) < 0)) {
1086 		error("%s:%s: %s (unlink)\n",
1087 			host, target, strerror(errno));
1088 		return;
1089 	}
1090 	if (*oldname == '~')
1091 		oldname = exptilde(oldnamebuf, sizeof (oldnamebuf), oldname);
1092 	if (link(oldname, target) < 0) {
1093 		error("%s:can't link %s to %s\n",
1094 			host, target, oldname);
1095 		return;
1096 	}
1097 	ack();
1098 }
1099 
1100 /*
1101  * Check to see if parent directory exists and create one if not.
1102  */
1103 chkparent(name)
1104 	char *name;
1105 {
1106 	register char *cp;
1107 	struct stat stb;
1108 
1109 	cp = rindex(name, '/');
1110 	if (cp == NULL || cp == name)
1111 		return (0);
1112 	*cp = '\0';
1113 	if (lstat(name, &stb) < 0) {
1114 		if (errno == ENOENT && chkparent(name) >= 0 &&
1115 		    mkdir(name, 0777 & ~oumask) >= 0) {
1116 			*cp = '/';
1117 			return (0);
1118 		}
1119 	} else if (ISDIR(stb.st_mode)) {
1120 		*cp = '/';
1121 		return (0);
1122 	}
1123 	*cp = '/';
1124 	return (-1);
1125 }
1126 
1127 /*
1128  * Change owner, group and mode of file.
1129  */
1130 chog(file, owner, group, mode)
1131 	char *file, *owner, *group;
1132 	int mode;
1133 {
1134 	register int i;
1135 	uid_t uid, gid;
1136 	extern char user[];
1137 
1138 	/*
1139 	 * by default, set uid of file to the uid of the person running
1140 	 * this program.
1141 	 */
1142 	uid = getuid();
1143 
1144 	/*
1145 	 * We'll use available privileges so we just try to do what
1146 	 * the client specifies.  If the chown() fails we'll not
1147 	 * add the set-[ug]id bits; and if we want to add the set-[ug]id
1148 	 * bits and we're not permitted to do so, the OS will prevent us
1149 	 * from doing so.
1150 	 */
1151 	if (*owner == ':') {
1152 		uid = atoi(owner + 1);
1153 	} else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
1154 		if ((pw = getpwnam(owner)) == NULL) {
1155 			if (mode & 04000) {
1156 				note("%s:%s: unknown login name, "
1157 				    "clearing setuid", host, owner);
1158 				mode &= ~04000;
1159 			}
1160 		} else {
1161 			uid = pw->pw_uid;
1162 		}
1163 	} else {
1164 		uid = pw->pw_uid;
1165 	}
1166 
1167 	if (*group == ':') {
1168 		gid = atoi(group + 1);
1169 		goto ok;
1170 	}
1171 
1172 	gid = -1;
1173 	if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
1174 		if ((*group == ':' &&
1175 		    (getgrgid(gid = atoi(group + 1)) == NULL)) ||
1176 		    ((gr = getgrnam(group)) == NULL)) {
1177 			if (mode & 02000) {
1178 				note("%s:%s: unknown group", host, group);
1179 				mode &= ~02000;
1180 			}
1181 		} else
1182 			gid = gr->gr_gid;
1183 	} else
1184 		gid = gr->gr_gid;
1185 ok:
1186 	if (chown(file, uid, gid) < 0 ||
1187 	    (mode & 07000) && chmod(file, mode) < 0) {
1188 		note("%s: chown or chmod failed: file %s:  %s",
1189 		    host, file, strerror(errno));
1190 	}
1191 	return (0);
1192 }
1193 
1194 /*
1195  * Check for files on the machine being updated that are not on the master
1196  * machine and remove them.
1197  */
1198 rmchk(opts)
1199 	int opts;
1200 {
1201 	register char *cp, *s;
1202 	struct stat stb;
1203 
1204 	if (debug)
1205 		printf("rmchk()\n");
1206 
1207 	/*
1208 	 * Tell the remote to clean the files from the last directory sent.
1209 	 */
1210 	(void) sprintf(buf, "C%o\n", opts & VERIFY);
1211 	if (debug)
1212 		printf("buf = %s", buf);
1213 	(void) deswrite(rem, buf, strlen(buf), 0);
1214 	if (response() < 0)
1215 		return;
1216 	for (;;) {
1217 		cp = s = buf;
1218 		do {
1219 			if (desread(rem, cp, 1, 0) != 1)
1220 				lostconn();
1221 		} while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
1222 
1223 		switch (*s++) {
1224 		case 'Q': /* Query if file should be removed */
1225 			/*
1226 			 * Return the following codes to remove query.
1227 			 * N\n -- file exists - DON'T remove.
1228 			 * Y\n -- file doesn't exist - REMOVE.
1229 			 */
1230 			*--cp = '\0';
1231 			(void) sprintf(tp, "/%s", s);
1232 			if (debug)
1233 				printf("check %s\n", target);
1234 			if (except(target))
1235 				(void) deswrite(rem, "N\n", 2, 0);
1236 			else if (lstat(target, &stb) < 0)
1237 				(void) deswrite(rem, "Y\n", 2, 0);
1238 			else
1239 				(void) deswrite(rem, "N\n", 2, 0);
1240 			break;
1241 
1242 		case '\0':
1243 			*--cp = '\0';
1244 			if (*s != '\0')
1245 				log(lfp, "%s\n", s);
1246 			break;
1247 
1248 		case 'E':
1249 			*tp = '\0';
1250 			(void) deswrite(rem, "\0\n", 2, 0);
1251 			return;
1252 
1253 		case '\1':
1254 		case '\2':
1255 			nerrs++;
1256 			if (*s != '\n') {
1257 				if (!iamremote) {
1258 					fflush(stdout);
1259 					(void) write(2, s, cp - s);
1260 				}
1261 				if (lfp != NULL)
1262 					(void) fwrite(s, 1, cp - s, lfp);
1263 			}
1264 			if (buf[0] == '\2')
1265 				lostconn();
1266 			break;
1267 
1268 		default:
1269 			error("rmchk: unexpected response '%s'\n", buf);
1270 			(void) deswrite(rem, "\1\n", 2, 0);
1271 		}
1272 	}
1273 }
1274 
1275 /*
1276  * Check the current directory (initialized by the 'T' command to server())
1277  * for extraneous files and remove them.
1278  */
1279 clean(cp)
1280 	register char *cp;
1281 {
1282 	DIR *d;
1283 	register struct dirent *dp;
1284 	struct stat stb;
1285 	char *otp;
1286 	int len, opts;
1287 
1288 	opts = 0;
1289 	while (*cp >= '0' && *cp <= '7')
1290 		opts = (opts << 3) | (*cp++ - '0');
1291 	if (*cp != '\0') {
1292 		error("clean: options not delimited\n");
1293 		return;
1294 	}
1295 	if ((d = opendir(target)) == NULL) {
1296 		error("%s:%s: %s\n", host, target, strerror(errno));
1297 		return;
1298 	}
1299 	ack();
1300 
1301 	otp = tp;
1302 	len = tp - target;
1303 	while (dp = readdir(d)) {
1304 		if ((strcmp(dp->d_name, ".") == 0) ||
1305 		    (strcmp(dp->d_name, "..") == 0))
1306 			continue;
1307 		if ((int)(len + 1 + strlen(dp->d_name)) >=
1308 		    (int)(RDIST_BUFSIZ - 1)) {
1309 			error("%s:%s/%s: Name too long\n",
1310 				host, target, dp->d_name);
1311 			continue;
1312 		}
1313 		tp = otp;
1314 		*tp++ = '/';
1315 		cp = dp->d_name;
1316 		while (*tp++ = *cp++)
1317 			;
1318 		tp--;
1319 		if (lstat(target, &stb) < 0) {
1320 			error("%s:%s: %s\n", host, target, strerror(errno));
1321 			continue;
1322 		}
1323 		(void) snprintf(buf, sizeof (buf), "Q%s\n", dp->d_name);
1324 		(void) write(wrem, buf, strlen(buf));
1325 		cp = buf;
1326 		do {
1327 			if (read(rem, cp, 1) != 1)
1328 				cleanup();
1329 		} while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
1330 		*--cp = '\0';
1331 		cp = buf;
1332 		if (*cp != 'Y')
1333 			continue;
1334 		if (opts & VERIFY) {
1335 			sendrem("need to remove: %s", target);
1336 		} else
1337 			(void) recursive_remove(&stb);
1338 	}
1339 	closedir(d);
1340 	(void) write(wrem, "E\n", 2);
1341 	(void) response();
1342 	tp = otp;
1343 	*tp = '\0';
1344 }
1345 
1346 /*
1347  * Remove a file or directory (recursively) and send back an acknowledge
1348  * or an error message.
1349  */
1350 static
1351 recursive_remove(stp)
1352 	struct stat *stp;
1353 {
1354 	DIR *d;
1355 	struct dirent *dp;
1356 	register char *cp;
1357 	struct stat stb;
1358 	char *otp;
1359 	int len;
1360 
1361 	switch (stp->st_mode & S_IFMT) {
1362 	case S_IFREG:
1363 	case S_IFLNK:
1364 		if (unlink(target) < 0)
1365 			goto bad;
1366 		goto removed;
1367 
1368 	case S_IFDIR:
1369 		break;
1370 
1371 	default:
1372 		error("%s:%s: not a plain file\n", host, target);
1373 		return;
1374 	}
1375 
1376 	if ((d = opendir(target)) == NULL)
1377 		goto bad;
1378 
1379 	otp = tp;
1380 	len = tp - target;
1381 	while (dp = readdir(d)) {
1382 		if ((strcmp(dp->d_name, ".") == 0) ||
1383 		    (strcmp(dp->d_name, "..") == 0))
1384 			continue;
1385 		if ((int)(len + 1 + strlen(dp->d_name)) >=
1386 		    (int)(RDIST_BUFSIZ - 1)) {
1387 			error("%s:%s/%s: Name too long\n",
1388 				host, target, dp->d_name);
1389 			continue;
1390 		}
1391 		tp = otp;
1392 		*tp++ = '/';
1393 		cp = dp->d_name;
1394 		while (*tp++ = *cp++)
1395 			;
1396 		tp--;
1397 		if (lstat(target, &stb) < 0) {
1398 			error("%s:%s: %s\n", host, target, strerror(errno));
1399 			continue;
1400 		}
1401 		recursive_remove(&stb);
1402 	}
1403 	closedir(d);
1404 	tp = otp;
1405 	*tp = '\0';
1406 	if (rmdir(target) < 0) {
1407 bad:
1408 		error("%s:%s: %s\n", host, target, strerror(errno));
1409 		return;
1410 	}
1411 removed:
1412 	sendrem("removed %s", target);
1413 }
1414 
1415 /*
1416  * Execute a shell command to handle special cases.
1417  */
1418 dospecial(cmd)
1419 	char *cmd;
1420 {
1421 	int fd[2], status, pid, i;
1422 	register char *cp, *s;
1423 	char sbuf[RDIST_BUFSIZ];
1424 
1425 	if (pipe(fd) < 0) {
1426 		error("%s\n", strerror(errno));
1427 		return;
1428 	}
1429 	if ((pid = fork()) == 0) {
1430 		/*
1431 		 * Return everything the shell commands print.
1432 		 */
1433 		(void) close(0);
1434 		(void) close(1);
1435 		(void) close(2);
1436 		(void) open("/dev/null", 0);
1437 		(void) dup(fd[1]);
1438 		(void) dup(fd[1]);
1439 		(void) close(fd[0]);
1440 		(void) close(fd[1]);
1441 		execl("/bin/sh", "sh", "-c", cmd, 0);
1442 		_exit(127);
1443 	}
1444 	(void) close(fd[1]);
1445 	s = sbuf;
1446 	*s++ = '\0';
1447 	while ((i = read(fd[0], buf, RDIST_BUFSIZ)) > 0) {
1448 		cp = buf;
1449 		do {
1450 			*s++ = *cp++;
1451 			if (cp[-1] != '\n') {
1452 				if (s < &sbuf[RDIST_BUFSIZ - 1])
1453 					continue;
1454 				*s++ = '\n';
1455 			}
1456 			/*
1457 			 * Throw away blank lines.
1458 			 */
1459 			if (s == &sbuf[2]) {
1460 				s--;
1461 				continue;
1462 			}
1463 			(void) write(wrem, sbuf, s - sbuf);
1464 			s = &sbuf[1];
1465 		} while (--i);
1466 	}
1467 	if (s > &sbuf[1]) {
1468 		*s++ = '\n';
1469 		(void) write(wrem, sbuf, s - sbuf);
1470 	}
1471 	while ((i = wait(&status)) != pid && i != -1)
1472 		;
1473 	if (i == -1)
1474 		status = -1;
1475 	(void) close(fd[0]);
1476 	if (status)
1477 		error("shell returned %d\n", status);
1478 	else
1479 		ack();
1480 }
1481 
1482 /*VARARGS2*/
1483 log(fp, fmt, a1, a2, a3)
1484 	FILE *fp;
1485 	char *fmt;
1486 	int a1, a2, a3;
1487 {
1488 	/* Print changes locally if not quiet mode */
1489 	if (!qflag)
1490 		printf(fmt, a1, a2, a3);
1491 
1492 	/* Save changes (for mailing) if really updating files */
1493 	if (!(options & VERIFY) && fp != NULL)
1494 		fprintf(fp, fmt, a1, a2, a3);
1495 }
1496 
1497 /*VARARGS1*/
1498 error(fmt, a1, a2, a3)
1499 	char *fmt;
1500 	int a1, a2, a3;
1501 {
1502 	static FILE *fp;
1503 
1504 	nerrs++;
1505 	if (!fp && !(fp = fdopen(rem, "w")))
1506 		return;
1507 	if (iamremote) {
1508 		(void) fprintf(fp, "%crdist: ", 0x01);
1509 		(void) fprintf(fp, fmt, a1, a2, a3);
1510 		fflush(fp);
1511 	} else {
1512 		fflush(stdout);
1513 		(void) fprintf(stderr, "rdist: ");
1514 		(void) fprintf(stderr, fmt, a1, a2, a3);
1515 		fflush(stderr);
1516 	}
1517 	if (lfp != NULL) {
1518 		(void) fprintf(lfp, "rdist: ");
1519 		(void) fprintf(lfp, fmt, a1, a2, a3);
1520 		fflush(lfp);
1521 	}
1522 }
1523 
1524 /*VARARGS1*/
1525 fatal(fmt, a1, a2, a3)
1526 	char *fmt;
1527 	int a1, a2, a3;
1528 {
1529 	static FILE *fp;
1530 
1531 	nerrs++;
1532 	if (!fp && !(fp = fdopen(rem, "w")))
1533 		return;
1534 	if (iamremote) {
1535 		(void) fprintf(fp, "%crdist: ", 0x02);
1536 		(void) fprintf(fp, fmt, a1, a2, a3);
1537 		fflush(fp);
1538 	} else {
1539 		fflush(stdout);
1540 		(void) fprintf(stderr, "rdist: ");
1541 		(void) fprintf(stderr, fmt, a1, a2, a3);
1542 		fflush(stderr);
1543 	}
1544 	if (lfp != NULL) {
1545 		(void) fprintf(lfp, "rdist: ");
1546 		(void) fprintf(lfp, fmt, a1, a2, a3);
1547 		fflush(lfp);
1548 	}
1549 	cleanup();
1550 }
1551 
1552 response()
1553 {
1554 	char *cp, *s;
1555 	char resp[RDIST_BUFSIZ];
1556 
1557 	if (debug)
1558 		printf("response()\n");
1559 
1560 	cp = s = resp;
1561 more:
1562 	do {
1563 		if (desread(rem, cp, 1, 0) != 1)
1564 			lostconn();
1565 	} while (*cp++ != '\n' && cp < &resp[RDIST_BUFSIZ]);
1566 
1567 	switch (*s++) {
1568 	case '\0':
1569 		*--cp = '\0';
1570 		if (*s != '\0') {
1571 			log(lfp, "%s\n", s);
1572 			return (1);
1573 		}
1574 		return (0);
1575 	case '\3':
1576 		*--cp = '\0';
1577 		log(lfp, "Note: %s\n", s);
1578 		return (response());
1579 
1580 	default:
1581 		s--;
1582 		/* fall into... */
1583 	case '\1':
1584 	case '\2':
1585 		nerrs++;
1586 		if (*s != '\n') {
1587 			if (!iamremote) {
1588 				fflush(stdout);
1589 				(void) write(2, s, cp - s);
1590 			}
1591 			if (lfp != NULL)
1592 				(void) fwrite(s, 1, cp - s, lfp);
1593 		}
1594 		if (cp == &resp[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
1595 			/* preserve status code */
1596 			cp = s;
1597 			s = resp;
1598 			goto more;
1599 		}
1600 		if (resp[0] == '\2')
1601 			lostconn();
1602 		return (-1);
1603 	}
1604 }
1605 
1606 /*
1607  * Remove temporary files and do any cleanup operations before exiting.
1608  */
1609 void
1610 cleanup()
1611 {
1612 	(void) unlink(Tmpfile);
1613 	exit(1);
1614 }
1615 
1616 note(fmt, a1, a2, a3)
1617 char *fmt;
1618 int a1, a2, a3;
1619 {
1620 	static char buf[RDIST_BUFSIZ];
1621 	(void) snprintf(buf, sizeof (buf) - 1, fmt, a1, a2, a3);
1622 	comment(buf);
1623 }
1624 
1625 comment(s)
1626 char *s;
1627 {
1628 	char three = '\3';
1629 	char nl = '\n';
1630 	struct iovec iov[3];
1631 
1632 	iov[0].iov_base = &three;
1633 	iov[0].iov_len = sizeof (char);
1634 	iov[1].iov_base = s;
1635 	iov[1].iov_len = strlen(s);
1636 	iov[2].iov_base = &nl;
1637 	iov[2].iov_len = sizeof (char);
1638 	(void) writev(rem, iov, 3);
1639 }
1640 
1641 /*
1642  * Send message to other end.
1643  * N.B.: uses buf[].
1644  */
1645 void
1646 sendrem(fmt, a1, a2, a3)
1647 char *fmt;
1648 int a1, a2, a3;
1649 {
1650 	register int len;
1651 
1652 	buf[0] = '\0';
1653 	len = snprintf(buf + 1, sizeof (buf) - 1, fmt, a1, a2, a3) + 2;
1654 	if (len > sizeof (buf))
1655 		len = sizeof (buf);
1656 	buf[len - 1] = '\n';
1657 	(void) write(wrem, buf, len);
1658 }
1659 
1660 /*
1661  * strsub(old, new, s)
1662  *
1663  * Return a pointer to a new string created by replacing substring old
1664  * with substring new in string s.  String s is assumed to begin with
1665  * substring old.
1666  */
1667 char *
1668 strsub(old, new, s)
1669 	char *old, *new, *s;
1670 {
1671 	static char pbuf[PATH_MAX];
1672 	register char *p, *q, *r, *plim;
1673 
1674 	/* prepend new to pbuf */
1675 	for (p = pbuf, q = new, plim = pbuf + sizeof (pbuf) - 1;
1676 	/* CSTYLED */
1677 	    *q && (p < plim);)
1678 		*p++ = *q++;
1679 	/* p now points to the byte in pbuf where more copying should begin */
1680 
1681 	/* skip over the part of s which begins with old */
1682 	for (r = old, q = s; *r; q++, r++)
1683 		;
1684 	/* q now points to the byte in s where more copying should begin */
1685 
1686 	while (*q && (p < plim))
1687 		*p++ = *q++;
1688 	*p = '\0';
1689 
1690 	return (pbuf);
1691 }
1692