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