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