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