xref: /netbsd-src/usr.bin/rdist/server.c (revision fbffadb9f864c0324fb295860ab0faeb187269cc)
1 /*	$NetBSD: server.c,v 1.33 2019/02/03 03:19:29 mrg 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.33 2019/02/03 03:19:29 mrg 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
server(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 			/* FALLTHROUGH */
235 		case '\0':
236 			continue;
237 		}
238 	}
239 }
240 
241 /*
242  * Update the file(s) if they are different.
243  * destdir = 1 if destination should be a directory
244  * (i.e., more than one source is being copied to the same destination).
245  */
246 void
install(char * src,char * dest,int destdir,int opts)247 install(char *src, char *dest, int destdir, int opts)
248 {
249 	char *rname;
250 	char destcopy[BUFSIZ];
251 
252 	if (dest == NULL) {
253 		opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
254 		dest = src;
255 	} else if (!(opts & WHOLE)) {
256 		/* prepare for proper renaming of directory trees */
257 		Destcopy = destcopy;
258 		Destcopylen = strlen(dest);
259 		while (Destcopylen > 0 && dest[Destcopylen] == '/')
260 		    Destcopylen--;
261 	}
262 	strlcpy(destcopy, dest, sizeof(destcopy));
263 
264 	if (nflag || debug) {
265 		printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
266 			opts & WHOLE ? " -w" : "",
267 			opts & YOUNGER ? " -y" : "",
268 			opts & COMPARE ? " -b" : "",
269 			opts & REMOVE ? " -R" : "", src, dest);
270 		if (nflag)
271 			return;
272 	}
273 
274 	rname = exptilde(target, src);
275 	if (rname == NULL)
276 		return;
277 	tp = target;
278 	while (*tp)
279 		tp++;
280 	if (Destcopy) {
281 		/* We can only do this after expansion of src */
282 		Sourcelen = strlen(target);
283 		while (Sourcelen > 0 && target[Sourcelen] == '/')
284 		    Sourcelen--;
285 	}
286 	/*
287 	 * If we are renaming a directory and we want to preserve
288 	 * the directory hierarchy (-w), we must strip off the leading
289 	 * directory name and preserve the rest.
290 	 */
291 	if (opts & WHOLE) {
292 		while (*rname == '/')
293 			rname++;
294 		destdir = 1;
295 	} else {
296 		rname = strrchr(target, '/');
297 		if (rname == NULL)
298 			rname = target;
299 		else
300 			rname++;
301 	}
302 	if (debug)
303 		printf("target = %s, rname = %s\n", target, rname);
304 	/*
305 	 * Pass the destination file/directory name to remote.
306 	 */
307 	(void) snprintf(buf, sizeof(buf), "%c%s\n", destdir ? 'T' : 't', dest);
308 	if (debug)
309 		printf("buf = %s", buf);
310 	if (write(rem, buf, strlen(buf)) < 0)
311 		error("could not pass filename to remote: %s\n",
312 		    strerror(errno));
313 	if (response() < 0)
314 		return;
315 
316 	if (destdir)
317 		Tdest = destcopy;
318 	sendf(rname, opts);
319 	Destcopy = 0;
320 	Tdest = 0;
321 }
322 
323 #define protoname() (pw ? pw->pw_name : user)
324 #define protogroup() (gr ? gr->gr_name : group)
325 /*
326  * Transfer the file or directory in target[].
327  * rname is the name of the file on the remote host.
328  */
329 static void
sendf(char * rname,int opts)330 sendf(char *rname, int opts)
331 {
332 	struct subcmd *sc;
333 	struct stat stb;
334 	int sizerr, f, u, len;
335 	off_t i;
336 	DIR *d;
337 	struct dirent *dp;
338 	char *otp, *cp;
339 	extern struct subcmd *subcmds;
340 	static char user[15], group[15];
341 
342 	if (debug)
343 		printf("sendf(%s, %x)\n", rname, opts);
344 
345 	if (except(target))
346 		return;
347 	if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
348 		error("%s: %s\n", target, strerror(errno));
349 		return;
350 	}
351 	if ((u = update(rname, opts, &stb)) == 0) {
352 		if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
353 			(void) savelink(&stb);
354 		return;
355 	}
356 
357 	if (pw == NULL || pw->pw_uid != stb.st_uid)
358 		if ((pw = getpwuid(stb.st_uid)) == NULL) {
359 			dolog(lfp, "%s: no password entry for uid %d \n",
360 				target, stb.st_uid);
361 			pw = NULL;
362 			(void)snprintf(user, sizeof(user), ":%lu",
363 			    (u_long)stb.st_uid);
364 		}
365 	if (gr == NULL || gr->gr_gid != stb.st_gid)
366 		if ((gr = getgrgid(stb.st_gid)) == NULL) {
367 			dolog(lfp, "%s: no name for group %d\n",
368 				target, stb.st_gid);
369 			gr = NULL;
370 			(void)snprintf(group, sizeof(group), ":%lu",
371 			    (u_long)stb.st_gid);
372 		}
373 	if (u == 1) {
374 		if (opts & VERIFY) {
375 			dolog(lfp, "need to install: %s\n", target);
376 			goto dospecial;
377 		}
378 		dolog(lfp, "installing: %s\n", target);
379 		opts &= ~(COMPARE|REMOVE);
380 	}
381 
382 	switch (stb.st_mode & S_IFMT) {
383 	case S_IFDIR:
384 		if ((d = opendir(target)) == NULL) {
385 			error("%s: %s\n", target, strerror(errno));
386 			return;
387 		}
388 		(void) snprintf(buf, sizeof(buf), "D%o %04o 0 0 %s %s %s\n",
389 		    opts, stb.st_mode & 07777, protoname(), protogroup(),
390 		    rname);
391 		if (debug)
392 			printf("buf = %s", buf);
393 		if (write(rem, buf, strlen(buf)) < 0)
394 			error("can not write dir spec to remote: %s\n",
395 			    strerror(errno));
396 
397 		if (response() < 0) {
398 			closedir(d);
399 			return;
400 		}
401 
402 		if (opts & REMOVE)
403 			rmchk(opts);
404 
405 		otp = tp;
406 		len = tp - target;
407 		while ((dp = readdir(d)) != NULL) {
408 			if (!strcmp(dp->d_name, ".") ||
409 			    !strcmp(dp->d_name, ".."))
410 				continue;
411 			if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
412 				error("%s/%s: Name too long\n", target,
413 					dp->d_name);
414 				continue;
415 			}
416 			tp = otp;
417 			*tp++ = '/';
418 			cp = dp->d_name;
419 			while ((*tp++ = *cp++) != 0)
420 				;
421 			tp--;
422 			sendf(dp->d_name, opts);
423 		}
424 		closedir(d);
425 		if (write(rem, "E\n", 2) < 0)
426 			error("can not write E to remote: %s\n",
427 			    strerror(errno));
428 		(void) response();
429 		tp = otp;
430 		*tp = '\0';
431 		return;
432 
433 	case S_IFLNK:
434 		if (u != 1)
435 			opts |= COMPARE;
436 		if (stb.st_nlink > 1) {
437 			struct linkbuf *lp;
438 
439 			if ((lp = savelink(&stb)) != NULL) {
440 				/* install link */
441 				if (*lp->target == 0)
442 				(void) snprintf(buf, sizeof(buf),
443 				    "k%o %s %s\n", opts, lp->pathname, rname);
444 				else
445 				(void) snprintf(buf, sizeof(buf),
446 				    "k%o %s/%s %s\n", opts, lp->target,
447 				    lp->pathname, rname);
448 				if (debug)
449 					printf("buf = %s", buf);
450 				if (write(rem, buf, strlen(buf)) < 0)
451 					error("can not write link spec to remote: %s\n",
452 					    strerror(errno));
453 				(void) response();
454 				return;
455 			}
456 		}
457 		(void) snprintf(buf, sizeof(buf), "K%o %o %lld %ld %s %s %s\n",
458 		    opts, stb.st_mode & 07777, (unsigned long long)stb.st_size,
459 		    (u_long)stb.st_mtime, protoname(), protogroup(), rname);
460 		if (debug)
461 			printf("buf = %s", buf);
462 		if (write(rem, buf, strlen(buf)) < 0)
463 			error("can not write link spec to remote: %s\n",
464 			    strerror(errno));
465 		if (response() < 0)
466 			return;
467 		sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
468 		if (write(rem, buf, stb.st_size) < 0)
469 			error("can not write link name to remote: %s\n",
470 			    strerror(errno));
471 		if (debug)
472 			printf("readlink = %.*s\n", (int)stb.st_size, buf);
473 		goto done;
474 
475 	case S_IFREG:
476 		break;
477 
478 	default:
479 		error("%s: not a file or directory\n", target);
480 		return;
481 	}
482 
483 	if (u == 2) {
484 		if (opts & VERIFY) {
485 			dolog(lfp, "need to update: %s\n", target);
486 			goto dospecial;
487 		}
488 		dolog(lfp, "updating: %s\n", target);
489 	}
490 
491 	if (stb.st_nlink > 1) {
492 		struct linkbuf *lp;
493 
494 		if ((lp = savelink(&stb)) != NULL) {
495 			/* install link */
496 			if (*lp->target == 0)
497 			(void) snprintf(buf, sizeof(buf), "k%o %s %s\n", opts,
498 				lp->pathname, rname);
499 			else
500 			(void) snprintf(buf, sizeof(buf), "k%o %s/%s %s\n",
501 			    opts, lp->target, lp->pathname, rname);
502 			if (debug)
503 				printf("buf = %s", buf);
504 			if (write(rem, buf, strlen(buf)) <0)
505 				error("write of file name failed: %s\n",
506 				    strerror(errno));
507 			(void) response();
508 			return;
509 		}
510 	}
511 
512 	if ((f = open(target, O_RDONLY, 0)) < 0) {
513 		error("%s: %s\n", target, strerror(errno));
514 		return;
515 	}
516 	(void)snprintf(buf, sizeof(buf), "R%o %o %lld %lu %s %s %s\n", opts,
517 		stb.st_mode & 07777, (unsigned long long)stb.st_size,
518 		(u_long)stb.st_mtime, protoname(), protogroup(), rname);
519 	if (debug)
520 		printf("buf = %s", buf);
521 	if (write(rem, buf, strlen(buf)) < 0)
522 		error("write of file name failed: %s\n", strerror(errno));
523 	if (response() < 0) {
524 		(void) close(f);
525 		return;
526 	}
527 	sizerr = 0;
528 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
529 		int amt = BUFSIZ;
530 		if (i + amt > stb.st_size)
531 			amt = stb.st_size - i;
532 		if (sizerr == 0 && read(f, buf, amt) != amt)
533 			sizerr = 1;
534 		if (write(rem, buf, amt) < 0)
535 			error("write of file data failed: %s\n", strerror(errno));
536 	}
537 	(void) close(f);
538 done:
539 	if (sizerr) {
540 		error("%s: file changed size\n", target);
541 		err();
542 	} else
543 		ack();
544 	f = response();
545 	if (f < 0 || (f == 0 && (opts & COMPARE)))
546 		return;
547 dospecial:
548 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
549 		if (sc->sc_type != SPECIAL)
550 			continue;
551 		if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
552 			continue;
553 		dolog(lfp, "special \"%s\"\n", sc->sc_name);
554 		if (opts & VERIFY)
555 			continue;
556 		(void) snprintf(buf, sizeof(buf), "SFILE=%s;%s\n", target,
557 		    sc->sc_name);
558 		if (debug)
559 			printf("buf = %s", buf);
560 		if (write(rem, buf, strlen(buf)) < 0)
561 			error("write of special failed: %s\n", strerror(errno));
562 		while (response() > 0)
563 			;
564 	}
565 }
566 
567 static struct linkbuf *
savelink(struct stat * st)568 savelink(struct stat *st)
569 {
570 	struct linkbuf *lp;
571 
572 	for (lp = ihead; lp != NULL; lp = lp->nextp)
573 		if (lp->inum == st->st_ino && lp->devnum == st->st_dev) {
574 			lp->count--;
575 			return(lp);
576 		}
577 	lp = (struct linkbuf *) malloc(sizeof(*lp));
578 	if (lp == NULL)
579 		dolog(lfp, "out of memory, link information lost\n");
580 	else {
581 		lp->nextp = ihead;
582 		ihead = lp;
583 		lp->inum = st->st_ino;
584 		lp->devnum = st->st_dev;
585 		lp->count = st->st_nlink - 1;
586  		if (Destcopy) {
587  			/*
588  			 * Change the starting directory of target
589  			 * into the destination directory
590  			 */
591  			strncpy(lp->pathname, Destcopy, Destcopylen);
592  			strlcpy(lp->pathname + Destcopylen, target + Sourcelen, sizeof(lp->pathname) - Destcopylen);
593  		} else
594 			strlcpy(lp->pathname, target, sizeof(lp->pathname));
595 		if (Tdest)
596 			strlcpy(lp->target, Tdest, sizeof(lp->target));
597 		else
598 			*lp->target = 0;
599 	}
600 	return(NULL);
601 }
602 
603 /*
604  * Check to see if file needs to be updated on the remote machine.
605  * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
606  * and 3 if comparing binaries to determine if out of date.
607  */
608 static int
update(char * rname,int opts,struct stat * st)609 update(char *rname, int opts, struct stat *st)
610 {
611 	char *cp, *s;
612 	off_t size;
613 	time_t mtime;
614 
615 	if (debug)
616 		printf("update(%s, %lx, %lx)\n", rname, (long)opts, (long)st);
617 
618 	/*
619 	 * Check to see if the file exists on the remote machine.
620 	 */
621 	(void) snprintf(buf, sizeof(buf), "Q%s\n", rname);
622 	if (debug)
623 		printf("buf = %s", buf);
624 	if (write(rem, buf, strlen(buf)) < 0)
625 		error("write to remote failed: %s\n", strerror(errno));
626 again:
627 	cp = s = buf;
628 	do {
629 		if (read(rem, cp, 1) != 1)
630 			lostconn(0);
631 	} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
632 
633 	switch (*s++) {
634 	case 'Y':
635 		break;
636 
637 	case 'N':  /* file doesn't exist so install it */
638 		return(1);
639 
640 	case '\1':
641 		nerrs++;
642 		if (*s != '\n') {
643 			if (!iamremote) {
644 				fflush(stdout);
645 				(void) write(2, s, cp - s);
646 			}
647 			if (lfp != NULL)
648 				(void) fwrite(s, 1, cp - s, lfp);
649 		}
650 		return(0);
651 
652 	case '\3':
653 		*--cp = '\0';
654 		if (lfp != NULL)
655 			dolog(lfp, "update: note: %s\n", s);
656 		goto again;
657 
658 	default:
659 		*--cp = '\0';
660 		error("update: unexpected response '%s'\n", s);
661 		return(0);
662 	}
663 
664 	if (*s == '\n')
665 		return(2);
666 
667 	if (opts & COMPARE)
668 		return(3);
669 
670 	size = 0;
671 	while (isdigit((unsigned char)*s))
672 		size = size * 10 + (*s++ - '0');
673 	if (*s++ != ' ') {
674 		error("update: size not delimited\n");
675 		return(0);
676 	}
677 	mtime = 0;
678 	while (isdigit((unsigned char)*s))
679 		mtime = mtime * 10 + (*s++ - '0');
680 	if (*s != '\n') {
681 		error("update: mtime not delimited\n");
682 		return(0);
683 	}
684 	/*
685 	 * File needs to be updated?
686 	 */
687 	if (opts & YOUNGER) {
688 		if (st->st_mtime == mtime)
689 			return(0);
690 		if (st->st_mtime < mtime) {
691 			dolog(lfp, "Warning: %s: remote copy is newer\n",
692 			    target);
693 			return(0);
694 		}
695 	} else if (st->st_mtime == mtime && st->st_size == size)
696 		return(0);
697 	return(2);
698 }
699 
700 /*
701  * Query. Check to see if file exists. Return one of the following:
702  *	N\n		- doesn't exist
703  *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
704  *	Y\n		- exists and its a directory or symbolic link
705  *	^Aerror message\n
706  */
707 static void
query(char * name)708 query(char *name)
709 {
710 	struct stat stb;
711 
712 	if (catname)
713 		(void) snprintf(tp, sizeof(target) - (tp - target),
714 		    "/%s", name);
715 
716 	if (lstat(target, &stb) < 0) {
717 		if (errno == ENOENT) {
718 			if (write(rem, "N\n", 2) < 0)
719 				error("write to remote failed: %s\n",
720 				    strerror(errno));
721 		} else
722 			error("%s:%s: %s\n", host, target, strerror(errno));
723 		*tp = '\0';
724 		return;
725 	}
726 
727 	switch (stb.st_mode & S_IFMT) {
728 	case S_IFREG:
729 		(void)snprintf(buf, sizeof(buf), "Y%lld %ld\n",
730 		    (unsigned long long)stb.st_size, (u_long)stb.st_mtime);
731 		if (write(rem, buf, strlen(buf)) < 0)
732 			error("write to remote failed: %s\n", strerror(errno));
733 		break;
734 
735 	case S_IFLNK:
736 	case S_IFDIR:
737 		if (write(rem, "Y\n", 2) < 0)
738 			error("write to remote failed: %s\n", strerror(errno));
739 		break;
740 
741 	default:
742 		error("%s: not a file or directory\n", name);
743 		break;
744 	}
745 	*tp = '\0';
746 }
747 
748 static void
recvf(char * cmd,int type)749 recvf(char *cmd, int type)
750 {
751 	char *cp = cmd;
752 	int f = -1, opts = 0, wrerr;
753 	mode_t mode;
754 	off_t i, size;
755 	time_t mtime;
756 	struct stat stb;
757 	char *owner, *group;
758 	char new[BUFSIZ];
759 	extern char *tempname;
760 
761 	while (*cp >= '0' && *cp <= '7')
762 		opts = (opts << 3) | (*cp++ - '0');
763 	if (*cp++ != ' ') {
764 		error("recvf: options not delimited\n");
765 		return;
766 	}
767 	mode = 0;
768 	while (*cp >= '0' && *cp <= '7')
769 		mode = (mode << 3) | (*cp++ - '0');
770 	if (*cp++ != ' ') {
771 		error("recvf: mode not delimited\n");
772 		return;
773 	}
774 	size = 0;
775 	while (isdigit((unsigned char)*cp))
776 		size = size * 10 + (*cp++ - '0');
777 	if (*cp++ != ' ') {
778 		error("recvf: size not delimited\n");
779 		return;
780 	}
781 	mtime = 0;
782 	while (isdigit((unsigned char)*cp))
783 		mtime = mtime * 10 + (*cp++ - '0');
784 	if (*cp++ != ' ') {
785 		error("recvf: mtime not delimited\n");
786 		return;
787 	}
788 	owner = cp;
789 	while (*cp && *cp != ' ')
790 		cp++;
791 	if (*cp != ' ') {
792 		error("recvf: owner name not delimited\n");
793 		return;
794 	}
795 	*cp++ = '\0';
796 	group = cp;
797 	while (*cp && *cp != ' ')
798 		cp++;
799 	if (*cp != ' ') {
800 		error("recvf: group name not delimited\n");
801 		return;
802 	}
803 	*cp++ = '\0';
804 
805 	if (type == S_IFDIR) {
806 		if (catname >= (int)sizeof(stp)) {
807 			error("%s:%s: too many directory levels\n",
808 				host, target);
809 			return;
810 		}
811 		stp[catname] = tp;
812 		if (catname++) {
813 			*tp++ = '/';
814 			while ((*tp++ = *cp++) != 0)
815 				;
816 			tp--;
817 		}
818 		if (opts & VERIFY) {
819 			ack();
820 			return;
821 		}
822 		if (lstat(target, &stb) == 0) {
823 			if (S_ISDIR(stb.st_mode)) {
824 				if ((stb.st_mode & 07777) == mode) {
825 					ack();
826 					return;
827 				}
828 				buf[0] = '\0';
829 				(void) snprintf(buf + 1, sizeof(buf) - 1,
830 			    "%s: Warning: remote mode %o != local mode %o\n",
831 				    target, stb.st_mode & 07777, mode);
832 				if (write(rem, buf, strlen(buf + 1) + 1) < 0)
833 					error("write to remote failed: %s\n",
834 					    strerror(errno));
835 				return;
836 			}
837 			errno = ENOTDIR;
838 		} else if ((errno == ENOENT && mkdir(target, mode) == 0) ||
839 		    (chkparent(target) == 0 && mkdir(target, mode) == 0)) {
840 			if (fchtogm(-1, target, mtime, owner, group, mode) == 0)
841 				ack();
842 			return;
843 		}
844 		error("%s:%s: %s\n", host, target, strerror(errno));
845 		tp = stp[--catname];
846 		*tp = '\0';
847 		return;
848 	}
849 
850 	if (catname)
851 		(void) snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
852 	cp = strrchr(target, '/');
853 	if (cp == NULL)
854 		strlcpy(new, tempname, sizeof(new));
855 	else if (cp == target)
856 		(void) snprintf(new, sizeof(new), "/%s", tempname);
857 	else {
858 		*cp = '\0';
859 		(void) snprintf(new, sizeof(new), "%s/%s", target, tempname);
860 		*cp = '/';
861 	}
862 
863 	if (type == S_IFLNK) {
864 		int j;
865 
866 		ack();
867 		cp = buf;
868 		for (i = 0; i < size; i += j) {
869 			if ((j = read(rem, cp, size - i)) <= 0)
870 				cleanup(0);
871 			cp += j;
872 		}
873 		*cp = '\0';
874 		if (response() < 0) {
875 			err();
876 			return;
877 		}
878 		if (symlink(buf, new) < 0) {
879 			if (errno != ENOENT || chkparent(new) < 0 ||
880 			    symlink(buf, new) < 0)
881 				goto badnew1;
882 		}
883 		mode &= 0777;
884 		if (opts & COMPARE) {
885 			char tbuf[BUFSIZ];
886 
887 			if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
888 			    i == size && strncmp(buf, tbuf, size) == 0) {
889 				(void) unlink(new);
890 				ack();
891 				return;
892 			}
893 			if (opts & VERIFY)
894 				goto differ;
895 		}
896 		goto fixup;
897 	}
898 
899 	if ((f = creat(new, mode)) < 0) {
900 		if (errno != ENOENT || chkparent(new) < 0 ||
901 		    (f = creat(new, mode)) < 0)
902 			goto badnew1;
903 	}
904 
905 	ack();
906 	wrerr = 0;
907 	for (i = 0; i < size; i += BUFSIZ) {
908 		int amt = BUFSIZ;
909 
910 		cp = buf;
911 		if (i + amt > size)
912 			amt = size - i;
913 		do {
914 			int j = read(rem, cp, amt);
915 
916 			if (j <= 0) {
917 				(void) close(f);
918 				(void) unlink(new);
919 				cleanup(0);
920 			}
921 			amt -= j;
922 			cp += j;
923 		} while (amt > 0);
924 		amt = BUFSIZ;
925 		if (i + amt > size)
926 			amt = size - i;
927 		if (wrerr == 0 && write(f, buf, amt) != amt) {
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
hardlink(char * cmd)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
chkparent(char * name)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
fchtogm(int fd,char * file,time_t mtime,char * owner,char * group,__mode_t mode)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 != (gid_t)-1) {
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
rmchk(int opts)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
clean(char * cp)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
removeit(struct stat * st)1299 removeit(struct stat *st)
1300 {
1301 	DIR *d;
1302 	struct dirent *dp;
1303 	char *cp;
1304 	struct stat stb;
1305 	char *otp;
1306 	int len;
1307 
1308 	switch (st->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
dospecial(char * cmd)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, NULL);
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
dolog(FILE * fp,const char * fmt,...)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
error(const char * fmt,...)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
fatal(const char * fmt,...)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
response(void)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 		/* FALLTHROUGH */
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 /*ARGSUSED*/
cleanup(int signo __unused)1569 cleanup(int signo __unused)
1570 {
1571 	(void) unlink(tempfile);
1572 	exit(1);
1573 }
1574 
1575 static void
note(const char * fmt,...)1576 note(const char *fmt, ...)
1577 {
1578 	static char nbuf[BUFSIZ];
1579 	va_list ap;
1580 
1581 	va_start(ap, fmt);
1582 	(void)vsnprintf(nbuf, sizeof(nbuf), fmt, ap);
1583 	va_end(ap);
1584 	comment(nbuf);
1585 }
1586 
1587 static void
comment(char * s)1588 comment(char *s)
1589 {
1590 	char c;
1591 
1592 	c = '\3';
1593 	write(rem, &c, 1);
1594 	write(rem, s, strlen(s));
1595 	c = '\n';
1596 	write(rem, &c, 1);
1597 }
1598