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