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