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