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