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