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