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