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