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