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