xref: /netbsd-src/usr.bin/rdist/server.c (revision fbffadb9f864c0324fb295860ab0faeb187269cc)
1*fbffadb9Smrg /*	$NetBSD: server.c,v 1.33 2019/02/03 03:19:29 mrg Exp $	*/
23aab4f7dSthorpej 
361f28255Scgd /*
4a5bfdf78Scgd  * Copyright (c) 1983, 1993
5a5bfdf78Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
323b3cf635Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
343aab4f7dSthorpej #if 0
353aab4f7dSthorpej static char sccsid[] = "@(#)server.c	8.1 (Berkeley) 6/9/93";
363aab4f7dSthorpej #else
37*fbffadb9Smrg __RCSID("$NetBSD: server.c,v 1.33 2019/02/03 03:19:29 mrg Exp $");
383aab4f7dSthorpej #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
410026c9e9Smrg #include <sys/types.h>
42a5bfdf78Scgd #include <sys/wait.h>
430026c9e9Smrg 
440026c9e9Smrg #include <errno.h>
45b751ad2cSchristos #include <fcntl.h>
46a9b4ca62Swiz #include <grp.h>
47a9b4ca62Swiz #include <pwd.h>
48a9b4ca62Swiz #include <stdarg.h>
490026c9e9Smrg 
5061f28255Scgd #include "defs.h"
5161f28255Scgd 
52c08b0cf3Smrg #define	ack() 	do { if (write(rem, "\0\n", 2) < 0) error("ack failed: %s\n", strerror(errno)); } while (0)
53c08b0cf3Smrg #define	err() 	do { if (write(rem, "\1\n", 2) < 0) error("err failed: %s\n", strerror(errno)); } while (0)
5461f28255Scgd 
5561f28255Scgd struct	linkbuf *ihead;		/* list of files with more than one link */
5661f28255Scgd char	buf[BUFSIZ];		/* general purpose buffer */
5761f28255Scgd char	target[BUFSIZ];		/* target/source directory name */
5861f28255Scgd char	*tp;			/* pointer to end of target name */
5961f28255Scgd char	*Tdest;			/* pointer to last T dest*/
600428b497Smycroft char	*Destcopy;		/* pointer to current dest */
610428b497Smycroft int	Destcopylen;		/* length of destination directory name */
620428b497Smycroft int	Sourcelen;		/* length of source directory name */
6361f28255Scgd int	catname;		/* cat name to target name */
6461f28255Scgd char	*stp[32];		/* stack of saved tp's for directories */
6561f28255Scgd int	oumask;			/* old umask for creating files */
6661f28255Scgd 
6761f28255Scgd extern	FILE *lfp;		/* log file for mailing changes */
6861f28255Scgd 
69a9b4ca62Swiz static int	chkparent(char *);
70a9b4ca62Swiz static void	clean(char *);
71a9b4ca62Swiz static void	comment(char *);
72a9b4ca62Swiz static void	dospecial(char *);
73a9b4ca62Swiz static int	fchtogm(int, char *, time_t, char *, char *, mode_t);
74a9b4ca62Swiz static void	hardlink(char *);
75a9b4ca62Swiz static void	note(const char *, ...)
76135600f9Sis      __attribute__((__format__(__printf__, 1, 2)));
77a9b4ca62Swiz static void	query(char *);
78a9b4ca62Swiz static void	recvf(char *, int);
79a9b4ca62Swiz static void	removeit(struct stat *);
80a9b4ca62Swiz static int	response(void);
81a9b4ca62Swiz static void	rmchk(int);
82a5bfdf78Scgd static struct linkbuf *
83a9b4ca62Swiz 		    savelink(struct stat *);
84a9b4ca62Swiz static void	sendf(char *, int);
85a9b4ca62Swiz static int	update(char *, int, struct stat *);
8661f28255Scgd 
8761f28255Scgd /*
8861f28255Scgd  * Server routine to read requests and process them.
8961f28255Scgd  * Commands are:
9061f28255Scgd  *	Tname	- Transmit file if out of date
9161f28255Scgd  *	Vname	- Verify if file out of date or not
9261f28255Scgd  *	Qname	- Query if file exists. Return mtime & size if it does.
9361f28255Scgd  */
94a5bfdf78Scgd void
server(void)95a9b4ca62Swiz server(void)
9661f28255Scgd {
9761f28255Scgd 	char cmdbuf[BUFSIZ];
983b3cf635Slukem 	char *cp;
9961f28255Scgd 
10061f28255Scgd 	signal(SIGHUP, cleanup);
10161f28255Scgd 	signal(SIGINT, cleanup);
10261f28255Scgd 	signal(SIGQUIT, cleanup);
10361f28255Scgd 	signal(SIGTERM, cleanup);
10461f28255Scgd 	signal(SIGPIPE, cleanup);
10561f28255Scgd 
10661f28255Scgd 	rem = 0;
10761f28255Scgd 	oumask = umask(0);
108336eeb5fSthorpej 	(void) snprintf(buf, sizeof(buf), "V%d\n", VERSION);
109c08b0cf3Smrg 	if (write(rem, buf, strlen(buf)) < 0)
110c08b0cf3Smrg 		error("server: could not write remote end: %s\n",
111c08b0cf3Smrg 		    strerror(errno));
11261f28255Scgd 
11361f28255Scgd 	for (;;) {
11461f28255Scgd 		cp = cmdbuf;
11561f28255Scgd 		if (read(rem, cp, 1) <= 0)
11661f28255Scgd 			return;
11761f28255Scgd 		if (*cp++ == '\n') {
11861f28255Scgd 			error("server: expected control record\n");
11961f28255Scgd 			continue;
12061f28255Scgd 		}
12161f28255Scgd 		do {
12261f28255Scgd 			if (read(rem, cp, 1) != 1)
123a5bfdf78Scgd 				cleanup(0);
12461f28255Scgd 		} while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
12561f28255Scgd 		*--cp = '\0';
12661f28255Scgd 		cp = cmdbuf;
12761f28255Scgd 		switch (*cp++) {
12861f28255Scgd 		case 'T':  /* init target file/directory name */
12961f28255Scgd 			catname = 1;	/* target should be directory */
13061f28255Scgd 			goto dotarget;
13161f28255Scgd 
13261f28255Scgd 		case 't':  /* init target file/directory name */
13361f28255Scgd 			catname = 0;
13461f28255Scgd 		dotarget:
13561f28255Scgd 			if (exptilde(target, cp) == NULL)
13661f28255Scgd 				continue;
13761f28255Scgd 			tp = target;
13861f28255Scgd 			while (*tp)
13961f28255Scgd 				tp++;
14061f28255Scgd 			ack();
14161f28255Scgd 			continue;
14261f28255Scgd 
14361f28255Scgd 		case 'R':  /* Transfer a regular file. */
14461f28255Scgd 			recvf(cp, S_IFREG);
14561f28255Scgd 			continue;
14661f28255Scgd 
14761f28255Scgd 		case 'D':  /* Transfer a directory. */
14861f28255Scgd 			recvf(cp, S_IFDIR);
14961f28255Scgd 			continue;
15061f28255Scgd 
15161f28255Scgd 		case 'K':  /* Transfer symbolic link. */
15261f28255Scgd 			recvf(cp, S_IFLNK);
15361f28255Scgd 			continue;
15461f28255Scgd 
15561f28255Scgd 		case 'k':  /* Transfer hard link. */
15661f28255Scgd 			hardlink(cp);
15761f28255Scgd 			continue;
15861f28255Scgd 
15961f28255Scgd 		case 'E':  /* End. (of directory) */
16061f28255Scgd 			*tp = '\0';
16161f28255Scgd 			if (catname <= 0) {
16261f28255Scgd 				error("server: too many 'E's\n");
16361f28255Scgd 				continue;
16461f28255Scgd 			}
16561f28255Scgd 			tp = stp[--catname];
16661f28255Scgd 			*tp = '\0';
16761f28255Scgd 			ack();
16861f28255Scgd 			continue;
16961f28255Scgd 
17061f28255Scgd 		case 'C':  /* Clean. Cleanup a directory */
17161f28255Scgd 			clean(cp);
17261f28255Scgd 			continue;
17361f28255Scgd 
17461f28255Scgd 		case 'Q':  /* Query. Does the file/directory exist? */
17561f28255Scgd 			query(cp);
17661f28255Scgd 			continue;
17761f28255Scgd 
17861f28255Scgd 		case 'S':  /* Special. Execute commands */
17961f28255Scgd 			dospecial(cp);
18061f28255Scgd 			continue;
18161f28255Scgd 
18261f28255Scgd #ifdef notdef
18361f28255Scgd 		/*
18461f28255Scgd 		 * These entries are reserved but not currently used.
18561f28255Scgd 		 * The intent is to allow remote hosts to have master copies.
18661f28255Scgd 		 * Currently, only the host rdist runs on can have masters.
18761f28255Scgd 		 */
18861f28255Scgd 		case 'X':  /* start a new list of files to exclude */
18961f28255Scgd 			except = bp = NULL;
19061f28255Scgd 		case 'x':  /* add name to list of files to exclude */
19161f28255Scgd 			if (*cp == '\0') {
19261f28255Scgd 				ack();
19361f28255Scgd 				continue;
19461f28255Scgd 			}
19561f28255Scgd 			if (*cp == '~') {
19661f28255Scgd 				if (exptilde(buf, cp) == NULL)
19761f28255Scgd 					continue;
19861f28255Scgd 				cp = buf;
19961f28255Scgd 			}
20061f28255Scgd 			if (bp == NULL)
20161f28255Scgd 				except = bp = expand(makeblock(NAME, cp), E_VARS);
20261f28255Scgd 			else
20361f28255Scgd 				bp->b_next = expand(makeblock(NAME, cp), E_VARS);
20461f28255Scgd 			while (bp->b_next != NULL)
20561f28255Scgd 				bp = bp->b_next;
20661f28255Scgd 			ack();
20761f28255Scgd 			continue;
20861f28255Scgd 
20961f28255Scgd 		case 'I':  /* Install. Transfer file if out of date. */
21061f28255Scgd 			opts = 0;
21161f28255Scgd 			while (*cp >= '0' && *cp <= '7')
21261f28255Scgd 				opts = (opts << 3) | (*cp++ - '0');
21361f28255Scgd 			if (*cp++ != ' ') {
21461f28255Scgd 				error("server: options not delimited\n");
21561f28255Scgd 				return;
21661f28255Scgd 			}
21761f28255Scgd 			install(cp, opts);
21861f28255Scgd 			continue;
21961f28255Scgd 
22061f28255Scgd 		case 'L':  /* Log. save message in log file */
22161f28255Scgd 			log(lfp, cp);
22261f28255Scgd 			continue;
22361f28255Scgd #endif
22461f28255Scgd 
22561f28255Scgd 		case '\1':
22661f28255Scgd 			nerrs++;
22761f28255Scgd 			continue;
22861f28255Scgd 
22961f28255Scgd 		case '\2':
23061f28255Scgd 			return;
23161f28255Scgd 
23261f28255Scgd 		default:
23361f28255Scgd 			error("server: unknown command '%s'\n", cp);
234*fbffadb9Smrg 			/* FALLTHROUGH */
23561f28255Scgd 		case '\0':
23661f28255Scgd 			continue;
23761f28255Scgd 		}
23861f28255Scgd 	}
23961f28255Scgd }
24061f28255Scgd 
24161f28255Scgd /*
24261f28255Scgd  * Update the file(s) if they are different.
24361f28255Scgd  * destdir = 1 if destination should be a directory
24461f28255Scgd  * (i.e., more than one source is being copied to the same destination).
24561f28255Scgd  */
246a5bfdf78Scgd void
install(char * src,char * dest,int destdir,int opts)247a9b4ca62Swiz install(char *src, char *dest, int destdir, int opts)
24861f28255Scgd {
24961f28255Scgd 	char *rname;
25061f28255Scgd 	char destcopy[BUFSIZ];
25161f28255Scgd 
25261f28255Scgd 	if (dest == NULL) {
25361f28255Scgd 		opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
25461f28255Scgd 		dest = src;
2550428b497Smycroft 	} else if (!(opts & WHOLE)) {
2560428b497Smycroft 		/* prepare for proper renaming of directory trees */
2570428b497Smycroft 		Destcopy = destcopy;
2580428b497Smycroft 		Destcopylen = strlen(dest);
2590428b497Smycroft 		while (Destcopylen > 0 && dest[Destcopylen] == '/')
2600428b497Smycroft 		    Destcopylen--;
26161f28255Scgd 	}
2620428b497Smycroft 	strlcpy(destcopy, dest, sizeof(destcopy));
26361f28255Scgd 
26461f28255Scgd 	if (nflag || debug) {
26561f28255Scgd 		printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
26661f28255Scgd 			opts & WHOLE ? " -w" : "",
26761f28255Scgd 			opts & YOUNGER ? " -y" : "",
26861f28255Scgd 			opts & COMPARE ? " -b" : "",
26961f28255Scgd 			opts & REMOVE ? " -R" : "", src, dest);
27061f28255Scgd 		if (nflag)
27161f28255Scgd 			return;
27261f28255Scgd 	}
27361f28255Scgd 
27461f28255Scgd 	rname = exptilde(target, src);
27561f28255Scgd 	if (rname == NULL)
27661f28255Scgd 		return;
27761f28255Scgd 	tp = target;
27861f28255Scgd 	while (*tp)
27961f28255Scgd 		tp++;
2800428b497Smycroft 	if (Destcopy) {
2810428b497Smycroft 		/* We can only do this after expansion of src */
2820428b497Smycroft 		Sourcelen = strlen(target);
2830428b497Smycroft 		while (Sourcelen > 0 && target[Sourcelen] == '/')
2840428b497Smycroft 		    Sourcelen--;
2850428b497Smycroft 	}
28661f28255Scgd 	/*
28761f28255Scgd 	 * If we are renaming a directory and we want to preserve
288251b3464Swiz 	 * the directory hierarchy (-w), we must strip off the leading
28961f28255Scgd 	 * directory name and preserve the rest.
29061f28255Scgd 	 */
29161f28255Scgd 	if (opts & WHOLE) {
29261f28255Scgd 		while (*rname == '/')
29361f28255Scgd 			rname++;
29461f28255Scgd 		destdir = 1;
29561f28255Scgd 	} else {
2963b3cf635Slukem 		rname = strrchr(target, '/');
29761f28255Scgd 		if (rname == NULL)
29861f28255Scgd 			rname = target;
29961f28255Scgd 		else
30061f28255Scgd 			rname++;
30161f28255Scgd 	}
30261f28255Scgd 	if (debug)
30361f28255Scgd 		printf("target = %s, rname = %s\n", target, rname);
30461f28255Scgd 	/*
30561f28255Scgd 	 * Pass the destination file/directory name to remote.
30661f28255Scgd 	 */
307336eeb5fSthorpej 	(void) snprintf(buf, sizeof(buf), "%c%s\n", destdir ? 'T' : 't', dest);
30861f28255Scgd 	if (debug)
30961f28255Scgd 		printf("buf = %s", buf);
310c08b0cf3Smrg 	if (write(rem, buf, strlen(buf)) < 0)
311c08b0cf3Smrg 		error("could not pass filename to remote: %s\n",
312c08b0cf3Smrg 		    strerror(errno));
31361f28255Scgd 	if (response() < 0)
31461f28255Scgd 		return;
31561f28255Scgd 
3160428b497Smycroft 	if (destdir)
31761f28255Scgd 		Tdest = destcopy;
31861f28255Scgd 	sendf(rname, opts);
3190428b497Smycroft 	Destcopy = 0;
32061f28255Scgd 	Tdest = 0;
32161f28255Scgd }
32261f28255Scgd 
32361f28255Scgd #define protoname() (pw ? pw->pw_name : user)
32461f28255Scgd #define protogroup() (gr ? gr->gr_name : group)
32561f28255Scgd /*
32661f28255Scgd  * Transfer the file or directory in target[].
32761f28255Scgd  * rname is the name of the file on the remote host.
32861f28255Scgd  */
329a5bfdf78Scgd static void
sendf(char * rname,int opts)330a9b4ca62Swiz sendf(char *rname, int opts)
33161f28255Scgd {
3323b3cf635Slukem 	struct subcmd *sc;
33361f28255Scgd 	struct stat stb;
33461f28255Scgd 	int sizerr, f, u, len;
33561f28255Scgd 	off_t i;
33661f28255Scgd 	DIR *d;
337b751ad2cSchristos 	struct dirent *dp;
33861f28255Scgd 	char *otp, *cp;
33961f28255Scgd 	extern struct subcmd *subcmds;
34061f28255Scgd 	static char user[15], group[15];
34161f28255Scgd 
34261f28255Scgd 	if (debug)
34361f28255Scgd 		printf("sendf(%s, %x)\n", rname, opts);
34461f28255Scgd 
34561f28255Scgd 	if (except(target))
34661f28255Scgd 		return;
34761f28255Scgd 	if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
34861f28255Scgd 		error("%s: %s\n", target, strerror(errno));
34961f28255Scgd 		return;
35061f28255Scgd 	}
35161f28255Scgd 	if ((u = update(rname, opts, &stb)) == 0) {
352985cca83Smycroft 		if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
35361f28255Scgd 			(void) savelink(&stb);
35461f28255Scgd 		return;
35561f28255Scgd 	}
35661f28255Scgd 
35761f28255Scgd 	if (pw == NULL || pw->pw_uid != stb.st_uid)
35861f28255Scgd 		if ((pw = getpwuid(stb.st_uid)) == NULL) {
359f393397bSthorpej 			dolog(lfp, "%s: no password entry for uid %d \n",
36061f28255Scgd 				target, stb.st_uid);
36161f28255Scgd 			pw = NULL;
3623b3cf635Slukem 			(void)snprintf(user, sizeof(user), ":%lu",
3633b3cf635Slukem 			    (u_long)stb.st_uid);
36461f28255Scgd 		}
36561f28255Scgd 	if (gr == NULL || gr->gr_gid != stb.st_gid)
36661f28255Scgd 		if ((gr = getgrgid(stb.st_gid)) == NULL) {
367f393397bSthorpej 			dolog(lfp, "%s: no name for group %d\n",
36861f28255Scgd 				target, stb.st_gid);
36961f28255Scgd 			gr = NULL;
370336eeb5fSthorpej 			(void)snprintf(group, sizeof(group), ":%lu",
3713b3cf635Slukem 			    (u_long)stb.st_gid);
37261f28255Scgd 		}
37361f28255Scgd 	if (u == 1) {
37461f28255Scgd 		if (opts & VERIFY) {
375f393397bSthorpej 			dolog(lfp, "need to install: %s\n", target);
37661f28255Scgd 			goto dospecial;
37761f28255Scgd 		}
378f393397bSthorpej 		dolog(lfp, "installing: %s\n", target);
37961f28255Scgd 		opts &= ~(COMPARE|REMOVE);
38061f28255Scgd 	}
38161f28255Scgd 
38261f28255Scgd 	switch (stb.st_mode & S_IFMT) {
38361f28255Scgd 	case S_IFDIR:
38461f28255Scgd 		if ((d = opendir(target)) == NULL) {
38561f28255Scgd 			error("%s: %s\n", target, strerror(errno));
38661f28255Scgd 			return;
38761f28255Scgd 		}
388336eeb5fSthorpej 		(void) snprintf(buf, sizeof(buf), "D%o %04o 0 0 %s %s %s\n",
389336eeb5fSthorpej 		    opts, stb.st_mode & 07777, protoname(), protogroup(),
390336eeb5fSthorpej 		    rname);
39161f28255Scgd 		if (debug)
39261f28255Scgd 			printf("buf = %s", buf);
393c08b0cf3Smrg 		if (write(rem, buf, strlen(buf)) < 0)
394c08b0cf3Smrg 			error("can not write dir spec to remote: %s\n",
395c08b0cf3Smrg 			    strerror(errno));
396c08b0cf3Smrg 
39761f28255Scgd 		if (response() < 0) {
39861f28255Scgd 			closedir(d);
39961f28255Scgd 			return;
40061f28255Scgd 		}
40161f28255Scgd 
40261f28255Scgd 		if (opts & REMOVE)
40361f28255Scgd 			rmchk(opts);
40461f28255Scgd 
40561f28255Scgd 		otp = tp;
40661f28255Scgd 		len = tp - target;
4073b3cf635Slukem 		while ((dp = readdir(d)) != NULL) {
40861f28255Scgd 			if (!strcmp(dp->d_name, ".") ||
40961f28255Scgd 			    !strcmp(dp->d_name, ".."))
41061f28255Scgd 				continue;
41161f28255Scgd 			if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
41261f28255Scgd 				error("%s/%s: Name too long\n", target,
41361f28255Scgd 					dp->d_name);
41461f28255Scgd 				continue;
41561f28255Scgd 			}
41661f28255Scgd 			tp = otp;
41761f28255Scgd 			*tp++ = '/';
41861f28255Scgd 			cp = dp->d_name;
4193b3cf635Slukem 			while ((*tp++ = *cp++) != 0)
42061f28255Scgd 				;
42161f28255Scgd 			tp--;
42261f28255Scgd 			sendf(dp->d_name, opts);
42361f28255Scgd 		}
42461f28255Scgd 		closedir(d);
425c08b0cf3Smrg 		if (write(rem, "E\n", 2) < 0)
426c08b0cf3Smrg 			error("can not write E to remote: %s\n",
427c08b0cf3Smrg 			    strerror(errno));
42861f28255Scgd 		(void) response();
42961f28255Scgd 		tp = otp;
43061f28255Scgd 		*tp = '\0';
43161f28255Scgd 		return;
43261f28255Scgd 
43361f28255Scgd 	case S_IFLNK:
43461f28255Scgd 		if (u != 1)
43561f28255Scgd 			opts |= COMPARE;
43661f28255Scgd 		if (stb.st_nlink > 1) {
43761f28255Scgd 			struct linkbuf *lp;
43861f28255Scgd 
43961f28255Scgd 			if ((lp = savelink(&stb)) != NULL) {
44061f28255Scgd 				/* install link */
44161f28255Scgd 				if (*lp->target == 0)
442336eeb5fSthorpej 				(void) snprintf(buf, sizeof(buf),
443336eeb5fSthorpej 				    "k%o %s %s\n", opts, lp->pathname, rname);
44461f28255Scgd 				else
445336eeb5fSthorpej 				(void) snprintf(buf, sizeof(buf),
446336eeb5fSthorpej 				    "k%o %s/%s %s\n", opts, lp->target,
447336eeb5fSthorpej 				    lp->pathname, rname);
44861f28255Scgd 				if (debug)
44961f28255Scgd 					printf("buf = %s", buf);
450c08b0cf3Smrg 				if (write(rem, buf, strlen(buf)) < 0)
451c08b0cf3Smrg 					error("can not write link spec to remote: %s\n",
452c08b0cf3Smrg 					    strerror(errno));
45361f28255Scgd 				(void) response();
45461f28255Scgd 				return;
45561f28255Scgd 			}
45661f28255Scgd 		}
457c2b84904Slukem 		(void) snprintf(buf, sizeof(buf), "K%o %o %lld %ld %s %s %s\n",
458538e6929Smrg 		    opts, stb.st_mode & 07777, (unsigned long long)stb.st_size,
459538e6929Smrg 		    (u_long)stb.st_mtime, protoname(), protogroup(), rname);
46061f28255Scgd 		if (debug)
46161f28255Scgd 			printf("buf = %s", buf);
462c08b0cf3Smrg 		if (write(rem, buf, strlen(buf)) < 0)
463c08b0cf3Smrg 			error("can not write link spec to remote: %s\n",
464c08b0cf3Smrg 			    strerror(errno));
46561f28255Scgd 		if (response() < 0)
46661f28255Scgd 			return;
46761f28255Scgd 		sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
468c08b0cf3Smrg 		if (write(rem, buf, stb.st_size) < 0)
469c08b0cf3Smrg 			error("can not write link name to remote: %s\n",
470c08b0cf3Smrg 			    strerror(errno));
47161f28255Scgd 		if (debug)
47261f28255Scgd 			printf("readlink = %.*s\n", (int)stb.st_size, buf);
47361f28255Scgd 		goto done;
47461f28255Scgd 
47561f28255Scgd 	case S_IFREG:
47661f28255Scgd 		break;
47761f28255Scgd 
47861f28255Scgd 	default:
47961f28255Scgd 		error("%s: not a file or directory\n", target);
48061f28255Scgd 		return;
48161f28255Scgd 	}
48261f28255Scgd 
48361f28255Scgd 	if (u == 2) {
48461f28255Scgd 		if (opts & VERIFY) {
485f393397bSthorpej 			dolog(lfp, "need to update: %s\n", target);
48661f28255Scgd 			goto dospecial;
48761f28255Scgd 		}
488f393397bSthorpej 		dolog(lfp, "updating: %s\n", target);
48961f28255Scgd 	}
49061f28255Scgd 
49161f28255Scgd 	if (stb.st_nlink > 1) {
49261f28255Scgd 		struct linkbuf *lp;
49361f28255Scgd 
49461f28255Scgd 		if ((lp = savelink(&stb)) != NULL) {
49561f28255Scgd 			/* install link */
49661f28255Scgd 			if (*lp->target == 0)
497336eeb5fSthorpej 			(void) snprintf(buf, sizeof(buf), "k%o %s %s\n", opts,
49861f28255Scgd 				lp->pathname, rname);
49961f28255Scgd 			else
500336eeb5fSthorpej 			(void) snprintf(buf, sizeof(buf), "k%o %s/%s %s\n",
501336eeb5fSthorpej 			    opts, lp->target, lp->pathname, rname);
50261f28255Scgd 			if (debug)
50361f28255Scgd 				printf("buf = %s", buf);
504c08b0cf3Smrg 			if (write(rem, buf, strlen(buf)) <0)
505c08b0cf3Smrg 				error("write of file name failed: %s\n",
506c08b0cf3Smrg 				    strerror(errno));
50761f28255Scgd 			(void) response();
50861f28255Scgd 			return;
50961f28255Scgd 		}
51061f28255Scgd 	}
51161f28255Scgd 
512a5bfdf78Scgd 	if ((f = open(target, O_RDONLY, 0)) < 0) {
51361f28255Scgd 		error("%s: %s\n", target, strerror(errno));
51461f28255Scgd 		return;
51561f28255Scgd 	}
516c2b84904Slukem 	(void)snprintf(buf, sizeof(buf), "R%o %o %lld %lu %s %s %s\n", opts,
517538e6929Smrg 		stb.st_mode & 07777, (unsigned long long)stb.st_size,
518538e6929Smrg 		(u_long)stb.st_mtime, protoname(), protogroup(), rname);
51961f28255Scgd 	if (debug)
52061f28255Scgd 		printf("buf = %s", buf);
521c08b0cf3Smrg 	if (write(rem, buf, strlen(buf)) < 0)
522c08b0cf3Smrg 		error("write of file name failed: %s\n", strerror(errno));
52361f28255Scgd 	if (response() < 0) {
52461f28255Scgd 		(void) close(f);
52561f28255Scgd 		return;
52661f28255Scgd 	}
52761f28255Scgd 	sizerr = 0;
52861f28255Scgd 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
52961f28255Scgd 		int amt = BUFSIZ;
53061f28255Scgd 		if (i + amt > stb.st_size)
53161f28255Scgd 			amt = stb.st_size - i;
53261f28255Scgd 		if (sizerr == 0 && read(f, buf, amt) != amt)
53361f28255Scgd 			sizerr = 1;
534c08b0cf3Smrg 		if (write(rem, buf, amt) < 0)
535c08b0cf3Smrg 			error("write of file data failed: %s\n", strerror(errno));
53661f28255Scgd 	}
53761f28255Scgd 	(void) close(f);
53861f28255Scgd done:
53961f28255Scgd 	if (sizerr) {
54061f28255Scgd 		error("%s: file changed size\n", target);
54161f28255Scgd 		err();
54261f28255Scgd 	} else
54361f28255Scgd 		ack();
54461f28255Scgd 	f = response();
5453b3cf635Slukem 	if (f < 0 || (f == 0 && (opts & COMPARE)))
54661f28255Scgd 		return;
54761f28255Scgd dospecial:
54861f28255Scgd 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
54961f28255Scgd 		if (sc->sc_type != SPECIAL)
55061f28255Scgd 			continue;
55161f28255Scgd 		if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
55261f28255Scgd 			continue;
553f393397bSthorpej 		dolog(lfp, "special \"%s\"\n", sc->sc_name);
55461f28255Scgd 		if (opts & VERIFY)
55561f28255Scgd 			continue;
556336eeb5fSthorpej 		(void) snprintf(buf, sizeof(buf), "SFILE=%s;%s\n", target,
557336eeb5fSthorpej 		    sc->sc_name);
55861f28255Scgd 		if (debug)
55961f28255Scgd 			printf("buf = %s", buf);
560c08b0cf3Smrg 		if (write(rem, buf, strlen(buf)) < 0)
561c08b0cf3Smrg 			error("write of special failed: %s\n", strerror(errno));
56261f28255Scgd 		while (response() > 0)
56361f28255Scgd 			;
56461f28255Scgd 	}
56561f28255Scgd }
56661f28255Scgd 
567a5bfdf78Scgd static struct linkbuf *
savelink(struct stat * st)5684538623fSlukem savelink(struct stat *st)
56961f28255Scgd {
57061f28255Scgd 	struct linkbuf *lp;
57161f28255Scgd 
57261f28255Scgd 	for (lp = ihead; lp != NULL; lp = lp->nextp)
5734538623fSlukem 		if (lp->inum == st->st_ino && lp->devnum == st->st_dev) {
57461f28255Scgd 			lp->count--;
57561f28255Scgd 			return(lp);
57661f28255Scgd 		}
57761f28255Scgd 	lp = (struct linkbuf *) malloc(sizeof(*lp));
57861f28255Scgd 	if (lp == NULL)
579f393397bSthorpej 		dolog(lfp, "out of memory, link information lost\n");
58061f28255Scgd 	else {
58161f28255Scgd 		lp->nextp = ihead;
58261f28255Scgd 		ihead = lp;
5834538623fSlukem 		lp->inum = st->st_ino;
5844538623fSlukem 		lp->devnum = st->st_dev;
5854538623fSlukem 		lp->count = st->st_nlink - 1;
5860428b497Smycroft  		if (Destcopy) {
5870428b497Smycroft  			/*
5880428b497Smycroft  			 * Change the starting directory of target
5890428b497Smycroft  			 * into the destination directory
5900428b497Smycroft  			 */
5910428b497Smycroft  			strncpy(lp->pathname, Destcopy, Destcopylen);
5920428b497Smycroft  			strlcpy(lp->pathname + Destcopylen, target + Sourcelen, sizeof(lp->pathname) - Destcopylen);
5930428b497Smycroft  		} else
594e97c220eSitojun 			strlcpy(lp->pathname, target, sizeof(lp->pathname));
59561f28255Scgd 		if (Tdest)
596e97c220eSitojun 			strlcpy(lp->target, Tdest, sizeof(lp->target));
59761f28255Scgd 		else
59861f28255Scgd 			*lp->target = 0;
59961f28255Scgd 	}
60061f28255Scgd 	return(NULL);
60161f28255Scgd }
60261f28255Scgd 
60361f28255Scgd /*
60461f28255Scgd  * Check to see if file needs to be updated on the remote machine.
60561f28255Scgd  * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
60661f28255Scgd  * and 3 if comparing binaries to determine if out of date.
60761f28255Scgd  */
608a5bfdf78Scgd static int
update(char * rname,int opts,struct stat * st)6094538623fSlukem update(char *rname, int opts, struct stat *st)
61061f28255Scgd {
6113b3cf635Slukem 	char *cp, *s;
6123b3cf635Slukem 	off_t size;
6133b3cf635Slukem 	time_t mtime;
61461f28255Scgd 
61561f28255Scgd 	if (debug)
6164538623fSlukem 		printf("update(%s, %lx, %lx)\n", rname, (long)opts, (long)st);
61761f28255Scgd 
61861f28255Scgd 	/*
61961f28255Scgd 	 * Check to see if the file exists on the remote machine.
62061f28255Scgd 	 */
621336eeb5fSthorpej 	(void) snprintf(buf, sizeof(buf), "Q%s\n", rname);
62261f28255Scgd 	if (debug)
62361f28255Scgd 		printf("buf = %s", buf);
624c08b0cf3Smrg 	if (write(rem, buf, strlen(buf)) < 0)
625c08b0cf3Smrg 		error("write to remote failed: %s\n", strerror(errno));
62661f28255Scgd again:
62761f28255Scgd 	cp = s = buf;
62861f28255Scgd 	do {
62961f28255Scgd 		if (read(rem, cp, 1) != 1)
630a5bfdf78Scgd 			lostconn(0);
63161f28255Scgd 	} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
63261f28255Scgd 
63361f28255Scgd 	switch (*s++) {
63461f28255Scgd 	case 'Y':
63561f28255Scgd 		break;
63661f28255Scgd 
63761f28255Scgd 	case 'N':  /* file doesn't exist so install it */
63861f28255Scgd 		return(1);
63961f28255Scgd 
64061f28255Scgd 	case '\1':
64161f28255Scgd 		nerrs++;
64261f28255Scgd 		if (*s != '\n') {
64361f28255Scgd 			if (!iamremote) {
64461f28255Scgd 				fflush(stdout);
64561f28255Scgd 				(void) write(2, s, cp - s);
64661f28255Scgd 			}
64761f28255Scgd 			if (lfp != NULL)
64861f28255Scgd 				(void) fwrite(s, 1, cp - s, lfp);
64961f28255Scgd 		}
65061f28255Scgd 		return(0);
65161f28255Scgd 
65261f28255Scgd 	case '\3':
65361f28255Scgd 		*--cp = '\0';
65461f28255Scgd 		if (lfp != NULL)
655f393397bSthorpej 			dolog(lfp, "update: note: %s\n", s);
65661f28255Scgd 		goto again;
65761f28255Scgd 
65861f28255Scgd 	default:
65961f28255Scgd 		*--cp = '\0';
66061f28255Scgd 		error("update: unexpected response '%s'\n", s);
66161f28255Scgd 		return(0);
66261f28255Scgd 	}
66361f28255Scgd 
66461f28255Scgd 	if (*s == '\n')
66561f28255Scgd 		return(2);
66661f28255Scgd 
66761f28255Scgd 	if (opts & COMPARE)
66861f28255Scgd 		return(3);
66961f28255Scgd 
67061f28255Scgd 	size = 0;
6717b232cdfSchristos 	while (isdigit((unsigned char)*s))
67261f28255Scgd 		size = size * 10 + (*s++ - '0');
67361f28255Scgd 	if (*s++ != ' ') {
67461f28255Scgd 		error("update: size not delimited\n");
67561f28255Scgd 		return(0);
67661f28255Scgd 	}
67761f28255Scgd 	mtime = 0;
6787b232cdfSchristos 	while (isdigit((unsigned char)*s))
67961f28255Scgd 		mtime = mtime * 10 + (*s++ - '0');
68061f28255Scgd 	if (*s != '\n') {
68161f28255Scgd 		error("update: mtime not delimited\n");
68261f28255Scgd 		return(0);
68361f28255Scgd 	}
68461f28255Scgd 	/*
68561f28255Scgd 	 * File needs to be updated?
68661f28255Scgd 	 */
68761f28255Scgd 	if (opts & YOUNGER) {
6884538623fSlukem 		if (st->st_mtime == mtime)
68961f28255Scgd 			return(0);
6904538623fSlukem 		if (st->st_mtime < mtime) {
691f393397bSthorpej 			dolog(lfp, "Warning: %s: remote copy is newer\n",
692f393397bSthorpej 			    target);
69361f28255Scgd 			return(0);
69461f28255Scgd 		}
6954538623fSlukem 	} else if (st->st_mtime == mtime && st->st_size == size)
69661f28255Scgd 		return(0);
69761f28255Scgd 	return(2);
69861f28255Scgd }
69961f28255Scgd 
70061f28255Scgd /*
70161f28255Scgd  * Query. Check to see if file exists. Return one of the following:
70261f28255Scgd  *	N\n		- doesn't exist
70361f28255Scgd  *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
70461f28255Scgd  *	Y\n		- exists and its a directory or symbolic link
70561f28255Scgd  *	^Aerror message\n
70661f28255Scgd  */
707a5bfdf78Scgd static void
query(char * name)708a9b4ca62Swiz query(char *name)
70961f28255Scgd {
71061f28255Scgd 	struct stat stb;
71161f28255Scgd 
71261f28255Scgd 	if (catname)
713cf0afdf8Sthorpej 		(void) snprintf(tp, sizeof(target) - (tp - target),
714cf0afdf8Sthorpej 		    "/%s", name);
71561f28255Scgd 
71661f28255Scgd 	if (lstat(target, &stb) < 0) {
717c08b0cf3Smrg 		if (errno == ENOENT) {
718c08b0cf3Smrg 			if (write(rem, "N\n", 2) < 0)
719c08b0cf3Smrg 				error("write to remote failed: %s\n",
720c08b0cf3Smrg 				    strerror(errno));
721c08b0cf3Smrg 		} else
72261f28255Scgd 			error("%s:%s: %s\n", host, target, strerror(errno));
72361f28255Scgd 		*tp = '\0';
72461f28255Scgd 		return;
72561f28255Scgd 	}
72661f28255Scgd 
72761f28255Scgd 	switch (stb.st_mode & S_IFMT) {
72861f28255Scgd 	case S_IFREG:
729c2b84904Slukem 		(void)snprintf(buf, sizeof(buf), "Y%lld %ld\n",
730538e6929Smrg 		    (unsigned long long)stb.st_size, (u_long)stb.st_mtime);
731c08b0cf3Smrg 		if (write(rem, buf, strlen(buf)) < 0)
732c08b0cf3Smrg 			error("write to remote failed: %s\n", strerror(errno));
73361f28255Scgd 		break;
73461f28255Scgd 
73561f28255Scgd 	case S_IFLNK:
73661f28255Scgd 	case S_IFDIR:
737c08b0cf3Smrg 		if (write(rem, "Y\n", 2) < 0)
738c08b0cf3Smrg 			error("write to remote failed: %s\n", strerror(errno));
73961f28255Scgd 		break;
74061f28255Scgd 
74161f28255Scgd 	default:
74261f28255Scgd 		error("%s: not a file or directory\n", name);
74361f28255Scgd 		break;
74461f28255Scgd 	}
74561f28255Scgd 	*tp = '\0';
74661f28255Scgd }
74761f28255Scgd 
748a5bfdf78Scgd static void
recvf(char * cmd,int type)749a9b4ca62Swiz recvf(char *cmd, int type)
75061f28255Scgd {
7513b3cf635Slukem 	char *cp = cmd;
752b52ab07dSchristos 	int f = -1, opts = 0, wrerr;
753985cca83Smycroft 	mode_t mode;
75461f28255Scgd 	off_t i, size;
75561f28255Scgd 	time_t mtime;
75661f28255Scgd 	struct stat stb;
75761f28255Scgd 	char *owner, *group;
75861f28255Scgd 	char new[BUFSIZ];
75961f28255Scgd 	extern char *tempname;
76061f28255Scgd 
76161f28255Scgd 	while (*cp >= '0' && *cp <= '7')
76261f28255Scgd 		opts = (opts << 3) | (*cp++ - '0');
76361f28255Scgd 	if (*cp++ != ' ') {
76461f28255Scgd 		error("recvf: options not delimited\n");
76561f28255Scgd 		return;
76661f28255Scgd 	}
76761f28255Scgd 	mode = 0;
76861f28255Scgd 	while (*cp >= '0' && *cp <= '7')
76961f28255Scgd 		mode = (mode << 3) | (*cp++ - '0');
77061f28255Scgd 	if (*cp++ != ' ') {
77161f28255Scgd 		error("recvf: mode not delimited\n");
77261f28255Scgd 		return;
77361f28255Scgd 	}
77461f28255Scgd 	size = 0;
7757b232cdfSchristos 	while (isdigit((unsigned char)*cp))
77661f28255Scgd 		size = size * 10 + (*cp++ - '0');
77761f28255Scgd 	if (*cp++ != ' ') {
77861f28255Scgd 		error("recvf: size not delimited\n");
77961f28255Scgd 		return;
78061f28255Scgd 	}
78161f28255Scgd 	mtime = 0;
7827b232cdfSchristos 	while (isdigit((unsigned char)*cp))
78361f28255Scgd 		mtime = mtime * 10 + (*cp++ - '0');
78461f28255Scgd 	if (*cp++ != ' ') {
78561f28255Scgd 		error("recvf: mtime not delimited\n");
78661f28255Scgd 		return;
78761f28255Scgd 	}
78861f28255Scgd 	owner = cp;
78961f28255Scgd 	while (*cp && *cp != ' ')
79061f28255Scgd 		cp++;
79161f28255Scgd 	if (*cp != ' ') {
79261f28255Scgd 		error("recvf: owner name not delimited\n");
79361f28255Scgd 		return;
79461f28255Scgd 	}
79561f28255Scgd 	*cp++ = '\0';
79661f28255Scgd 	group = cp;
79761f28255Scgd 	while (*cp && *cp != ' ')
79861f28255Scgd 		cp++;
79961f28255Scgd 	if (*cp != ' ') {
80061f28255Scgd 		error("recvf: group name not delimited\n");
80161f28255Scgd 		return;
80261f28255Scgd 	}
80361f28255Scgd 	*cp++ = '\0';
80461f28255Scgd 
80561f28255Scgd 	if (type == S_IFDIR) {
8064538623fSlukem 		if (catname >= (int)sizeof(stp)) {
80761f28255Scgd 			error("%s:%s: too many directory levels\n",
80861f28255Scgd 				host, target);
80961f28255Scgd 			return;
81061f28255Scgd 		}
81161f28255Scgd 		stp[catname] = tp;
81261f28255Scgd 		if (catname++) {
81361f28255Scgd 			*tp++ = '/';
8143b3cf635Slukem 			while ((*tp++ = *cp++) != 0)
81561f28255Scgd 				;
81661f28255Scgd 			tp--;
81761f28255Scgd 		}
81861f28255Scgd 		if (opts & VERIFY) {
81961f28255Scgd 			ack();
82061f28255Scgd 			return;
82161f28255Scgd 		}
82261f28255Scgd 		if (lstat(target, &stb) == 0) {
823985cca83Smycroft 			if (S_ISDIR(stb.st_mode)) {
82461f28255Scgd 				if ((stb.st_mode & 07777) == mode) {
82561f28255Scgd 					ack();
82661f28255Scgd 					return;
82761f28255Scgd 				}
82861f28255Scgd 				buf[0] = '\0';
829336eeb5fSthorpej 				(void) snprintf(buf + 1, sizeof(buf) - 1,
83061f28255Scgd 			    "%s: Warning: remote mode %o != local mode %o\n",
83161f28255Scgd 				    target, stb.st_mode & 07777, mode);
832c08b0cf3Smrg 				if (write(rem, buf, strlen(buf + 1) + 1) < 0)
833c08b0cf3Smrg 					error("write to remote failed: %s\n",
834c08b0cf3Smrg 					    strerror(errno));
83561f28255Scgd 				return;
83661f28255Scgd 			}
83761f28255Scgd 			errno = ENOTDIR;
838985cca83Smycroft 		} else if ((errno == ENOENT && mkdir(target, mode) == 0) ||
8393b3cf635Slukem 		    (chkparent(target) == 0 && mkdir(target, mode) == 0)) {
840985cca83Smycroft 			if (fchtogm(-1, target, mtime, owner, group, mode) == 0)
84161f28255Scgd 				ack();
84261f28255Scgd 			return;
84361f28255Scgd 		}
84461f28255Scgd 		error("%s:%s: %s\n", host, target, strerror(errno));
84561f28255Scgd 		tp = stp[--catname];
84661f28255Scgd 		*tp = '\0';
84761f28255Scgd 		return;
84861f28255Scgd 	}
84961f28255Scgd 
85061f28255Scgd 	if (catname)
851cf0afdf8Sthorpej 		(void) snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
8523b3cf635Slukem 	cp = strrchr(target, '/');
85361f28255Scgd 	if (cp == NULL)
854e97c220eSitojun 		strlcpy(new, tempname, sizeof(new));
85561f28255Scgd 	else if (cp == target)
856336eeb5fSthorpej 		(void) snprintf(new, sizeof(new), "/%s", tempname);
85761f28255Scgd 	else {
85861f28255Scgd 		*cp = '\0';
859336eeb5fSthorpej 		(void) snprintf(new, sizeof(new), "%s/%s", target, tempname);
86061f28255Scgd 		*cp = '/';
86161f28255Scgd 	}
86261f28255Scgd 
86361f28255Scgd 	if (type == S_IFLNK) {
86461f28255Scgd 		int j;
86561f28255Scgd 
86661f28255Scgd 		ack();
86761f28255Scgd 		cp = buf;
86861f28255Scgd 		for (i = 0; i < size; i += j) {
86961f28255Scgd 			if ((j = read(rem, cp, size - i)) <= 0)
870a5bfdf78Scgd 				cleanup(0);
87161f28255Scgd 			cp += j;
87261f28255Scgd 		}
87361f28255Scgd 		*cp = '\0';
87461f28255Scgd 		if (response() < 0) {
87561f28255Scgd 			err();
87661f28255Scgd 			return;
87761f28255Scgd 		}
87861f28255Scgd 		if (symlink(buf, new) < 0) {
87961f28255Scgd 			if (errno != ENOENT || chkparent(new) < 0 ||
88061f28255Scgd 			    symlink(buf, new) < 0)
881a5bfdf78Scgd 				goto badnew1;
88261f28255Scgd 		}
88361f28255Scgd 		mode &= 0777;
88461f28255Scgd 		if (opts & COMPARE) {
88561f28255Scgd 			char tbuf[BUFSIZ];
88661f28255Scgd 
88761f28255Scgd 			if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
88861f28255Scgd 			    i == size && strncmp(buf, tbuf, size) == 0) {
88961f28255Scgd 				(void) unlink(new);
89061f28255Scgd 				ack();
89161f28255Scgd 				return;
89261f28255Scgd 			}
89361f28255Scgd 			if (opts & VERIFY)
89461f28255Scgd 				goto differ;
89561f28255Scgd 		}
89661f28255Scgd 		goto fixup;
89761f28255Scgd 	}
89861f28255Scgd 
89961f28255Scgd 	if ((f = creat(new, mode)) < 0) {
90061f28255Scgd 		if (errno != ENOENT || chkparent(new) < 0 ||
90161f28255Scgd 		    (f = creat(new, mode)) < 0)
902a5bfdf78Scgd 			goto badnew1;
90361f28255Scgd 	}
90461f28255Scgd 
90561f28255Scgd 	ack();
90661f28255Scgd 	wrerr = 0;
90761f28255Scgd 	for (i = 0; i < size; i += BUFSIZ) {
90861f28255Scgd 		int amt = BUFSIZ;
90961f28255Scgd 
91061f28255Scgd 		cp = buf;
91161f28255Scgd 		if (i + amt > size)
91261f28255Scgd 			amt = size - i;
91361f28255Scgd 		do {
91461f28255Scgd 			int j = read(rem, cp, amt);
91561f28255Scgd 
91661f28255Scgd 			if (j <= 0) {
91761f28255Scgd 				(void) close(f);
91861f28255Scgd 				(void) unlink(new);
919a5bfdf78Scgd 				cleanup(0);
92061f28255Scgd 			}
92161f28255Scgd 			amt -= j;
92261f28255Scgd 			cp += j;
92361f28255Scgd 		} while (amt > 0);
92461f28255Scgd 		amt = BUFSIZ;
92561f28255Scgd 		if (i + amt > size)
92661f28255Scgd 			amt = size - i;
92761f28255Scgd 		if (wrerr == 0 && write(f, buf, amt) != amt) {
92861f28255Scgd 			wrerr++;
92961f28255Scgd 		}
93061f28255Scgd 	}
93161f28255Scgd 	if (response() < 0) {
93261f28255Scgd 		err();
933a5bfdf78Scgd 		goto badnew2;
93461f28255Scgd 	}
935a5bfdf78Scgd 	if (wrerr)
936a5bfdf78Scgd 		goto badnew1;
93761f28255Scgd 	if (opts & COMPARE) {
93861f28255Scgd 		FILE *f1, *f2;
93961f28255Scgd 		int c;
94061f28255Scgd 
94161f28255Scgd 		if ((f1 = fopen(target, "r")) == NULL)
942a5bfdf78Scgd 			goto badtarget;
94361f28255Scgd 		if ((f2 = fopen(new, "r")) == NULL) {
944a5bfdf78Scgd badnew1:		error("%s:%s: %s\n", host, new, strerror(errno));
945a5bfdf78Scgd 			goto badnew2;
94661f28255Scgd 		}
94761f28255Scgd 		while ((c = getc(f1)) == getc(f2))
94861f28255Scgd 			if (c == EOF) {
94961f28255Scgd 				(void) fclose(f1);
95061f28255Scgd 				(void) fclose(f2);
95161f28255Scgd 				ack();
952a5bfdf78Scgd 				goto badnew2;
95361f28255Scgd 			}
95461f28255Scgd 		(void) fclose(f1);
95561f28255Scgd 		(void) fclose(f2);
95661f28255Scgd 		if (opts & VERIFY) {
957a5bfdf78Scgd differ:			buf[0] = '\0';
958336eeb5fSthorpej 			(void)snprintf(buf + 1, sizeof(buf) - 1,
959336eeb5fSthorpej 			    "need to update: %s\n",target);
96061f28255Scgd 			(void) write(rem, buf, strlen(buf + 1) + 1);
961a5bfdf78Scgd 			goto badnew2;
96261f28255Scgd 		}
96361f28255Scgd 	}
96461f28255Scgd 
965985cca83Smycroft 	if (fchtogm(f, new, mtime, owner, group, mode) < 0) {
966bd664fdbSmrg badnew2:
967bd664fdbSmrg 		if (f != -1)
968bd664fdbSmrg 			(void) close(f);
96961f28255Scgd 		(void) unlink(new);
97061f28255Scgd 		return;
97161f28255Scgd 	}
972a5bfdf78Scgd 	(void) close(f);
973a5bfdf78Scgd 
974a5bfdf78Scgd fixup:	if (rename(new, target) < 0) {
975a5bfdf78Scgd badtarget:	error("%s:%s: %s\n", host, target, strerror(errno));
97661f28255Scgd 		(void) unlink(new);
97761f28255Scgd 		return;
97861f28255Scgd 	}
979a5bfdf78Scgd 
98061f28255Scgd 	if (opts & COMPARE) {
98161f28255Scgd 		buf[0] = '\0';
982336eeb5fSthorpej 		(void) snprintf(buf + 1, sizeof(buf) - 1,
983336eeb5fSthorpej 		    "updated %s\n", target);
98461f28255Scgd 		(void) write(rem, buf, strlen(buf + 1) + 1);
98561f28255Scgd 	} else
98661f28255Scgd 		ack();
98761f28255Scgd }
98861f28255Scgd 
98961f28255Scgd /*
99061f28255Scgd  * Creat a hard link to existing file.
99161f28255Scgd  */
992a5bfdf78Scgd static void
hardlink(char * cmd)993a9b4ca62Swiz hardlink(char *cmd)
99461f28255Scgd {
9953b3cf635Slukem 	char *cp;
99661f28255Scgd 	struct stat stb;
99761f28255Scgd 	char *oldname;
99861f28255Scgd 	int opts, exists = 0;
99961f28255Scgd 
100061f28255Scgd 	cp = cmd;
100161f28255Scgd 	opts = 0;
100261f28255Scgd 	while (*cp >= '0' && *cp <= '7')
100361f28255Scgd 		opts = (opts << 3) | (*cp++ - '0');
100461f28255Scgd 	if (*cp++ != ' ') {
100561f28255Scgd 		error("hardlink: options not delimited\n");
100661f28255Scgd 		return;
100761f28255Scgd 	}
100861f28255Scgd 	oldname = cp;
100961f28255Scgd 	while (*cp && *cp != ' ')
101061f28255Scgd 		cp++;
101161f28255Scgd 	if (*cp != ' ') {
101261f28255Scgd 		error("hardlink: oldname name not delimited\n");
101361f28255Scgd 		return;
101461f28255Scgd 	}
101561f28255Scgd 	*cp++ = '\0';
101661f28255Scgd 
101761f28255Scgd 	if (catname) {
1018cf0afdf8Sthorpej 		(void) snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
101961f28255Scgd 	}
102061f28255Scgd 	if (lstat(target, &stb) == 0) {
1021985cca83Smycroft 		if (!S_ISREG(stb.st_mode) && !S_ISLNK(stb.st_mode)) {
102261f28255Scgd 			error("%s: %s: not a regular file\n", host, target);
102361f28255Scgd 			return;
102461f28255Scgd 		}
102561f28255Scgd 		exists = 1;
102661f28255Scgd 	}
102761f28255Scgd 	if (chkparent(target) < 0 ) {
102861f28255Scgd 		error("%s:%s: %s (no parent)\n",
102961f28255Scgd 			host, target, strerror(errno));
103061f28255Scgd 		return;
103161f28255Scgd 	}
103261f28255Scgd 	if (exists && (unlink(target) < 0)) {
103361f28255Scgd 		error("%s:%s: %s (unlink)\n",
103461f28255Scgd 			host, target, strerror(errno));
103561f28255Scgd 		return;
103661f28255Scgd 	}
103761f28255Scgd 	if (link(oldname, target) < 0) {
103861f28255Scgd 		error("%s:can't link %s to %s\n",
103961f28255Scgd 			host, target, oldname);
104061f28255Scgd 		return;
104161f28255Scgd 	}
104261f28255Scgd 	ack();
104361f28255Scgd }
104461f28255Scgd 
104561f28255Scgd /*
104661f28255Scgd  * Check to see if parent directory exists and create one if not.
104761f28255Scgd  */
1048a5bfdf78Scgd static int
chkparent(char * name)1049a9b4ca62Swiz chkparent(char *name)
105061f28255Scgd {
10513b3cf635Slukem 	char *cp;
105261f28255Scgd 	struct stat stb;
105361f28255Scgd 
10543b3cf635Slukem 	cp = strrchr(name, '/');
105561f28255Scgd 	if (cp == NULL || cp == name)
105661f28255Scgd 		return(0);
105761f28255Scgd 	*cp = '\0';
105861f28255Scgd 	if (lstat(name, &stb) < 0) {
105961f28255Scgd 		if (errno == ENOENT && chkparent(name) >= 0 &&
106061f28255Scgd 		    mkdir(name, 0777 & ~oumask) >= 0) {
106161f28255Scgd 			*cp = '/';
106261f28255Scgd 			return(0);
106361f28255Scgd 		}
1064985cca83Smycroft 	} else if (S_ISDIR(stb.st_mode)) {
106561f28255Scgd 		*cp = '/';
106661f28255Scgd 		return(0);
106761f28255Scgd 	}
106861f28255Scgd 	*cp = '/';
106961f28255Scgd 	return(-1);
107061f28255Scgd }
107161f28255Scgd 
107261f28255Scgd /*
107361f28255Scgd  * Change owner, group and mode of file.
107461f28255Scgd  */
1075a5bfdf78Scgd static int
fchtogm(int fd,char * file,time_t mtime,char * owner,char * group,__mode_t mode)1076a9b4ca62Swiz fchtogm(int fd, char *file, time_t mtime, char *owner, char *group, __mode_t mode)
107761f28255Scgd {
10783b3cf635Slukem 	int i;
1079985cca83Smycroft 	struct timeval tv[2];
1080985cca83Smycroft 	uid_t uid;
1081985cca83Smycroft 	gid_t gid;
108261f28255Scgd 	extern char user[];
108361f28255Scgd 
108461f28255Scgd 	uid = userid;
108561f28255Scgd 	if (userid == 0) {
108661f28255Scgd 		if (*owner == ':') {
108761f28255Scgd 			uid = atoi(owner + 1);
108861f28255Scgd 		} else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
108961f28255Scgd 			if ((pw = getpwnam(owner)) == NULL) {
109061f28255Scgd 				if (mode & 04000) {
109161f28255Scgd 					note("%s:%s: unknown login name, clearing setuid",
109261f28255Scgd 						host, owner);
109361f28255Scgd 					mode &= ~04000;
109461f28255Scgd 					uid = 0;
109561f28255Scgd 				}
109661f28255Scgd 			} else
109761f28255Scgd 				uid = pw->pw_uid;
109861f28255Scgd 		} else
109961f28255Scgd 			uid = pw->pw_uid;
110061f28255Scgd 		if (*group == ':') {
110161f28255Scgd 			gid = atoi(group + 1);
110261f28255Scgd 			goto ok;
110361f28255Scgd 		}
110461f28255Scgd 	} else if ((mode & 04000) && strcmp(user, owner) != 0)
110561f28255Scgd 		mode &= ~04000;
110661f28255Scgd 	gid = -1;
110761f28255Scgd 	if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
110861f28255Scgd 		if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
110961f28255Scgd 		   || ((gr = getgrnam(group)) == NULL)) {
111061f28255Scgd 			if (mode & 02000) {
111161f28255Scgd 				note("%s:%s: unknown group", host, group);
111261f28255Scgd 				mode &= ~02000;
111361f28255Scgd 			}
111461f28255Scgd 		} else
111561f28255Scgd 			gid = gr->gr_gid;
111661f28255Scgd 	} else
111761f28255Scgd 		gid = gr->gr_gid;
11187d2ef9d9Schristos 	if (userid && gid != (gid_t)-1) {
111961f28255Scgd 		if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
112061f28255Scgd 			if (!(strcmp(user, gr->gr_mem[i])))
112161f28255Scgd 				goto ok;
112261f28255Scgd 		mode &= ~02000;
112361f28255Scgd 		gid = -1;
112461f28255Scgd 	}
1125985cca83Smycroft ok:
1126985cca83Smycroft 	(void) gettimeofday(&tv[0], (struct timezone *)0);
1127985cca83Smycroft 	tv[1].tv_sec = mtime;
1128985cca83Smycroft 	tv[1].tv_usec = 0;
1129985cca83Smycroft 	if (fd != -1 ? futimes(fd, tv) < 0 : utimes(file, tv) < 0)
1130985cca83Smycroft 		note("%s: %s utimes: %s", host, file, strerror(errno));
1131985cca83Smycroft 	if (fd != -1 ? fchown(fd, uid, gid) < 0 : chown(file, uid, gid) < 0)
1132a5bfdf78Scgd 		note("%s: %s chown: %s", host, file, strerror(errno));
1133a5bfdf78Scgd 	else if (mode & 07000 &&
1134985cca83Smycroft 	   (fd != -1 ? fchmod(fd, mode) < 0 : chmod(file, mode) < 0))
1135a5bfdf78Scgd 		note("%s: %s chmod: %s", host, file, strerror(errno));
113661f28255Scgd 	return(0);
113761f28255Scgd }
113861f28255Scgd 
113961f28255Scgd /*
114061f28255Scgd  * Check for files on the machine being updated that are not on the master
114161f28255Scgd  * machine and remove them.
114261f28255Scgd  */
1143a5bfdf78Scgd static void
rmchk(int opts)1144a9b4ca62Swiz rmchk(int opts)
114561f28255Scgd {
11463b3cf635Slukem 	char *cp, *s;
114761f28255Scgd 	struct stat stb;
114861f28255Scgd 
114961f28255Scgd 	if (debug)
115061f28255Scgd 		printf("rmchk()\n");
115161f28255Scgd 
115261f28255Scgd 	/*
115361f28255Scgd 	 * Tell the remote to clean the files from the last directory sent.
115461f28255Scgd 	 */
1155336eeb5fSthorpej 	(void) snprintf(buf, sizeof(buf), "C%o\n", opts & VERIFY);
115661f28255Scgd 	if (debug)
115761f28255Scgd 		printf("buf = %s", buf);
115861f28255Scgd 	(void) write(rem, buf, strlen(buf));
115961f28255Scgd 	if (response() < 0)
116061f28255Scgd 		return;
116161f28255Scgd 	for (;;) {
116261f28255Scgd 		cp = s = buf;
116361f28255Scgd 		do {
116461f28255Scgd 			if (read(rem, cp, 1) != 1)
1165a5bfdf78Scgd 				lostconn(0);
116661f28255Scgd 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
116761f28255Scgd 
116861f28255Scgd 		switch (*s++) {
116961f28255Scgd 		case 'Q': /* Query if file should be removed */
117061f28255Scgd 			/*
117161f28255Scgd 			 * Return the following codes to remove query.
117261f28255Scgd 			 * N\n -- file exists - DON'T remove.
117361f28255Scgd 			 * Y\n -- file doesn't exist - REMOVE.
117461f28255Scgd 			 */
117561f28255Scgd 			*--cp = '\0';
1176cf0afdf8Sthorpej 			(void) snprintf(tp, sizeof(target) - (tp - target),
1177cf0afdf8Sthorpej 			    "/%s", s);
117861f28255Scgd 			if (debug)
117961f28255Scgd 				printf("check %s\n", target);
118061f28255Scgd 			if (except(target))
118161f28255Scgd 				(void) write(rem, "N\n", 2);
118261f28255Scgd 			else if (lstat(target, &stb) < 0)
118361f28255Scgd 				(void) write(rem, "Y\n", 2);
118461f28255Scgd 			else
118561f28255Scgd 				(void) write(rem, "N\n", 2);
118661f28255Scgd 			break;
118761f28255Scgd 
118861f28255Scgd 		case '\0':
118961f28255Scgd 			*--cp = '\0';
119061f28255Scgd 			if (*s != '\0')
1191f393397bSthorpej 				dolog(lfp, "%s\n", s);
119261f28255Scgd 			break;
119361f28255Scgd 
119461f28255Scgd 		case 'E':
119561f28255Scgd 			*tp = '\0';
119661f28255Scgd 			ack();
119761f28255Scgd 			return;
119861f28255Scgd 
119961f28255Scgd 		case '\1':
120061f28255Scgd 		case '\2':
120161f28255Scgd 			nerrs++;
120261f28255Scgd 			if (*s != '\n') {
120361f28255Scgd 				if (!iamremote) {
120461f28255Scgd 					fflush(stdout);
120561f28255Scgd 					(void) write(2, s, cp - s);
120661f28255Scgd 				}
120761f28255Scgd 				if (lfp != NULL)
120861f28255Scgd 					(void) fwrite(s, 1, cp - s, lfp);
120961f28255Scgd 			}
121061f28255Scgd 			if (buf[0] == '\2')
1211a5bfdf78Scgd 				lostconn(0);
121261f28255Scgd 			break;
121361f28255Scgd 
121461f28255Scgd 		default:
121561f28255Scgd 			error("rmchk: unexpected response '%s'\n", buf);
121661f28255Scgd 			err();
121761f28255Scgd 		}
121861f28255Scgd 	}
121961f28255Scgd }
122061f28255Scgd 
122161f28255Scgd /*
122261f28255Scgd  * Check the current directory (initialized by the 'T' command to server())
122361f28255Scgd  * for extraneous files and remove them.
122461f28255Scgd  */
1225a5bfdf78Scgd static void
clean(char * cp)1226a9b4ca62Swiz clean(char *cp)
122761f28255Scgd {
122861f28255Scgd 	DIR *d;
1229b751ad2cSchristos 	struct dirent *dp;
123061f28255Scgd 	struct stat stb;
123161f28255Scgd 	char *otp;
123261f28255Scgd 	int len, opts;
123361f28255Scgd 
123461f28255Scgd 	opts = 0;
123561f28255Scgd 	while (*cp >= '0' && *cp <= '7')
123661f28255Scgd 		opts = (opts << 3) | (*cp++ - '0');
123761f28255Scgd 	if (*cp != '\0') {
123861f28255Scgd 		error("clean: options not delimited\n");
123961f28255Scgd 		return;
124061f28255Scgd 	}
124161f28255Scgd 	if ((d = opendir(target)) == NULL) {
124261f28255Scgd 		error("%s:%s: %s\n", host, target, strerror(errno));
124361f28255Scgd 		return;
124461f28255Scgd 	}
124561f28255Scgd 	ack();
124661f28255Scgd 
124761f28255Scgd 	otp = tp;
124861f28255Scgd 	len = tp - target;
12493b3cf635Slukem 	while ((dp = readdir(d)) != NULL) {
125061f28255Scgd 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
125161f28255Scgd 			continue;
125261f28255Scgd 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
125361f28255Scgd 			error("%s:%s/%s: Name too long\n",
125461f28255Scgd 				host, target, dp->d_name);
125561f28255Scgd 			continue;
125661f28255Scgd 		}
125761f28255Scgd 		tp = otp;
125861f28255Scgd 		*tp++ = '/';
1259276fd166Ssimonb 		cp = dp->d_name;
12603b3cf635Slukem 		while ((*tp++ = *cp++) != 0)
126161f28255Scgd 			;
126261f28255Scgd 		tp--;
126361f28255Scgd 		if (lstat(target, &stb) < 0) {
126461f28255Scgd 			error("%s:%s: %s\n", host, target, strerror(errno));
126561f28255Scgd 			continue;
126661f28255Scgd 		}
1267336eeb5fSthorpej 		(void) snprintf(buf, sizeof(buf), "Q%s\n", dp->d_name);
126861f28255Scgd 		(void) write(rem, buf, strlen(buf));
126961f28255Scgd 		cp = buf;
127061f28255Scgd 		do {
127161f28255Scgd 			if (read(rem, cp, 1) != 1)
1272a5bfdf78Scgd 				cleanup(0);
127361f28255Scgd 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
127461f28255Scgd 		*--cp = '\0';
127561f28255Scgd 		cp = buf;
127661f28255Scgd 		if (*cp != 'Y')
127761f28255Scgd 			continue;
127861f28255Scgd 		if (opts & VERIFY) {
127961f28255Scgd 			cp = buf;
128061f28255Scgd 			*cp++ = '\0';
1281336eeb5fSthorpej 			(void) snprintf(cp, sizeof(buf) - 1,
1282336eeb5fSthorpej 			    "need to remove: %s\n", target);
128361f28255Scgd 			(void) write(rem, buf, strlen(cp) + 1);
128461f28255Scgd 		} else
128561f28255Scgd 			removeit(&stb);
128661f28255Scgd 	}
128761f28255Scgd 	closedir(d);
128861f28255Scgd 	(void) write(rem, "E\n", 2);
128961f28255Scgd 	(void) response();
129061f28255Scgd 	tp = otp;
129161f28255Scgd 	*tp = '\0';
129261f28255Scgd }
129361f28255Scgd 
129461f28255Scgd /*
129561f28255Scgd  * Remove a file or directory (recursively) and send back an acknowledge
129661f28255Scgd  * or an error message.
129761f28255Scgd  */
1298a5bfdf78Scgd static void
removeit(struct stat * st)12994538623fSlukem removeit(struct stat *st)
130061f28255Scgd {
130161f28255Scgd 	DIR *d;
1302b751ad2cSchristos 	struct dirent *dp;
13033b3cf635Slukem 	char *cp;
130461f28255Scgd 	struct stat stb;
130561f28255Scgd 	char *otp;
130661f28255Scgd 	int len;
130761f28255Scgd 
13084538623fSlukem 	switch (st->st_mode & S_IFMT) {
130961f28255Scgd 	case S_IFREG:
131061f28255Scgd 	case S_IFLNK:
131161f28255Scgd 		if (unlink(target) < 0)
131261f28255Scgd 			goto bad;
131361f28255Scgd 		goto removed;
131461f28255Scgd 
131561f28255Scgd 	case S_IFDIR:
131661f28255Scgd 		break;
131761f28255Scgd 
131861f28255Scgd 	default:
131961f28255Scgd 		error("%s:%s: not a plain file\n", host, target);
132061f28255Scgd 		return;
132161f28255Scgd 	}
132261f28255Scgd 
132361f28255Scgd 	if ((d = opendir(target)) == NULL)
132461f28255Scgd 		goto bad;
132561f28255Scgd 
132661f28255Scgd 	otp = tp;
132761f28255Scgd 	len = tp - target;
13283b3cf635Slukem 	while ((dp = readdir(d)) != NULL) {
132961f28255Scgd 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
133061f28255Scgd 			continue;
133161f28255Scgd 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
133261f28255Scgd 			error("%s:%s/%s: Name too long\n",
133361f28255Scgd 				host, target, dp->d_name);
133461f28255Scgd 			continue;
133561f28255Scgd 		}
133661f28255Scgd 		tp = otp;
133761f28255Scgd 		*tp++ = '/';
1338276fd166Ssimonb 		cp = dp->d_name;
13393b3cf635Slukem 		while ((*tp++ = *cp++) != 0)
134061f28255Scgd 			;
134161f28255Scgd 		tp--;
134261f28255Scgd 		if (lstat(target, &stb) < 0) {
134361f28255Scgd 			error("%s:%s: %s\n", host, target, strerror(errno));
134461f28255Scgd 			continue;
134561f28255Scgd 		}
134661f28255Scgd 		removeit(&stb);
134761f28255Scgd 	}
134861f28255Scgd 	closedir(d);
134961f28255Scgd 	tp = otp;
135061f28255Scgd 	*tp = '\0';
135161f28255Scgd 	if (rmdir(target) < 0) {
135261f28255Scgd bad:
135361f28255Scgd 		error("%s:%s: %s\n", host, target, strerror(errno));
135461f28255Scgd 		return;
135561f28255Scgd 	}
135661f28255Scgd removed:
135761f28255Scgd 	cp = buf;
135861f28255Scgd 	*cp++ = '\0';
1359336eeb5fSthorpej 	(void) snprintf(cp, sizeof(buf) - 1, "removed %s\n", target);
136061f28255Scgd 	(void) write(rem, buf, strlen(cp) + 1);
136161f28255Scgd }
136261f28255Scgd 
136361f28255Scgd /*
136461f28255Scgd  * Execute a shell command to handle special cases.
136561f28255Scgd  */
1366a5bfdf78Scgd static void
dospecial(char * cmd)1367a9b4ca62Swiz dospecial(char *cmd)
136861f28255Scgd {
136961f28255Scgd 	int fd[2], status, pid, i;
13703b3cf635Slukem 	char *cp, *s;
137161f28255Scgd 	char sbuf[BUFSIZ];
137261f28255Scgd 
137361f28255Scgd 	if (pipe(fd) < 0) {
137461f28255Scgd 		error("%s\n", strerror(errno));
137561f28255Scgd 		return;
137661f28255Scgd 	}
137761f28255Scgd 	if ((pid = fork()) == 0) {
137861f28255Scgd 		/*
137961f28255Scgd 		 * Return everything the shell commands print.
138061f28255Scgd 		 */
138161f28255Scgd 		(void) close(0);
138261f28255Scgd 		(void) close(1);
138361f28255Scgd 		(void) close(2);
138461f28255Scgd 		(void) open(_PATH_DEVNULL, O_RDONLY);
138561f28255Scgd 		(void) dup(fd[1]);
138661f28255Scgd 		(void) dup(fd[1]);
138761f28255Scgd 		(void) close(fd[0]);
138861f28255Scgd 		(void) close(fd[1]);
138961f28255Scgd 		setgid(groupid);
139061f28255Scgd 		setuid(userid);
13914bc6feceSmrg 		execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
139261f28255Scgd 		_exit(127);
139361f28255Scgd 	}
139461f28255Scgd 	(void) close(fd[1]);
139561f28255Scgd 	s = sbuf;
139661f28255Scgd 	*s++ = '\0';
139761f28255Scgd 	while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
139861f28255Scgd 		cp = buf;
139961f28255Scgd 		do {
140061f28255Scgd 			*s++ = *cp++;
140161f28255Scgd 			if (cp[-1] != '\n') {
140261f28255Scgd 				if (s < &sbuf[sizeof(sbuf)-1])
140361f28255Scgd 					continue;
140461f28255Scgd 				*s++ = '\n';
140561f28255Scgd 			}
140661f28255Scgd 			/*
140761f28255Scgd 			 * Throw away blank lines.
140861f28255Scgd 			 */
140961f28255Scgd 			if (s == &sbuf[2]) {
141061f28255Scgd 				s--;
141161f28255Scgd 				continue;
141261f28255Scgd 			}
141361f28255Scgd 			(void) write(rem, sbuf, s - sbuf);
141461f28255Scgd 			s = &sbuf[1];
141561f28255Scgd 		} while (--i);
141661f28255Scgd 	}
141761f28255Scgd 	if (s > &sbuf[1]) {
141861f28255Scgd 		*s++ = '\n';
141961f28255Scgd 		(void) write(rem, sbuf, s - sbuf);
142061f28255Scgd 	}
142161f28255Scgd 	while ((i = wait(&status)) != pid && i != -1)
142261f28255Scgd 		;
142361f28255Scgd 	if (i == -1)
142461f28255Scgd 		status = -1;
142561f28255Scgd 	(void) close(fd[0]);
142661f28255Scgd 	if (status)
142761f28255Scgd 		error("shell returned %d\n", status);
142861f28255Scgd 	else
142961f28255Scgd 		ack();
143061f28255Scgd }
143161f28255Scgd 
1432a5bfdf78Scgd 
1433a5bfdf78Scgd void
dolog(FILE * fp,const char * fmt,...)1434f393397bSthorpej dolog(FILE *fp, const char *fmt, ...)
143561f28255Scgd {
1436a5bfdf78Scgd 	va_list ap;
14374c999163Swiz 
14384c999163Swiz 	/* Print changes locally if not quiet mode */
14394c999163Swiz 	if (!qflag) {
1440a5bfdf78Scgd 		va_start(ap, fmt);
1441a5bfdf78Scgd 		(void)vprintf(fmt, ap);
14424c999163Swiz 		va_end(ap);
14434c999163Swiz 	}
144461f28255Scgd 
144561f28255Scgd 	/* Save changes (for mailing) if really updating files */
14464c999163Swiz 	if (!(options & VERIFY) && fp != NULL) {
14474c999163Swiz 		va_start(ap, fmt);
1448a5bfdf78Scgd 		(void)vfprintf(fp, fmt, ap);
1449a5bfdf78Scgd 		va_end(ap);
145061f28255Scgd 	}
14514c999163Swiz }
145261f28255Scgd 
1453a5bfdf78Scgd void
error(const char * fmt,...)1454a5bfdf78Scgd error(const char *fmt, ...)
145561f28255Scgd {
145661f28255Scgd 	static FILE *fp;
1457a5bfdf78Scgd 	va_list ap;
14584c999163Swiz 
14594c999163Swiz 	++nerrs;
14604c999163Swiz 	if (!fp && !(fp = fdopen(rem, "w")))
14614c999163Swiz 		return;
1462a5bfdf78Scgd 	va_start(ap, fmt);
146361f28255Scgd 	if (iamremote) {
146461f28255Scgd 		(void)fprintf(fp, "%crdist: ", 0x01);
1465a5bfdf78Scgd 		(void)vfprintf(fp, fmt, ap);
146661f28255Scgd 		fflush(fp);
146761f28255Scgd 	}
146861f28255Scgd 	else {
146961f28255Scgd 		fflush(stdout);
147061f28255Scgd 		(void)fprintf(stderr, "rdist: ");
1471a5bfdf78Scgd 		(void)vfprintf(stderr, fmt, ap);
147261f28255Scgd 		fflush(stderr);
147361f28255Scgd 	}
14744c999163Swiz 	va_end(ap);
147561f28255Scgd 	if (lfp != NULL) {
147661f28255Scgd 		(void)fprintf(lfp, "rdist: ");
14774c999163Swiz 		va_start(ap, fmt);
1478a5bfdf78Scgd 		(void)vfprintf(lfp, fmt, ap);
14794c999163Swiz 		va_end(ap);
148061f28255Scgd 		fflush(lfp);
148161f28255Scgd 	}
148261f28255Scgd }
148361f28255Scgd 
1484a5bfdf78Scgd void
fatal(const char * fmt,...)1485a5bfdf78Scgd fatal(const char *fmt, ...)
148661f28255Scgd {
148761f28255Scgd 	static FILE *fp;
1488a5bfdf78Scgd 	va_list ap;
14894c999163Swiz 
14904c999163Swiz 	++nerrs;
14914c999163Swiz 	if (!fp && !(fp = fdopen(rem, "w")))
14924c999163Swiz 		return;
1493a5bfdf78Scgd 	va_start(ap, fmt);
149461f28255Scgd 	if (iamremote) {
149561f28255Scgd 		(void)fprintf(fp, "%crdist: ", 0x02);
1496a5bfdf78Scgd 		(void)vfprintf(fp, fmt, ap);
149761f28255Scgd 		fflush(fp);
149861f28255Scgd 	}
149961f28255Scgd 	else {
150061f28255Scgd 		fflush(stdout);
150161f28255Scgd 		(void)fprintf(stderr, "rdist: ");
1502a5bfdf78Scgd 		(void)vfprintf(stderr, fmt, ap);
150361f28255Scgd 		fflush(stderr);
150461f28255Scgd 	}
15054c999163Swiz 	va_end(ap);
150661f28255Scgd 	if (lfp != NULL) {
150761f28255Scgd 		(void)fprintf(lfp, "rdist: ");
15084c999163Swiz 		va_start(ap, fmt);
1509a5bfdf78Scgd 		(void)vfprintf(lfp, fmt, ap);
15104c999163Swiz 		va_end(ap);
151161f28255Scgd 		fflush(lfp);
151261f28255Scgd 	}
1513a5bfdf78Scgd 	cleanup(0);
151461f28255Scgd }
151561f28255Scgd 
1516a5bfdf78Scgd static int
response(void)1517a9b4ca62Swiz response(void)
151861f28255Scgd {
151961f28255Scgd 	char *cp, *s;
152061f28255Scgd 	char resp[BUFSIZ];
152161f28255Scgd 
152261f28255Scgd 	if (debug)
152361f28255Scgd 		printf("response()\n");
152461f28255Scgd 
152561f28255Scgd 	cp = s = resp;
152661f28255Scgd 	do {
152761f28255Scgd 		if (read(rem, cp, 1) != 1)
1528a5bfdf78Scgd 			lostconn(0);
152961f28255Scgd 	} while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
153061f28255Scgd 
153161f28255Scgd 	switch (*s++) {
153261f28255Scgd 	case '\0':
153361f28255Scgd 		*--cp = '\0';
153461f28255Scgd 		if (*s != '\0') {
1535f393397bSthorpej 			dolog(lfp, "%s\n", s);
153661f28255Scgd 			return(1);
153761f28255Scgd 		}
153861f28255Scgd 		return(0);
153961f28255Scgd 	case '\3':
154061f28255Scgd 		*--cp = '\0';
1541f393397bSthorpej 		dolog(lfp, "Note: %s\n",s);
154261f28255Scgd 		return(response());
154361f28255Scgd 
154461f28255Scgd 	default:
154561f28255Scgd 		s--;
1546*fbffadb9Smrg 		/* FALLTHROUGH */
154761f28255Scgd 	case '\1':
154861f28255Scgd 	case '\2':
154961f28255Scgd 		nerrs++;
155061f28255Scgd 		if (*s != '\n') {
155161f28255Scgd 			if (!iamremote) {
155261f28255Scgd 				fflush(stdout);
155361f28255Scgd 				(void) write(2, s, cp - s);
155461f28255Scgd 			}
155561f28255Scgd 			if (lfp != NULL)
155661f28255Scgd 				(void) fwrite(s, 1, cp - s, lfp);
155761f28255Scgd 		}
155861f28255Scgd 		if (resp[0] == '\2')
1559a5bfdf78Scgd 			lostconn(0);
156061f28255Scgd 		return(-1);
156161f28255Scgd 	}
156261f28255Scgd }
156361f28255Scgd 
156461f28255Scgd /*
156561f28255Scgd  * Remove temporary files and do any cleanup operations before exiting.
156661f28255Scgd  */
156761f28255Scgd void
15687d2ef9d9Schristos /*ARGSUSED*/
cleanup(int signo __unused)15697d2ef9d9Schristos cleanup(int signo __unused)
157061f28255Scgd {
157161f28255Scgd 	(void) unlink(tempfile);
157261f28255Scgd 	exit(1);
157361f28255Scgd }
157461f28255Scgd 
1575a5bfdf78Scgd static void
note(const char * fmt,...)1576a5bfdf78Scgd note(const char *fmt, ...)
157761f28255Scgd {
15784538623fSlukem 	static char nbuf[BUFSIZ];
1579a5bfdf78Scgd 	va_list ap;
1580a9b4ca62Swiz 
1581a5bfdf78Scgd 	va_start(ap, fmt);
15824538623fSlukem 	(void)vsnprintf(nbuf, sizeof(nbuf), fmt, ap);
1583a5bfdf78Scgd 	va_end(ap);
15844538623fSlukem 	comment(nbuf);
158561f28255Scgd }
158661f28255Scgd 
1587a5bfdf78Scgd static void
comment(char * s)1588a9b4ca62Swiz comment(char *s)
158961f28255Scgd {
1590a5bfdf78Scgd 	char c;
1591a5bfdf78Scgd 
1592a5bfdf78Scgd 	c = '\3';
159361f28255Scgd 	write(rem, &c, 1);
159461f28255Scgd 	write(rem, s, strlen(s));
159561f28255Scgd 	c = '\n';
159661f28255Scgd 	write(rem, &c, 1);
159761f28255Scgd }
1598