xref: /original-bsd/usr.bin/rdist/server.c (revision bdfadfff)
1babdf84bSdist /*
2babdf84bSdist  * Copyright (c) 1983 Regents of the University of California.
37a864411Sbostic  * All rights reserved.
47a864411Sbostic  *
54ec22e22Sbostic  * %sccs.include.redist.c%
6babdf84bSdist  */
7babdf84bSdist 
8978e44c9Sralph #ifndef lint
9*bdfadfffSbostic static char sccsid[] = "@(#)server.c	5.17 (Berkeley) 07/06/92";
107a864411Sbostic #endif /* not lint */
11978e44c9Sralph 
12*bdfadfffSbostic #include <sys/wait.h>
13978e44c9Sralph #include "defs.h"
14978e44c9Sralph 
156d401060Sralph #define	ack() 	(void) write(rem, "\0\n", 2)
166d401060Sralph #define	err() 	(void) write(rem, "\1\n", 2)
17978e44c9Sralph 
182cb25675Sralph struct	linkbuf *ihead;		/* list of files with more than one link */
19d49851feSralph char	buf[BUFSIZ];		/* general purpose buffer */
20978e44c9Sralph char	target[BUFSIZ];		/* target/source directory name */
21978e44c9Sralph char	*tp;			/* pointer to end of target name */
2226ce117eSsklower char	*Tdest;			/* pointer to last T dest*/
23978e44c9Sralph int	catname;		/* cat name to target name */
2465a0d230Sralph char	*stp[32];		/* stack of saved tp's for directories */
2501de96ecSralph int	oumask;			/* old umask for creating files */
26d49851feSralph 
27d49851feSralph extern	FILE *lfp;		/* log file for mailing changes */
28d49851feSralph 
29*bdfadfffSbostic static int	chkparent __P((char *));
30*bdfadfffSbostic static void	clean __P((char *));
31*bdfadfffSbostic static void	comment __P((char *));
32*bdfadfffSbostic static void	dospecial __P((char *));
33*bdfadfffSbostic static int	fchog __P((int, char *, char *, char *, int));
34*bdfadfffSbostic static void	hardlink __P((char *));
35*bdfadfffSbostic static void	note __P((const char *, ...));
36*bdfadfffSbostic static void	query __P((char *));
37*bdfadfffSbostic static void	recvf __P((char *, int));
38*bdfadfffSbostic static void	removeit __P((struct stat *));
39*bdfadfffSbostic static int	response __P((void));
40*bdfadfffSbostic static void	rmchk __P((int));
41*bdfadfffSbostic static struct linkbuf *
42*bdfadfffSbostic 		    savelink __P((struct stat *));
43*bdfadfffSbostic static void	sendf __P((char *, int));
44*bdfadfffSbostic static int	update __P((char *, int, struct stat *));
45aca7acf4Sralph 
46978e44c9Sralph /*
47978e44c9Sralph  * Server routine to read requests and process them.
48978e44c9Sralph  * Commands are:
49978e44c9Sralph  *	Tname	- Transmit file if out of date
50978e44c9Sralph  *	Vname	- Verify if file out of date or not
51978e44c9Sralph  *	Qname	- Query if file exists. Return mtime & size if it does.
52978e44c9Sralph  */
53*bdfadfffSbostic void
54978e44c9Sralph server()
55978e44c9Sralph {
56978e44c9Sralph 	char cmdbuf[BUFSIZ];
57978e44c9Sralph 	register char *cp;
58978e44c9Sralph 
59aca7acf4Sralph 	signal(SIGHUP, cleanup);
60aca7acf4Sralph 	signal(SIGINT, cleanup);
61aca7acf4Sralph 	signal(SIGQUIT, cleanup);
62aca7acf4Sralph 	signal(SIGTERM, cleanup);
63aca7acf4Sralph 	signal(SIGPIPE, cleanup);
64aca7acf4Sralph 
65aca7acf4Sralph 	rem = 0;
6601de96ecSralph 	oumask = umask(0);
6736a9f5b1Sralph 	(void) sprintf(buf, "V%d\n", VERSION);
6836a9f5b1Sralph 	(void) write(rem, buf, strlen(buf));
69978e44c9Sralph 
70978e44c9Sralph 	for (;;) {
71978e44c9Sralph 		cp = cmdbuf;
72978e44c9Sralph 		if (read(rem, cp, 1) <= 0)
73978e44c9Sralph 			return;
74978e44c9Sralph 		if (*cp++ == '\n') {
7501de96ecSralph 			error("server: expected control record\n");
76978e44c9Sralph 			continue;
77978e44c9Sralph 		}
78978e44c9Sralph 		do {
79978e44c9Sralph 			if (read(rem, cp, 1) != 1)
80*bdfadfffSbostic 				cleanup(0);
81d49851feSralph 		} while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
82978e44c9Sralph 		*--cp = '\0';
83978e44c9Sralph 		cp = cmdbuf;
84978e44c9Sralph 		switch (*cp++) {
85978e44c9Sralph 		case 'T':  /* init target file/directory name */
86d49851feSralph 			catname = 1;	/* target should be directory */
87d49851feSralph 			goto dotarget;
88d49851feSralph 
89d49851feSralph 		case 't':  /* init target file/directory name */
90978e44c9Sralph 			catname = 0;
91d49851feSralph 		dotarget:
9201de96ecSralph 			if (exptilde(target, cp) == NULL)
9301de96ecSralph 				continue;
94978e44c9Sralph 			tp = target;
95978e44c9Sralph 			while (*tp)
96978e44c9Sralph 				tp++;
976d401060Sralph 			ack();
98978e44c9Sralph 			continue;
99978e44c9Sralph 
1002cb25675Sralph 		case 'R':  /* Transfer a regular file. */
1012cb25675Sralph 			recvf(cp, S_IFREG);
102978e44c9Sralph 			continue;
103978e44c9Sralph 
1042cb25675Sralph 		case 'D':  /* Transfer a directory. */
1052cb25675Sralph 			recvf(cp, S_IFDIR);
1062cb25675Sralph 			continue;
1072cb25675Sralph 
1082cb25675Sralph 		case 'K':  /* Transfer symbolic link. */
1092cb25675Sralph 			recvf(cp, S_IFLNK);
1102cb25675Sralph 			continue;
1112cb25675Sralph 
1122cb25675Sralph 		case 'k':  /* Transfer hard link. */
1132cb25675Sralph 			hardlink(cp);
114978e44c9Sralph 			continue;
115978e44c9Sralph 
116978e44c9Sralph 		case 'E':  /* End. (of directory) */
117978e44c9Sralph 			*tp = '\0';
11801de96ecSralph 			if (catname <= 0) {
11901de96ecSralph 				error("server: too many 'E's\n");
120978e44c9Sralph 				continue;
121978e44c9Sralph 			}
12201de96ecSralph 			tp = stp[--catname];
12365a0d230Sralph 			*tp = '\0';
1246d401060Sralph 			ack();
125978e44c9Sralph 			continue;
126978e44c9Sralph 
127c27256c0Sralph 		case 'C':  /* Clean. Cleanup a directory */
1286d401060Sralph 			clean(cp);
129c27256c0Sralph 			continue;
130c27256c0Sralph 
13101de96ecSralph 		case 'Q':  /* Query. Does the file/directory exist? */
13201de96ecSralph 			query(cp);
13365a0d230Sralph 			continue;
13465a0d230Sralph 
1356d401060Sralph 		case 'S':  /* Special. Execute commands */
1366d401060Sralph 			dospecial(cp);
1376d401060Sralph 			continue;
1386d401060Sralph 
13901de96ecSralph #ifdef notdef
14001de96ecSralph 		/*
14101de96ecSralph 		 * These entries are reserved but not currently used.
14201de96ecSralph 		 * The intent is to allow remote hosts to have master copies.
14301de96ecSralph 		 * Currently, only the host rdist runs on can have masters.
14401de96ecSralph 		 */
14501de96ecSralph 		case 'X':  /* start a new list of files to exclude */
14601de96ecSralph 			except = bp = NULL;
14701de96ecSralph 		case 'x':  /* add name to list of files to exclude */
14801de96ecSralph 			if (*cp == '\0') {
1496d401060Sralph 				ack();
15001de96ecSralph 				continue;
15101de96ecSralph 			}
15201de96ecSralph 			if (*cp == '~') {
15301de96ecSralph 				if (exptilde(buf, cp) == NULL)
15401de96ecSralph 					continue;
15501de96ecSralph 				cp = buf;
15601de96ecSralph 			}
15701de96ecSralph 			if (bp == NULL)
1586d401060Sralph 				except = bp = expand(makeblock(NAME, cp), E_VARS);
15901de96ecSralph 			else
1606d401060Sralph 				bp->b_next = expand(makeblock(NAME, cp), E_VARS);
16101de96ecSralph 			while (bp->b_next != NULL)
16201de96ecSralph 				bp = bp->b_next;
1636d401060Sralph 			ack();
16401de96ecSralph 			continue;
16501de96ecSralph 
1666d401060Sralph 		case 'I':  /* Install. Transfer file if out of date. */
16701de96ecSralph 			opts = 0;
16801de96ecSralph 			while (*cp >= '0' && *cp <= '7')
16901de96ecSralph 				opts = (opts << 3) | (*cp++ - '0');
17001de96ecSralph 			if (*cp++ != ' ') {
17101de96ecSralph 				error("server: options not delimited\n");
17201de96ecSralph 				return;
17301de96ecSralph 			}
1746d401060Sralph 			install(cp, opts);
175978e44c9Sralph 			continue;
176978e44c9Sralph 
177978e44c9Sralph 		case 'L':  /* Log. save message in log file */
17865a0d230Sralph 			log(lfp, cp);
179978e44c9Sralph 			continue;
18001de96ecSralph #endif
181978e44c9Sralph 
182d49851feSralph 		case '\1':
183aca7acf4Sralph 			nerrs++;
184d49851feSralph 			continue;
185d49851feSralph 
186d49851feSralph 		case '\2':
187d49851feSralph 			return;
188d49851feSralph 
189978e44c9Sralph 		default:
19001de96ecSralph 			error("server: unknown command '%s'\n", cp);
191978e44c9Sralph 		case '\0':
192978e44c9Sralph 			continue;
193978e44c9Sralph 		}
194978e44c9Sralph 	}
195978e44c9Sralph }
196978e44c9Sralph 
197978e44c9Sralph /*
19801de96ecSralph  * Update the file(s) if they are different.
19901de96ecSralph  * destdir = 1 if destination should be a directory
20001de96ecSralph  * (i.e., more than one source is being copied to the same destination).
201978e44c9Sralph  */
202*bdfadfffSbostic void
20301de96ecSralph install(src, dest, destdir, opts)
20401de96ecSralph 	char *src, *dest;
20501de96ecSralph 	int destdir, opts;
20601de96ecSralph {
20701de96ecSralph 	char *rname;
20826ce117eSsklower 	char destcopy[BUFSIZ];
20901de96ecSralph 
21001de96ecSralph 	if (dest == NULL) {
21101de96ecSralph 		opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
21201de96ecSralph 		dest = src;
21301de96ecSralph 	}
21401de96ecSralph 
21501de96ecSralph 	if (nflag || debug) {
21601de96ecSralph 		printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
21701de96ecSralph 			opts & WHOLE ? " -w" : "",
21801de96ecSralph 			opts & YOUNGER ? " -y" : "",
21901de96ecSralph 			opts & COMPARE ? " -b" : "",
2206d401060Sralph 			opts & REMOVE ? " -R" : "", src, dest);
22101de96ecSralph 		if (nflag)
22201de96ecSralph 			return;
22301de96ecSralph 	}
22401de96ecSralph 
22501de96ecSralph 	rname = exptilde(target, src);
22601de96ecSralph 	if (rname == NULL)
22701de96ecSralph 		return;
22801de96ecSralph 	tp = target;
22901de96ecSralph 	while (*tp)
23001de96ecSralph 		tp++;
23101de96ecSralph 	/*
23201de96ecSralph 	 * If we are renaming a directory and we want to preserve
2336d401060Sralph 	 * the directory heirarchy (-w), we must strip off the leading
23401de96ecSralph 	 * directory name and preserve the rest.
23501de96ecSralph 	 */
23601de96ecSralph 	if (opts & WHOLE) {
2376d401060Sralph 		while (*rname == '/')
23801de96ecSralph 			rname++;
2396d401060Sralph 		destdir = 1;
24001de96ecSralph 	} else {
24101de96ecSralph 		rname = rindex(target, '/');
24201de96ecSralph 		if (rname == NULL)
24301de96ecSralph 			rname = target;
24401de96ecSralph 		else
24501de96ecSralph 			rname++;
24601de96ecSralph 	}
24701de96ecSralph 	if (debug)
24801de96ecSralph 		printf("target = %s, rname = %s\n", target, rname);
24901de96ecSralph 	/*
25001de96ecSralph 	 * Pass the destination file/directory name to remote.
25101de96ecSralph 	 */
25201de96ecSralph 	(void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
25301de96ecSralph 	if (debug)
25401de96ecSralph 		printf("buf = %s", buf);
25501de96ecSralph 	(void) write(rem, buf, strlen(buf));
25601de96ecSralph 	if (response() < 0)
25701de96ecSralph 		return;
25801de96ecSralph 
25926ce117eSsklower 	if (destdir) {
26026ce117eSsklower 		strcpy(destcopy, dest);
26126ce117eSsklower 		Tdest = destcopy;
26226ce117eSsklower 	}
26301de96ecSralph 	sendf(rname, opts);
26426ce117eSsklower 	Tdest = 0;
26501de96ecSralph }
26601de96ecSralph 
26726ce117eSsklower #define protoname() (pw ? pw->pw_name : user)
26826ce117eSsklower #define protogroup() (gr ? gr->gr_name : group)
26901de96ecSralph /*
27001de96ecSralph  * Transfer the file or directory in target[].
27101de96ecSralph  * rname is the name of the file on the remote host.
27201de96ecSralph  */
273*bdfadfffSbostic static void
27401de96ecSralph sendf(rname, opts)
27501de96ecSralph 	char *rname;
276ad3d87d9Sralph 	int opts;
277978e44c9Sralph {
278aca7acf4Sralph 	register struct subcmd *sc;
279978e44c9Sralph 	struct stat stb;
2802cb25675Sralph 	int sizerr, f, u, len;
281978e44c9Sralph 	off_t i;
2822cb25675Sralph 	DIR *d;
2832cb25675Sralph 	struct direct *dp;
2842cb25675Sralph 	char *otp, *cp;
28558fc31f7Sralph 	extern struct subcmd *subcmds;
28626ce117eSsklower 	static char user[15], group[15];
287978e44c9Sralph 
288978e44c9Sralph 	if (debug)
28901de96ecSralph 		printf("sendf(%s, %x)\n", rname, opts);
290978e44c9Sralph 
29158fc31f7Sralph 	if (except(target))
29265a0d230Sralph 		return;
293e980046fSralph 	if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
29490bde559Sbostic 		error("%s: %s\n", target, strerror(errno));
295978e44c9Sralph 		return;
296978e44c9Sralph 	}
2972cb25675Sralph 	if ((u = update(rname, opts, &stb)) == 0) {
2982cb25675Sralph 		if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
2992cb25675Sralph 			(void) savelink(&stb);
300978e44c9Sralph 		return;
3012cb25675Sralph 	}
302978e44c9Sralph 
30301de96ecSralph 	if (pw == NULL || pw->pw_uid != stb.st_uid)
30401de96ecSralph 		if ((pw = getpwuid(stb.st_uid)) == NULL) {
3056dfcab80Ssklower 			log(lfp, "%s: no password entry for uid %d \n",
3066dfcab80Ssklower 				target, stb.st_uid);
30726ce117eSsklower 			pw = NULL;
308*bdfadfffSbostic 			(void)sprintf(user, ":%lu", stb.st_uid);
309978e44c9Sralph 		}
31001de96ecSralph 	if (gr == NULL || gr->gr_gid != stb.st_gid)
31101de96ecSralph 		if ((gr = getgrgid(stb.st_gid)) == NULL) {
3126dfcab80Ssklower 			log(lfp, "%s: no name for group %d\n",
3136dfcab80Ssklower 				target, stb.st_gid);
31426ce117eSsklower 			gr = NULL;
315*bdfadfffSbostic 			(void)sprintf(group, ":%lu", stb.st_gid);
316978e44c9Sralph 		}
317156664fbSralph 	if (u == 1) {
3186d401060Sralph 		if (opts & VERIFY) {
3196d401060Sralph 			log(lfp, "need to install: %s\n", target);
3206d401060Sralph 			goto dospecial;
3216d401060Sralph 		}
32201de96ecSralph 		log(lfp, "installing: %s\n", target);
3236d401060Sralph 		opts &= ~(COMPARE|REMOVE);
324156664fbSralph 	}
325978e44c9Sralph 
326978e44c9Sralph 	switch (stb.st_mode & S_IFMT) {
3272cb25675Sralph 	case S_IFDIR:
3282cb25675Sralph 		if ((d = opendir(target)) == NULL) {
32990bde559Sbostic 			error("%s: %s\n", target, strerror(errno));
3302cb25675Sralph 			return;
3312cb25675Sralph 		}
3322cb25675Sralph 		(void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
33326ce117eSsklower 			stb.st_mode & 07777, protoname(), protogroup(), rname);
3342cb25675Sralph 		if (debug)
33515867ad7Sralph 			printf("buf = %s", buf);
3362cb25675Sralph 		(void) write(rem, buf, strlen(buf));
3372cb25675Sralph 		if (response() < 0) {
3382cb25675Sralph 			closedir(d);
3392cb25675Sralph 			return;
3402cb25675Sralph 		}
3412cb25675Sralph 
3422cb25675Sralph 		if (opts & REMOVE)
3432cb25675Sralph 			rmchk(opts);
3442cb25675Sralph 
3452cb25675Sralph 		otp = tp;
3462cb25675Sralph 		len = tp - target;
3472cb25675Sralph 		while (dp = readdir(d)) {
3482cb25675Sralph 			if (!strcmp(dp->d_name, ".") ||
3492cb25675Sralph 			    !strcmp(dp->d_name, ".."))
3502cb25675Sralph 				continue;
3512cb25675Sralph 			if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
3522cb25675Sralph 				error("%s/%s: Name too long\n", target,
3532cb25675Sralph 					dp->d_name);
3542cb25675Sralph 				continue;
3552cb25675Sralph 			}
3562cb25675Sralph 			tp = otp;
3572cb25675Sralph 			*tp++ = '/';
3582cb25675Sralph 			cp = dp->d_name;
3592cb25675Sralph 			while (*tp++ = *cp++)
3602cb25675Sralph 				;
3612cb25675Sralph 			tp--;
3622cb25675Sralph 			sendf(dp->d_name, opts);
3632cb25675Sralph 		}
3642cb25675Sralph 		closedir(d);
3652cb25675Sralph 		(void) write(rem, "E\n", 2);
3662cb25675Sralph 		(void) response();
3672cb25675Sralph 		tp = otp;
3682cb25675Sralph 		*tp = '\0';
3692cb25675Sralph 		return;
3702cb25675Sralph 
3712cb25675Sralph 	case S_IFLNK:
3722cb25675Sralph 		if (u != 1)
3732cb25675Sralph 			opts |= COMPARE;
37426ce117eSsklower 		if (stb.st_nlink > 1) {
37526ce117eSsklower 			struct linkbuf *lp;
37626ce117eSsklower 
37726ce117eSsklower 			if ((lp = savelink(&stb)) != NULL) {
37826ce117eSsklower 				/* install link */
37926ce117eSsklower 				if (*lp->target == 0)
38026ce117eSsklower 				(void) sprintf(buf, "k%o %s %s\n", opts,
38126ce117eSsklower 					lp->pathname, rname);
38226ce117eSsklower 				else
38326ce117eSsklower 				(void) sprintf(buf, "k%o %s/%s %s\n", opts,
38426ce117eSsklower 					lp->target, lp->pathname, rname);
38526ce117eSsklower 				if (debug)
38626ce117eSsklower 					printf("buf = %s", buf);
38726ce117eSsklower 				(void) write(rem, buf, strlen(buf));
38826ce117eSsklower 				(void) response();
38926ce117eSsklower 				return;
39026ce117eSsklower 			}
39126ce117eSsklower 		}
392*bdfadfffSbostic 		(void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts,
3932cb25675Sralph 			stb.st_mode & 07777, stb.st_size, stb.st_mtime,
39426ce117eSsklower 			protoname(), protogroup(), rname);
3952cb25675Sralph 		if (debug)
3962cb25675Sralph 			printf("buf = %s", buf);
3972cb25675Sralph 		(void) write(rem, buf, strlen(buf));
3982cb25675Sralph 		if (response() < 0)
3992cb25675Sralph 			return;
4002cb25675Sralph 		sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
4012cb25675Sralph 		(void) write(rem, buf, stb.st_size);
4022cb25675Sralph 		if (debug)
403e157bfacSbostic 			printf("readlink = %.*s\n", (int)stb.st_size, buf);
4042cb25675Sralph 		goto done;
4052cb25675Sralph 
406978e44c9Sralph 	case S_IFREG:
407978e44c9Sralph 		break;
408978e44c9Sralph 
409978e44c9Sralph 	default:
4102cb25675Sralph 		error("%s: not a file or directory\n", target);
411978e44c9Sralph 		return;
412978e44c9Sralph 	}
413978e44c9Sralph 
414156664fbSralph 	if (u == 2) {
4156d401060Sralph 		if (opts & VERIFY) {
4166d401060Sralph 			log(lfp, "need to update: %s\n", target);
4176d401060Sralph 			goto dospecial;
418156664fbSralph 		}
4196d401060Sralph 		log(lfp, "updating: %s\n", target);
4206d401060Sralph 	}
4212cb25675Sralph 
4222cb25675Sralph 	if (stb.st_nlink > 1) {
4232cb25675Sralph 		struct linkbuf *lp;
4242cb25675Sralph 
4252cb25675Sralph 		if ((lp = savelink(&stb)) != NULL) {
4262cb25675Sralph 			/* install link */
42726ce117eSsklower 			if (*lp->target == 0)
4282cb25675Sralph 			(void) sprintf(buf, "k%o %s %s\n", opts,
4292cb25675Sralph 				lp->pathname, rname);
43026ce117eSsklower 			else
43126ce117eSsklower 			(void) sprintf(buf, "k%o %s/%s %s\n", opts,
43226ce117eSsklower 				lp->target, lp->pathname, rname);
4332cb25675Sralph 			if (debug)
4342cb25675Sralph 				printf("buf = %s", buf);
4352cb25675Sralph 			(void) write(rem, buf, strlen(buf));
4362cb25675Sralph 			(void) response();
4372cb25675Sralph 			return;
4382cb25675Sralph 		}
4392cb25675Sralph 	}
440978e44c9Sralph 
441*bdfadfffSbostic 	if ((f = open(target, O_RDONLY, 0)) < 0) {
44290bde559Sbostic 		error("%s: %s\n", target, strerror(errno));
443d49851feSralph 		return;
444d49851feSralph 	}
445*bdfadfffSbostic 	(void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts,
446ad3d87d9Sralph 		stb.st_mode & 07777, stb.st_size, stb.st_mtime,
44726ce117eSsklower 		protoname(), protogroup(), rname);
448978e44c9Sralph 	if (debug)
449978e44c9Sralph 		printf("buf = %s", buf);
450978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
451978e44c9Sralph 	if (response() < 0) {
452978e44c9Sralph 		(void) close(f);
453978e44c9Sralph 		return;
454978e44c9Sralph 	}
455978e44c9Sralph 	sizerr = 0;
456978e44c9Sralph 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
457978e44c9Sralph 		int amt = BUFSIZ;
458978e44c9Sralph 		if (i + amt > stb.st_size)
459978e44c9Sralph 			amt = stb.st_size - i;
460978e44c9Sralph 		if (sizerr == 0 && read(f, buf, amt) != amt)
461978e44c9Sralph 			sizerr = 1;
462978e44c9Sralph 		(void) write(rem, buf, amt);
463978e44c9Sralph 	}
464978e44c9Sralph 	(void) close(f);
4652cb25675Sralph done:
4666d401060Sralph 	if (sizerr) {
46701de96ecSralph 		error("%s: file changed size\n", target);
4686d401060Sralph 		err();
4696d401060Sralph 	} else
4706d401060Sralph 		ack();
4712cb25675Sralph 	f = response();
4722cb25675Sralph 	if (f < 0 || f == 0 && (opts & COMPARE))
4736d401060Sralph 		return;
4746d401060Sralph dospecial:
47558fc31f7Sralph 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
476aca7acf4Sralph 		if (sc->sc_type != SPECIAL)
4776d401060Sralph 			continue;
478e980046fSralph 		if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
4796d401060Sralph 			continue;
480aca7acf4Sralph 		log(lfp, "special \"%s\"\n", sc->sc_name);
4816d401060Sralph 		if (opts & VERIFY)
4826d401060Sralph 			continue;
483e980046fSralph 		(void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
4846d401060Sralph 		if (debug)
4856d401060Sralph 			printf("buf = %s", buf);
4866d401060Sralph 		(void) write(rem, buf, strlen(buf));
4876d401060Sralph 		while (response() > 0)
4886d401060Sralph 			;
4896d401060Sralph 	}
490978e44c9Sralph }
491978e44c9Sralph 
492*bdfadfffSbostic static struct linkbuf *
4932cb25675Sralph savelink(stp)
4942cb25675Sralph 	struct stat *stp;
495978e44c9Sralph {
4962cb25675Sralph 	struct linkbuf *lp;
497978e44c9Sralph 
4982cb25675Sralph 	for (lp = ihead; lp != NULL; lp = lp->nextp)
4992cb25675Sralph 		if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
5002cb25675Sralph 			lp->count--;
5012cb25675Sralph 			return(lp);
502978e44c9Sralph 		}
5032cb25675Sralph 	lp = (struct linkbuf *) malloc(sizeof(*lp));
5042cb25675Sralph 	if (lp == NULL)
5052cb25675Sralph 		log(lfp, "out of memory, link information lost\n");
5062cb25675Sralph 	else {
5072cb25675Sralph 		lp->nextp = ihead;
5082cb25675Sralph 		ihead = lp;
5092cb25675Sralph 		lp->inum = stp->st_ino;
5102cb25675Sralph 		lp->devnum = stp->st_dev;
5112cb25675Sralph 		lp->count = stp->st_nlink - 1;
5122cb25675Sralph 		strcpy(lp->pathname, target);
51326ce117eSsklower 		if (Tdest)
51426ce117eSsklower 			strcpy(lp->target, Tdest);
51526ce117eSsklower 		else
51626ce117eSsklower 			*lp->target = 0;
517978e44c9Sralph 	}
5182cb25675Sralph 	return(NULL);
519978e44c9Sralph }
520978e44c9Sralph 
521978e44c9Sralph /*
522978e44c9Sralph  * Check to see if file needs to be updated on the remote machine.
52301de96ecSralph  * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
52401de96ecSralph  * and 3 if comparing binaries to determine if out of date.
525978e44c9Sralph  */
526*bdfadfffSbostic static int
5272cb25675Sralph update(rname, opts, stp)
52801de96ecSralph 	char *rname;
529ad3d87d9Sralph 	int opts;
5302cb25675Sralph 	struct stat *stp;
531978e44c9Sralph {
53201de96ecSralph 	register char *cp, *s;
533978e44c9Sralph 	register off_t size;
534978e44c9Sralph 	register time_t mtime;
535978e44c9Sralph 
536978e44c9Sralph 	if (debug)
5372cb25675Sralph 		printf("update(%s, %x, %x)\n", rname, opts, stp);
538978e44c9Sralph 
539978e44c9Sralph 	/*
540978e44c9Sralph 	 * Check to see if the file exists on the remote machine.
541978e44c9Sralph 	 */
54201de96ecSralph 	(void) sprintf(buf, "Q%s\n", rname);
543978e44c9Sralph 	if (debug)
544978e44c9Sralph 		printf("buf = %s", buf);
545978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
54626ce117eSsklower again:
54701de96ecSralph 	cp = s = buf;
548978e44c9Sralph 	do {
549978e44c9Sralph 		if (read(rem, cp, 1) != 1)
550*bdfadfffSbostic 			lostconn(0);
551d49851feSralph 	} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
552978e44c9Sralph 
55301de96ecSralph 	switch (*s++) {
554978e44c9Sralph 	case 'Y':
555978e44c9Sralph 		break;
556978e44c9Sralph 
557d49851feSralph 	case 'N':  /* file doesn't exist so install it */
558978e44c9Sralph 		return(1);
559978e44c9Sralph 
560978e44c9Sralph 	case '\1':
561aca7acf4Sralph 		nerrs++;
56201de96ecSralph 		if (*s != '\n') {
563d49851feSralph 			if (!iamremote) {
564d49851feSralph 				fflush(stdout);
56501de96ecSralph 				(void) write(2, s, cp - s);
566d49851feSralph 			}
567d49851feSralph 			if (lfp != NULL)
56801de96ecSralph 				(void) fwrite(s, 1, cp - s, lfp);
569d49851feSralph 		}
570978e44c9Sralph 		return(0);
571978e44c9Sralph 
57226ce117eSsklower 	case '\3':
57326ce117eSsklower 		*--cp = '\0';
57426ce117eSsklower 		if (lfp != NULL)
57526ce117eSsklower 			log(lfp, "update: note: %s\n", s);
57626ce117eSsklower 		goto again;
57726ce117eSsklower 
578978e44c9Sralph 	default:
57915867ad7Sralph 		*--cp = '\0';
58026ce117eSsklower 		error("update: unexpected response '%s'\n", s);
581978e44c9Sralph 		return(0);
582978e44c9Sralph 	}
583978e44c9Sralph 
58401de96ecSralph 	if (*s == '\n')
585d49851feSralph 		return(2);
586978e44c9Sralph 
587156664fbSralph 	if (opts & COMPARE)
588156664fbSralph 		return(3);
589156664fbSralph 
590978e44c9Sralph 	size = 0;
59101de96ecSralph 	while (isdigit(*s))
59201de96ecSralph 		size = size * 10 + (*s++ - '0');
59301de96ecSralph 	if (*s++ != ' ') {
59401de96ecSralph 		error("update: size not delimited\n");
595978e44c9Sralph 		return(0);
596978e44c9Sralph 	}
597978e44c9Sralph 	mtime = 0;
59801de96ecSralph 	while (isdigit(*s))
59901de96ecSralph 		mtime = mtime * 10 + (*s++ - '0');
60001de96ecSralph 	if (*s != '\n') {
60101de96ecSralph 		error("update: mtime not delimited\n");
602978e44c9Sralph 		return(0);
603978e44c9Sralph 	}
604978e44c9Sralph 	/*
605978e44c9Sralph 	 * File needs to be updated?
606978e44c9Sralph 	 */
607ad3d87d9Sralph 	if (opts & YOUNGER) {
6082cb25675Sralph 		if (stp->st_mtime == mtime)
60965a0d230Sralph 			return(0);
6102cb25675Sralph 		if (stp->st_mtime < mtime) {
61101de96ecSralph 			log(lfp, "Warning: %s: remote copy is newer\n", target);
61265a0d230Sralph 			return(0);
61365a0d230Sralph 		}
6142cb25675Sralph 	} else if (stp->st_mtime == mtime && stp->st_size == size)
615978e44c9Sralph 		return(0);
616d49851feSralph 	return(2);
617978e44c9Sralph }
618978e44c9Sralph 
619978e44c9Sralph /*
620978e44c9Sralph  * Query. Check to see if file exists. Return one of the following:
621978e44c9Sralph  *	N\n		- doesn't exist
622978e44c9Sralph  *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
6232cb25675Sralph  *	Y\n		- exists and its a directory or symbolic link
624978e44c9Sralph  *	^Aerror message\n
625978e44c9Sralph  */
626*bdfadfffSbostic static void
62701de96ecSralph query(name)
628978e44c9Sralph 	char *name;
629978e44c9Sralph {
630978e44c9Sralph 	struct stat stb;
631978e44c9Sralph 
632978e44c9Sralph 	if (catname)
633978e44c9Sralph 		(void) sprintf(tp, "/%s", name);
63465a0d230Sralph 
6352cb25675Sralph 	if (lstat(target, &stb) < 0) {
636e980046fSralph 		if (errno == ENOENT)
637978e44c9Sralph 			(void) write(rem, "N\n", 2);
638e980046fSralph 		else
63990bde559Sbostic 			error("%s:%s: %s\n", host, target, strerror(errno));
640978e44c9Sralph 		*tp = '\0';
641978e44c9Sralph 		return;
642978e44c9Sralph 	}
643978e44c9Sralph 
644978e44c9Sralph 	switch (stb.st_mode & S_IFMT) {
645978e44c9Sralph 	case S_IFREG:
646*bdfadfffSbostic 		(void) sprintf(buf, "Y%qd %ld\n", stb.st_size, stb.st_mtime);
647978e44c9Sralph 		(void) write(rem, buf, strlen(buf));
648978e44c9Sralph 		break;
649978e44c9Sralph 
6502cb25675Sralph 	case S_IFLNK:
651978e44c9Sralph 	case S_IFDIR:
652978e44c9Sralph 		(void) write(rem, "Y\n", 2);
653978e44c9Sralph 		break;
654978e44c9Sralph 
655978e44c9Sralph 	default:
6562cb25675Sralph 		error("%s: not a file or directory\n", name);
657978e44c9Sralph 		break;
658978e44c9Sralph 	}
659978e44c9Sralph 	*tp = '\0';
660978e44c9Sralph }
661978e44c9Sralph 
662*bdfadfffSbostic static void
6632cb25675Sralph recvf(cmd, type)
664978e44c9Sralph 	char *cmd;
6652cb25675Sralph 	int type;
666978e44c9Sralph {
667978e44c9Sralph 	register char *cp;
66844f1d1c2Sralph 	int f, mode, opts, wrerr, olderrno;
669978e44c9Sralph 	off_t i, size;
670978e44c9Sralph 	time_t mtime;
671978e44c9Sralph 	struct stat stb;
672978e44c9Sralph 	struct timeval tvp[2];
673e980046fSralph 	char *owner, *group;
674978e44c9Sralph 	char new[BUFSIZ];
67516580127Seric 	extern char *tempname;
676978e44c9Sralph 
677ad3d87d9Sralph 	cp = cmd;
678156664fbSralph 	opts = 0;
679156664fbSralph 	while (*cp >= '0' && *cp <= '7')
680156664fbSralph 		opts = (opts << 3) | (*cp++ - '0');
681ad3d87d9Sralph 	if (*cp++ != ' ') {
68201de96ecSralph 		error("recvf: options not delimited\n");
683ad3d87d9Sralph 		return;
684ad3d87d9Sralph 	}
685978e44c9Sralph 	mode = 0;
686156664fbSralph 	while (*cp >= '0' && *cp <= '7')
687ad3d87d9Sralph 		mode = (mode << 3) | (*cp++ - '0');
688978e44c9Sralph 	if (*cp++ != ' ') {
68901de96ecSralph 		error("recvf: mode not delimited\n");
690978e44c9Sralph 		return;
691978e44c9Sralph 	}
692978e44c9Sralph 	size = 0;
693978e44c9Sralph 	while (isdigit(*cp))
694978e44c9Sralph 		size = size * 10 + (*cp++ - '0');
695978e44c9Sralph 	if (*cp++ != ' ') {
69601de96ecSralph 		error("recvf: size not delimited\n");
697978e44c9Sralph 		return;
698978e44c9Sralph 	}
699978e44c9Sralph 	mtime = 0;
700978e44c9Sralph 	while (isdigit(*cp))
701978e44c9Sralph 		mtime = mtime * 10 + (*cp++ - '0');
702978e44c9Sralph 	if (*cp++ != ' ') {
70301de96ecSralph 		error("recvf: mtime not delimited\n");
704978e44c9Sralph 		return;
705978e44c9Sralph 	}
706978e44c9Sralph 	owner = cp;
707978e44c9Sralph 	while (*cp && *cp != ' ')
708978e44c9Sralph 		cp++;
709978e44c9Sralph 	if (*cp != ' ') {
71001de96ecSralph 		error("recvf: owner name not delimited\n");
711978e44c9Sralph 		return;
712978e44c9Sralph 	}
713978e44c9Sralph 	*cp++ = '\0';
714978e44c9Sralph 	group = cp;
715978e44c9Sralph 	while (*cp && *cp != ' ')
716978e44c9Sralph 		cp++;
717978e44c9Sralph 	if (*cp != ' ') {
71801de96ecSralph 		error("recvf: group name not delimited\n");
719978e44c9Sralph 		return;
720978e44c9Sralph 	}
721978e44c9Sralph 	*cp++ = '\0';
722978e44c9Sralph 
7232cb25675Sralph 	if (type == S_IFDIR) {
72465a0d230Sralph 		if (catname >= sizeof(stp)) {
7252cb25675Sralph 			error("%s:%s: too many directory levels\n",
7262cb25675Sralph 				host, target);
72765a0d230Sralph 			return;
72865a0d230Sralph 		}
72965a0d230Sralph 		stp[catname] = tp;
730978e44c9Sralph 		if (catname++) {
731978e44c9Sralph 			*tp++ = '/';
732978e44c9Sralph 			while (*tp++ = *cp++)
733978e44c9Sralph 				;
734978e44c9Sralph 			tp--;
735978e44c9Sralph 		}
736ad3d87d9Sralph 		if (opts & VERIFY) {
7376d401060Sralph 			ack();
738d49851feSralph 			return;
739d49851feSralph 		}
7402cb25675Sralph 		if (lstat(target, &stb) == 0) {
7416d401060Sralph 			if (ISDIR(stb.st_mode)) {
742e980046fSralph 				if ((stb.st_mode & 07777) == mode) {
7436d401060Sralph 					ack();
744978e44c9Sralph 					return;
745d49851feSralph 				}
7463e1cad88Sralph 				buf[0] = '\0';
7473e1cad88Sralph 				(void) sprintf(buf + 1,
748d66beb1aSlepreau 					"%s: Warning: remote mode %o != local mode %o\n",
749d66beb1aSlepreau 					target, stb.st_mode & 07777, mode);
7503e1cad88Sralph 				(void) write(rem, buf, strlen(buf + 1) + 1);
7513e1cad88Sralph 				return;
7523e1cad88Sralph 			}
753e980046fSralph 			errno = ENOTDIR;
754e980046fSralph 		} else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
755e980046fSralph 		    chkparent(target) == 0 && mkdir(target, mode) == 0)) {
7561b11f3a3Sbostic 			if (fchog(-1, target, owner, group, mode) == 0)
7576d401060Sralph 				ack();
7586d401060Sralph 			return;
7596d401060Sralph 		}
76090bde559Sbostic 		error("%s:%s: %s\n", host, target, strerror(errno));
7616d401060Sralph 		tp = stp[--catname];
7626d401060Sralph 		*tp = '\0';
763978e44c9Sralph 		return;
764978e44c9Sralph 	}
765978e44c9Sralph 
766978e44c9Sralph 	if (catname)
767978e44c9Sralph 		(void) sprintf(tp, "/%s", cp);
768978e44c9Sralph 	cp = rindex(target, '/');
769978e44c9Sralph 	if (cp == NULL)
77016580127Seric 		strcpy(new, tempname);
771e980046fSralph 	else if (cp == target)
77216580127Seric 		(void) sprintf(new, "/%s", tempname);
773e980046fSralph 	else {
774978e44c9Sralph 		*cp = '\0';
77516580127Seric 		(void) sprintf(new, "%s/%s", target, tempname);
776d49851feSralph 		*cp = '/';
777e980046fSralph 	}
7782cb25675Sralph 
7792cb25675Sralph 	if (type == S_IFLNK) {
7802cb25675Sralph 		int j;
7812cb25675Sralph 
7822cb25675Sralph 		ack();
7832cb25675Sralph 		cp = buf;
7842cb25675Sralph 		for (i = 0; i < size; i += j) {
7852cb25675Sralph 			if ((j = read(rem, cp, size - i)) <= 0)
786*bdfadfffSbostic 				cleanup(0);
7872cb25675Sralph 			cp += j;
7882cb25675Sralph 		}
7892cb25675Sralph 		*cp = '\0';
790e980046fSralph 		if (response() < 0) {
791e980046fSralph 			err();
792e980046fSralph 			return;
793e980046fSralph 		}
79475527c89Sralph 		if (symlink(buf, new) < 0) {
79575527c89Sralph 			if (errno != ENOENT || chkparent(new) < 0 ||
79675527c89Sralph 			    symlink(buf, new) < 0)
7971b11f3a3Sbostic 				goto badnew1;
79875527c89Sralph 		}
7992cb25675Sralph 		mode &= 0777;
8002cb25675Sralph 		if (opts & COMPARE) {
8012cb25675Sralph 			char tbuf[BUFSIZ];
8022cb25675Sralph 
80375527c89Sralph 			if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
80475527c89Sralph 			    i == size && strncmp(buf, tbuf, size) == 0) {
8052cb25675Sralph 				(void) unlink(new);
8062cb25675Sralph 				ack();
8072cb25675Sralph 				return;
8082cb25675Sralph 			}
80915867ad7Sralph 			if (opts & VERIFY)
81015867ad7Sralph 				goto differ;
8112cb25675Sralph 		}
8122cb25675Sralph 		goto fixup;
8132cb25675Sralph 	}
8142cb25675Sralph 
815e980046fSralph 	if ((f = creat(new, mode)) < 0) {
816e980046fSralph 		if (errno != ENOENT || chkparent(new) < 0 ||
817e980046fSralph 		    (f = creat(new, mode)) < 0)
8181b11f3a3Sbostic 			goto badnew1;
819e980046fSralph 	}
820e980046fSralph 
8216d401060Sralph 	ack();
822978e44c9Sralph 	wrerr = 0;
823978e44c9Sralph 	for (i = 0; i < size; i += BUFSIZ) {
824978e44c9Sralph 		int amt = BUFSIZ;
825978e44c9Sralph 
82665a0d230Sralph 		cp = buf;
827978e44c9Sralph 		if (i + amt > size)
828978e44c9Sralph 			amt = size - i;
829978e44c9Sralph 		do {
830978e44c9Sralph 			int j = read(rem, cp, amt);
831978e44c9Sralph 
832d49851feSralph 			if (j <= 0) {
833d49851feSralph 				(void) close(f);
834d49851feSralph 				(void) unlink(new);
835*bdfadfffSbostic 				cleanup(0);
836d49851feSralph 			}
837978e44c9Sralph 			amt -= j;
838978e44c9Sralph 			cp += j;
839978e44c9Sralph 		} while (amt > 0);
840978e44c9Sralph 		amt = BUFSIZ;
841978e44c9Sralph 		if (i + amt > size)
842978e44c9Sralph 			amt = size - i;
843978e44c9Sralph 		if (wrerr == 0 && write(f, buf, amt) != amt) {
844978e44c9Sralph 			olderrno = errno;
845978e44c9Sralph 			wrerr++;
846978e44c9Sralph 		}
847978e44c9Sralph 	}
848e980046fSralph 	if (response() < 0) {
849e980046fSralph 		err();
8501b11f3a3Sbostic 		goto badnew2;
851e980046fSralph 	}
8521b11f3a3Sbostic 	if (wrerr)
8531b11f3a3Sbostic 		goto badnew1;
854156664fbSralph 	if (opts & COMPARE) {
855156664fbSralph 		FILE *f1, *f2;
856156664fbSralph 		int c;
857156664fbSralph 
858156664fbSralph 		if ((f1 = fopen(target, "r")) == NULL)
8591b11f3a3Sbostic 			goto badtarget;
86026ce117eSsklower 		if ((f2 = fopen(new, "r")) == NULL) {
8611b11f3a3Sbostic badnew1:		error("%s:%s: %s\n", host, new, strerror(errno));
8621b11f3a3Sbostic 			goto badnew2;
86326ce117eSsklower 		}
864156664fbSralph 		while ((c = getc(f1)) == getc(f2))
865156664fbSralph 			if (c == EOF) {
866156664fbSralph 				(void) fclose(f1);
867156664fbSralph 				(void) fclose(f2);
8686d401060Sralph 				ack();
8691b11f3a3Sbostic 				goto badnew2;
870156664fbSralph 			}
871156664fbSralph 		(void) fclose(f1);
872156664fbSralph 		(void) fclose(f2);
873156664fbSralph 		if (opts & VERIFY) {
8741b11f3a3Sbostic differ:			buf[0] = '\0';
875e980046fSralph 			(void) sprintf(buf + 1, "need to update: %s\n",target);
876156664fbSralph 			(void) write(rem, buf, strlen(buf + 1) + 1);
8771b11f3a3Sbostic 			goto badnew2;
878156664fbSralph 		}
879156664fbSralph 	}
880978e44c9Sralph 
881978e44c9Sralph 	/*
882978e44c9Sralph 	 * Set last modified time
883978e44c9Sralph 	 */
8841b11f3a3Sbostic 	tvp[0].tv_sec = time(0);
885978e44c9Sralph 	tvp[0].tv_usec = 0;
886978e44c9Sralph 	tvp[1].tv_sec = mtime;
887978e44c9Sralph 	tvp[1].tv_usec = 0;
8881b11f3a3Sbostic 	if (utimes(new, tvp) < 0)
88990bde559Sbostic 		note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
8901b11f3a3Sbostic 
8911b11f3a3Sbostic 	if (fchog(f, new, owner, group, mode) < 0) {
8921b11f3a3Sbostic badnew2:	(void) close(f);
893d49851feSralph 		(void) unlink(new);
894978e44c9Sralph 		return;
895978e44c9Sralph 	}
8961b11f3a3Sbostic 	(void) close(f);
8971b11f3a3Sbostic 
8981b11f3a3Sbostic fixup:	if (rename(new, target) < 0) {
8991b11f3a3Sbostic badtarget:	error("%s:%s: %s\n", host, target, strerror(errno));
900d49851feSralph 		(void) unlink(new);
901978e44c9Sralph 		return;
902978e44c9Sralph 	}
9031b11f3a3Sbostic 
904156664fbSralph 	if (opts & COMPARE) {
905156664fbSralph 		buf[0] = '\0';
906d66beb1aSlepreau 		(void) sprintf(buf + 1, "updated %s\n", target);
907156664fbSralph 		(void) write(rem, buf, strlen(buf + 1) + 1);
908156664fbSralph 	} else
9096d401060Sralph 		ack();
910978e44c9Sralph }
911978e44c9Sralph 
912978e44c9Sralph /*
9132cb25675Sralph  * Creat a hard link to existing file.
9142cb25675Sralph  */
915*bdfadfffSbostic static void
9162cb25675Sralph hardlink(cmd)
9172cb25675Sralph 	char *cmd;
9182cb25675Sralph {
9192cb25675Sralph 	register char *cp;
9202cb25675Sralph 	struct stat stb;
9212cb25675Sralph 	char *oldname;
9222cb25675Sralph 	int opts, exists = 0;
9232cb25675Sralph 
9242cb25675Sralph 	cp = cmd;
9252cb25675Sralph 	opts = 0;
9262cb25675Sralph 	while (*cp >= '0' && *cp <= '7')
9272cb25675Sralph 		opts = (opts << 3) | (*cp++ - '0');
9282cb25675Sralph 	if (*cp++ != ' ') {
9292cb25675Sralph 		error("hardlink: options not delimited\n");
9302cb25675Sralph 		return;
9312cb25675Sralph 	}
9322cb25675Sralph 	oldname = cp;
9332cb25675Sralph 	while (*cp && *cp != ' ')
9342cb25675Sralph 		cp++;
9352cb25675Sralph 	if (*cp != ' ') {
9362cb25675Sralph 		error("hardlink: oldname name not delimited\n");
9372cb25675Sralph 		return;
9382cb25675Sralph 	}
9392cb25675Sralph 	*cp++ = '\0';
9402cb25675Sralph 
94126ce117eSsklower 	if (catname) {
9422cb25675Sralph 		(void) sprintf(tp, "/%s", cp);
94326ce117eSsklower 	}
9442cb25675Sralph 	if (lstat(target, &stb) == 0) {
94526ce117eSsklower 		int mode = stb.st_mode & S_IFMT;
94626ce117eSsklower 		if (mode != S_IFREG && mode != S_IFLNK) {
9472cb25675Sralph 			error("%s:%s: not a regular file\n", host, target);
9482cb25675Sralph 			return;
9492cb25675Sralph 		}
9502cb25675Sralph 		exists = 1;
9512cb25675Sralph 	}
95226ce117eSsklower 	if (chkparent(target) < 0 ) {
95326ce117eSsklower 		error("%s:%s: %s (no parent)\n",
95490bde559Sbostic 			host, target, strerror(errno));
95526ce117eSsklower 		return;
95626ce117eSsklower 	}
95726ce117eSsklower 	if (exists && (unlink(target) < 0)) {
95826ce117eSsklower 		error("%s:%s: %s (unlink)\n",
95990bde559Sbostic 			host, target, strerror(errno));
96026ce117eSsklower 		return;
96126ce117eSsklower 	}
96226ce117eSsklower 	if (link(oldname, target) < 0) {
96326ce117eSsklower 		error("%s:can't link %s to %s\n",
96426ce117eSsklower 			host, target, oldname);
9652cb25675Sralph 		return;
9662cb25675Sralph 	}
9672cb25675Sralph 	ack();
9682cb25675Sralph }
9692cb25675Sralph 
9702cb25675Sralph /*
971e980046fSralph  * Check to see if parent directory exists and create one if not.
97265a0d230Sralph  */
973*bdfadfffSbostic static int
97465a0d230Sralph chkparent(name)
97565a0d230Sralph 	char *name;
97665a0d230Sralph {
977e980046fSralph 	register char *cp;
978e980046fSralph 	struct stat stb;
97965a0d230Sralph 
98065a0d230Sralph 	cp = rindex(name, '/');
981e980046fSralph 	if (cp == NULL || cp == name)
982e980046fSralph 		return(0);
98365a0d230Sralph 	*cp = '\0';
984e980046fSralph 	if (lstat(name, &stb) < 0) {
985e980046fSralph 		if (errno == ENOENT && chkparent(name) >= 0 &&
986e980046fSralph 		    mkdir(name, 0777 & ~oumask) >= 0) {
98765a0d230Sralph 			*cp = '/';
98865a0d230Sralph 			return(0);
98965a0d230Sralph 		}
990e980046fSralph 	} else if (ISDIR(stb.st_mode)) {
99165a0d230Sralph 		*cp = '/';
99265a0d230Sralph 		return(0);
99365a0d230Sralph 	}
99465a0d230Sralph 	*cp = '/';
99565a0d230Sralph 	return(-1);
99665a0d230Sralph }
99765a0d230Sralph 
99865a0d230Sralph /*
9996d401060Sralph  * Change owner, group and mode of file.
1000978e44c9Sralph  */
1001*bdfadfffSbostic static int
10021b11f3a3Sbostic fchog(fd, file, owner, group, mode)
10031b11f3a3Sbostic 	int fd;
1004978e44c9Sralph 	char *file, *owner, *group;
1005d49851feSralph 	int mode;
1006978e44c9Sralph {
1007978e44c9Sralph 	register int i;
1008978e44c9Sralph 	int uid, gid;
1009e980046fSralph 	extern char user[];
1010e980046fSralph 	extern int userid;
1011978e44c9Sralph 
1012978e44c9Sralph 	uid = userid;
1013978e44c9Sralph 	if (userid == 0) {
101426ce117eSsklower 		if (*owner == ':') {
101526ce117eSsklower 			uid = atoi(owner + 1);
101626ce117eSsklower 		} else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
101701de96ecSralph 			if ((pw = getpwnam(owner)) == NULL) {
1018d49851feSralph 				if (mode & 04000) {
101926ce117eSsklower 					note("%s:%s: unknown login name, clearing setuid",
10202cb25675Sralph 						host, owner);
102126ce117eSsklower 					mode &= ~04000;
102226ce117eSsklower 					uid = 0;
1023978e44c9Sralph 				}
1024d49851feSralph 			} else
102501de96ecSralph 				uid = pw->pw_uid;
1026d49851feSralph 		} else
102701de96ecSralph 			uid = pw->pw_uid;
102826ce117eSsklower 		if (*group == ':') {
102926ce117eSsklower 			gid = atoi(group + 1);
103026ce117eSsklower 			goto ok;
103126ce117eSsklower 		}
10326d401060Sralph 	} else if ((mode & 04000) && strcmp(user, owner) != 0)
10336d401060Sralph 		mode &= ~04000;
1034e980046fSralph 	gid = -1;
103501de96ecSralph 	if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
103626ce117eSsklower 		if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
103726ce117eSsklower 		   || ((gr = getgrnam(group)) == NULL)) {
1038d49851feSralph 			if (mode & 02000) {
103926ce117eSsklower 				note("%s:%s: unknown group", host, group);
104026ce117eSsklower 				mode &= ~02000;
1041978e44c9Sralph 			}
1042d49851feSralph 		} else
104301de96ecSralph 			gid = gr->gr_gid;
1044d49851feSralph 	} else
104501de96ecSralph 		gid = gr->gr_gid;
1046e980046fSralph 	if (userid && gid >= 0) {
104726ce117eSsklower 		if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
104801de96ecSralph 			if (!(strcmp(user, gr->gr_mem[i])))
1049978e44c9Sralph 				goto ok;
10506d401060Sralph 		mode &= ~02000;
1051e980046fSralph 		gid = -1;
1052978e44c9Sralph 	}
10531b11f3a3Sbostic ok:	if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
10541b11f3a3Sbostic 		note("%s: %s chown: %s", host, file, strerror(errno));
10551b11f3a3Sbostic 	else if (mode & 07000 &&
10561b11f3a3Sbostic 	   (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
10571b11f3a3Sbostic 		note("%s: %s chmod: %s", host, file, strerror(errno));
1058978e44c9Sralph 	return(0);
1059978e44c9Sralph }
1060978e44c9Sralph 
1061c27256c0Sralph /*
1062c27256c0Sralph  * Check for files on the machine being updated that are not on the master
1063c27256c0Sralph  * machine and remove them.
1064c27256c0Sralph  */
1065*bdfadfffSbostic static void
10666d401060Sralph rmchk(opts)
1067c27256c0Sralph 	int opts;
1068c27256c0Sralph {
106901de96ecSralph 	register char *cp, *s;
1070c27256c0Sralph 	struct stat stb;
1071c27256c0Sralph 
1072c27256c0Sralph 	if (debug)
10736d401060Sralph 		printf("rmchk()\n");
1074156664fbSralph 
1075c27256c0Sralph 	/*
1076c27256c0Sralph 	 * Tell the remote to clean the files from the last directory sent.
1077c27256c0Sralph 	 */
10786d401060Sralph 	(void) sprintf(buf, "C%o\n", opts & VERIFY);
1079c27256c0Sralph 	if (debug)
1080c27256c0Sralph 		printf("buf = %s", buf);
1081c27256c0Sralph 	(void) write(rem, buf, strlen(buf));
1082c27256c0Sralph 	if (response() < 0)
1083c27256c0Sralph 		return;
1084c27256c0Sralph 	for (;;) {
108501de96ecSralph 		cp = s = buf;
1086c27256c0Sralph 		do {
1087c27256c0Sralph 			if (read(rem, cp, 1) != 1)
1088*bdfadfffSbostic 				lostconn(0);
1089c27256c0Sralph 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1090c27256c0Sralph 
109101de96ecSralph 		switch (*s++) {
10926d401060Sralph 		case 'Q': /* Query if file should be removed */
1093156664fbSralph 			/*
1094156664fbSralph 			 * Return the following codes to remove query.
10956d401060Sralph 			 * N\n -- file exists - DON'T remove.
10966d401060Sralph 			 * Y\n -- file doesn't exist - REMOVE.
1097156664fbSralph 			 */
1098c27256c0Sralph 			*--cp = '\0';
109901de96ecSralph 			(void) sprintf(tp, "/%s", s);
1100c27256c0Sralph 			if (debug)
1101c27256c0Sralph 				printf("check %s\n", target);
110258fc31f7Sralph 			if (except(target))
1103c27256c0Sralph 				(void) write(rem, "N\n", 2);
11042cb25675Sralph 			else if (lstat(target, &stb) < 0)
1105c27256c0Sralph 				(void) write(rem, "Y\n", 2);
11066d401060Sralph 			else
11076d401060Sralph 				(void) write(rem, "N\n", 2);
1108c27256c0Sralph 			break;
1109c27256c0Sralph 
1110c27256c0Sralph 		case '\0':
1111c27256c0Sralph 			*--cp = '\0';
111201de96ecSralph 			if (*s != '\0')
111301de96ecSralph 				log(lfp, "%s\n", s);
1114c27256c0Sralph 			break;
1115c27256c0Sralph 
11166d401060Sralph 		case 'E':
11176d401060Sralph 			*tp = '\0';
11186d401060Sralph 			ack();
11196d401060Sralph 			return;
11206d401060Sralph 
1121c27256c0Sralph 		case '\1':
1122c27256c0Sralph 		case '\2':
1123aca7acf4Sralph 			nerrs++;
112401de96ecSralph 			if (*s != '\n') {
1125c27256c0Sralph 				if (!iamremote) {
1126c27256c0Sralph 					fflush(stdout);
112701de96ecSralph 					(void) write(2, s, cp - s);
1128c27256c0Sralph 				}
1129c27256c0Sralph 				if (lfp != NULL)
113001de96ecSralph 					(void) fwrite(s, 1, cp - s, lfp);
1131c27256c0Sralph 			}
1132c27256c0Sralph 			if (buf[0] == '\2')
1133*bdfadfffSbostic 				lostconn(0);
1134c27256c0Sralph 			break;
1135c27256c0Sralph 
1136c27256c0Sralph 		default:
11376d401060Sralph 			error("rmchk: unexpected response '%s'\n", buf);
11386d401060Sralph 			err();
1139c27256c0Sralph 		}
1140c27256c0Sralph 	}
1141c27256c0Sralph }
1142c27256c0Sralph 
1143c27256c0Sralph /*
11446d401060Sralph  * Check the current directory (initialized by the 'T' command to server())
1145156664fbSralph  * for extraneous files and remove them.
1146c27256c0Sralph  */
1147*bdfadfffSbostic static void
114800fd8e74Sralph clean(cp)
114900fd8e74Sralph 	register char *cp;
1150c27256c0Sralph {
1151c27256c0Sralph 	DIR *d;
11526d401060Sralph 	register struct direct *dp;
1153c27256c0Sralph 	struct stat stb;
11546d401060Sralph 	char *otp;
11556d401060Sralph 	int len, opts;
1156c27256c0Sralph 
11576d401060Sralph 	opts = 0;
11586d401060Sralph 	while (*cp >= '0' && *cp <= '7')
11596d401060Sralph 		opts = (opts << 3) | (*cp++ - '0');
11606d401060Sralph 	if (*cp != '\0') {
11616d401060Sralph 		error("clean: options not delimited\n");
1162c27256c0Sralph 		return;
1163c27256c0Sralph 	}
1164e980046fSralph 	if ((d = opendir(target)) == NULL) {
116590bde559Sbostic 		error("%s:%s: %s\n", host, target, strerror(errno));
116601de96ecSralph 		return;
116701de96ecSralph 	}
11686d401060Sralph 	ack();
1169c27256c0Sralph 
1170c27256c0Sralph 	otp = tp;
1171c27256c0Sralph 	len = tp - target;
1172c27256c0Sralph 	while (dp = readdir(d)) {
1173c27256c0Sralph 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1174c27256c0Sralph 			continue;
1175c27256c0Sralph 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
11762cb25675Sralph 			error("%s:%s/%s: Name too long\n",
11772cb25675Sralph 				host, target, dp->d_name);
1178c27256c0Sralph 			continue;
1179c27256c0Sralph 		}
1180c27256c0Sralph 		tp = otp;
1181c27256c0Sralph 		*tp++ = '/';
1182c27256c0Sralph 		cp = dp->d_name;;
1183c27256c0Sralph 		while (*tp++ = *cp++)
1184c27256c0Sralph 			;
1185c27256c0Sralph 		tp--;
11862cb25675Sralph 		if (lstat(target, &stb) < 0) {
118790bde559Sbostic 			error("%s:%s: %s\n", host, target, strerror(errno));
1188c27256c0Sralph 			continue;
1189c27256c0Sralph 		}
11906d401060Sralph 		(void) sprintf(buf, "Q%s\n", dp->d_name);
1191c27256c0Sralph 		(void) write(rem, buf, strlen(buf));
1192c27256c0Sralph 		cp = buf;
1193c27256c0Sralph 		do {
1194c27256c0Sralph 			if (read(rem, cp, 1) != 1)
1195*bdfadfffSbostic 				cleanup(0);
1196c27256c0Sralph 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1197c27256c0Sralph 		*--cp = '\0';
1198c27256c0Sralph 		cp = buf;
11996d401060Sralph 		if (*cp != 'Y')
1200c27256c0Sralph 			continue;
120101de96ecSralph 		if (opts & VERIFY) {
120201de96ecSralph 			cp = buf;
120301de96ecSralph 			*cp++ = '\0';
1204e980046fSralph 			(void) sprintf(cp, "need to remove: %s\n", target);
120501de96ecSralph 			(void) write(rem, buf, strlen(cp) + 1);
120601de96ecSralph 		} else
12076d57652cStorek 			removeit(&stb);
1208c27256c0Sralph 	}
1209c27256c0Sralph 	closedir(d);
1210c27256c0Sralph 	(void) write(rem, "E\n", 2);
1211c27256c0Sralph 	(void) response();
12126d401060Sralph 	tp = otp;
121301de96ecSralph 	*tp = '\0';
1214c27256c0Sralph }
1215c27256c0Sralph 
121601de96ecSralph /*
121701de96ecSralph  * Remove a file or directory (recursively) and send back an acknowledge
121801de96ecSralph  * or an error message.
121901de96ecSralph  */
1220*bdfadfffSbostic static void
12216d57652cStorek removeit(stp)
12222cb25675Sralph 	struct stat *stp;
1223c27256c0Sralph {
1224c27256c0Sralph 	DIR *d;
1225c27256c0Sralph 	struct direct *dp;
1226c27256c0Sralph 	register char *cp;
1227c27256c0Sralph 	struct stat stb;
1228c27256c0Sralph 	char *otp;
1229c27256c0Sralph 	int len;
1230c27256c0Sralph 
12312cb25675Sralph 	switch (stp->st_mode & S_IFMT) {
1232c27256c0Sralph 	case S_IFREG:
12332cb25675Sralph 	case S_IFLNK:
1234c27256c0Sralph 		if (unlink(target) < 0)
1235c27256c0Sralph 			goto bad;
1236c27256c0Sralph 		goto removed;
1237c27256c0Sralph 
1238c27256c0Sralph 	case S_IFDIR:
1239c27256c0Sralph 		break;
1240c27256c0Sralph 
1241c27256c0Sralph 	default:
12422cb25675Sralph 		error("%s:%s: not a plain file\n", host, target);
1243c27256c0Sralph 		return;
1244c27256c0Sralph 	}
1245c27256c0Sralph 
1246e980046fSralph 	if ((d = opendir(target)) == NULL)
1247c27256c0Sralph 		goto bad;
1248c27256c0Sralph 
1249c27256c0Sralph 	otp = tp;
1250c27256c0Sralph 	len = tp - target;
1251c27256c0Sralph 	while (dp = readdir(d)) {
1252c27256c0Sralph 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1253c27256c0Sralph 			continue;
1254c27256c0Sralph 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
12552cb25675Sralph 			error("%s:%s/%s: Name too long\n",
12562cb25675Sralph 				host, target, dp->d_name);
1257c27256c0Sralph 			continue;
1258c27256c0Sralph 		}
1259c27256c0Sralph 		tp = otp;
1260c27256c0Sralph 		*tp++ = '/';
1261c27256c0Sralph 		cp = dp->d_name;;
1262c27256c0Sralph 		while (*tp++ = *cp++)
1263c27256c0Sralph 			;
1264c27256c0Sralph 		tp--;
12652cb25675Sralph 		if (lstat(target, &stb) < 0) {
126690bde559Sbostic 			error("%s:%s: %s\n", host, target, strerror(errno));
1267c27256c0Sralph 			continue;
1268c27256c0Sralph 		}
12696d57652cStorek 		removeit(&stb);
1270c27256c0Sralph 	}
1271c27256c0Sralph 	closedir(d);
1272c27256c0Sralph 	tp = otp;
1273c27256c0Sralph 	*tp = '\0';
1274c27256c0Sralph 	if (rmdir(target) < 0) {
1275c27256c0Sralph bad:
127690bde559Sbostic 		error("%s:%s: %s\n", host, target, strerror(errno));
1277c27256c0Sralph 		return;
1278c27256c0Sralph 	}
1279c27256c0Sralph removed:
1280c27256c0Sralph 	cp = buf;
1281c27256c0Sralph 	*cp++ = '\0';
1282c27256c0Sralph 	(void) sprintf(cp, "removed %s\n", target);
1283c27256c0Sralph 	(void) write(rem, buf, strlen(cp) + 1);
1284c27256c0Sralph }
1285c27256c0Sralph 
12866d401060Sralph /*
12876d401060Sralph  * Execute a shell command to handle special cases.
12886d401060Sralph  */
1289*bdfadfffSbostic static void
12906d401060Sralph dospecial(cmd)
12916d401060Sralph 	char *cmd;
12926d401060Sralph {
12936d401060Sralph 	int fd[2], status, pid, i;
12946d401060Sralph 	register char *cp, *s;
12956d401060Sralph 	char sbuf[BUFSIZ];
1296e980046fSralph 	extern int userid, groupid;
12976d401060Sralph 
12986d401060Sralph 	if (pipe(fd) < 0) {
129990bde559Sbostic 		error("%s\n", strerror(errno));
13006d401060Sralph 		return;
13016d401060Sralph 	}
13026d401060Sralph 	if ((pid = fork()) == 0) {
13036d401060Sralph 		/*
13046d401060Sralph 		 * Return everything the shell commands print.
13056d401060Sralph 		 */
13066d401060Sralph 		(void) close(0);
13076d401060Sralph 		(void) close(1);
13086d401060Sralph 		(void) close(2);
13090af019deSbostic 		(void) open(_PATH_DEVNULL, O_RDONLY);
13106d401060Sralph 		(void) dup(fd[1]);
13116d401060Sralph 		(void) dup(fd[1]);
13126d401060Sralph 		(void) close(fd[0]);
13136d401060Sralph 		(void) close(fd[1]);
1314e980046fSralph 		setgid(groupid);
1315a5437550Sralph 		setuid(userid);
13160af019deSbostic 		execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
13176d401060Sralph 		_exit(127);
13186d401060Sralph 	}
13196d401060Sralph 	(void) close(fd[1]);
13206d401060Sralph 	s = sbuf;
13216d401060Sralph 	*s++ = '\0';
13226d401060Sralph 	while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
13236d401060Sralph 		cp = buf;
13246d401060Sralph 		do {
13256d401060Sralph 			*s++ = *cp++;
13266d401060Sralph 			if (cp[-1] != '\n') {
13276d401060Sralph 				if (s < &sbuf[sizeof(sbuf)-1])
13286d401060Sralph 					continue;
13296d401060Sralph 				*s++ = '\n';
13306d401060Sralph 			}
13316d401060Sralph 			/*
13326d401060Sralph 			 * Throw away blank lines.
13336d401060Sralph 			 */
13346d401060Sralph 			if (s == &sbuf[2]) {
13356d401060Sralph 				s--;
13366d401060Sralph 				continue;
13376d401060Sralph 			}
13386d401060Sralph 			(void) write(rem, sbuf, s - sbuf);
13396d401060Sralph 			s = &sbuf[1];
13406d401060Sralph 		} while (--i);
13416d401060Sralph 	}
13426d401060Sralph 	if (s > &sbuf[1]) {
13436d401060Sralph 		*s++ = '\n';
13446d401060Sralph 		(void) write(rem, sbuf, s - sbuf);
13456d401060Sralph 	}
13466d401060Sralph 	while ((i = wait(&status)) != pid && i != -1)
13476d401060Sralph 		;
13486d401060Sralph 	if (i == -1)
13496d401060Sralph 		status = -1;
1350e980046fSralph 	(void) close(fd[0]);
13516d401060Sralph 	if (status)
13526d401060Sralph 		error("shell returned %d\n", status);
13536d401060Sralph 	else
13546d401060Sralph 		ack();
13556d401060Sralph }
13566d401060Sralph 
1357*bdfadfffSbostic #if __STDC__
1358*bdfadfffSbostic #include <stdarg.h>
1359*bdfadfffSbostic #else
1360*bdfadfffSbostic #include <varargs.h>
1361*bdfadfffSbostic #endif
1362*bdfadfffSbostic 
1363*bdfadfffSbostic void
1364*bdfadfffSbostic #if __STDC__
1365*bdfadfffSbostic log(FILE *fp, const char *fmt, ...)
1366*bdfadfffSbostic #else
1367*bdfadfffSbostic log(fp, fmt, va_alist)
1368d49851feSralph 	FILE *fp;
1369978e44c9Sralph 	char *fmt;
1370*bdfadfffSbostic         va_dcl
1371*bdfadfffSbostic #endif
1372978e44c9Sralph {
1373*bdfadfffSbostic 	va_list ap;
1374*bdfadfffSbostic #if __STDC__
1375*bdfadfffSbostic 	va_start(ap, fmt);
1376*bdfadfffSbostic #else
1377*bdfadfffSbostic 	va_start(ap);
1378*bdfadfffSbostic #endif
1379978e44c9Sralph 	/* Print changes locally if not quiet mode */
1380978e44c9Sralph 	if (!qflag)
1381*bdfadfffSbostic 		(void)vprintf(fmt, ap);
1382978e44c9Sralph 
1383978e44c9Sralph 	/* Save changes (for mailing) if really updating files */
1384ad3d87d9Sralph 	if (!(options & VERIFY) && fp != NULL)
1385*bdfadfffSbostic 		(void)vfprintf(fp, fmt, ap);
1386*bdfadfffSbostic 	va_end(ap);
1387978e44c9Sralph }
1388978e44c9Sralph 
1389*bdfadfffSbostic void
1390*bdfadfffSbostic #if __STDC__
1391*bdfadfffSbostic error(const char *fmt, ...)
1392*bdfadfffSbostic #else
1393*bdfadfffSbostic error(fmt, va_alist)
1394978e44c9Sralph 	char *fmt;
1395*bdfadfffSbostic         va_dcl
1396*bdfadfffSbostic #endif
1397978e44c9Sralph {
139852d94563Sbostic 	static FILE *fp;
1399*bdfadfffSbostic 	va_list ap;
1400*bdfadfffSbostic #if __STDC__
1401*bdfadfffSbostic 	va_start(ap, fmt);
1402*bdfadfffSbostic #else
1403*bdfadfffSbostic 	va_start(ap);
1404*bdfadfffSbostic #endif
140552d94563Sbostic 
140652d94563Sbostic 	++nerrs;
140752d94563Sbostic 	if (!fp && !(fp = fdopen(rem, "w")))
140852d94563Sbostic 		return;
140952d94563Sbostic 	if (iamremote) {
141052d94563Sbostic 		(void)fprintf(fp, "%crdist: ", 0x01);
1411*bdfadfffSbostic 		(void)vfprintf(fp, fmt, ap);
141252d94563Sbostic 		fflush(fp);
141352d94563Sbostic 	}
141452d94563Sbostic 	else {
1415d49851feSralph 		fflush(stdout);
141652d94563Sbostic 		(void)fprintf(stderr, "rdist: ");
1417*bdfadfffSbostic 		(void)vfprintf(stderr, fmt, ap);
141852d94563Sbostic 		fflush(stderr);
141952d94563Sbostic 	}
142052d94563Sbostic 	if (lfp != NULL) {
142152d94563Sbostic 		(void)fprintf(lfp, "rdist: ");
1422*bdfadfffSbostic 		(void)vfprintf(lfp, fmt, ap);
142352d94563Sbostic 		fflush(lfp);
142452d94563Sbostic 	}
1425*bdfadfffSbostic 	va_end(ap);
1426978e44c9Sralph }
1427978e44c9Sralph 
1428*bdfadfffSbostic void
1429*bdfadfffSbostic #if __STDC__
1430*bdfadfffSbostic fatal(const char *fmt, ...)
1431*bdfadfffSbostic #else
1432*bdfadfffSbostic fatal(fmt, va_alist)
1433978e44c9Sralph 	char *fmt;
1434*bdfadfffSbostic         va_dcl
1435*bdfadfffSbostic #endif
1436978e44c9Sralph {
143752d94563Sbostic 	static FILE *fp;
1438*bdfadfffSbostic 	va_list ap;
1439*bdfadfffSbostic #if __STDC__
1440*bdfadfffSbostic 	va_start(ap, fmt);
1441*bdfadfffSbostic #else
1442*bdfadfffSbostic 	va_start(ap);
1443*bdfadfffSbostic #endif
144452d94563Sbostic 
144552d94563Sbostic 	++nerrs;
144652d94563Sbostic 	if (!fp && !(fp = fdopen(rem, "w")))
144752d94563Sbostic 		return;
144852d94563Sbostic 	if (iamremote) {
144952d94563Sbostic 		(void)fprintf(fp, "%crdist: ", 0x02);
1450*bdfadfffSbostic 		(void)vfprintf(fp, fmt, ap);
145152d94563Sbostic 		fflush(fp);
145252d94563Sbostic 	}
145352d94563Sbostic 	else {
1454d49851feSralph 		fflush(stdout);
145552d94563Sbostic 		(void)fprintf(stderr, "rdist: ");
1456*bdfadfffSbostic 		(void)vfprintf(stderr, fmt, ap);
145752d94563Sbostic 		fflush(stderr);
145852d94563Sbostic 	}
145952d94563Sbostic 	if (lfp != NULL) {
146052d94563Sbostic 		(void)fprintf(lfp, "rdist: ");
1461*bdfadfffSbostic 		(void)vfprintf(lfp, fmt, ap);
146252d94563Sbostic 		fflush(lfp);
146352d94563Sbostic 	}
1464*bdfadfffSbostic 	cleanup(0);
1465978e44c9Sralph }
1466978e44c9Sralph 
1467*bdfadfffSbostic static int
1468978e44c9Sralph response()
1469978e44c9Sralph {
1470156664fbSralph 	char *cp, *s;
147115867ad7Sralph 	char resp[BUFSIZ];
1472978e44c9Sralph 
1473978e44c9Sralph 	if (debug)
1474978e44c9Sralph 		printf("response()\n");
1475978e44c9Sralph 
147615867ad7Sralph 	cp = s = resp;
1477978e44c9Sralph 	do {
1478978e44c9Sralph 		if (read(rem, cp, 1) != 1)
1479*bdfadfffSbostic 			lostconn(0);
148015867ad7Sralph 	} while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
1481156664fbSralph 
1482156664fbSralph 	switch (*s++) {
1483156664fbSralph 	case '\0':
1484156664fbSralph 		*--cp = '\0';
14856d401060Sralph 		if (*s != '\0') {
1486156664fbSralph 			log(lfp, "%s\n", s);
14876d401060Sralph 			return(1);
14886d401060Sralph 		}
1489156664fbSralph 		return(0);
149026ce117eSsklower 	case '\3':
149126ce117eSsklower 		*--cp = '\0';
149226ce117eSsklower 		log(lfp, "Note: %s\n",s);
149326ce117eSsklower 		return(response());
1494156664fbSralph 
1495156664fbSralph 	default:
1496156664fbSralph 		s--;
1497156664fbSralph 		/* fall into... */
1498156664fbSralph 	case '\1':
1499156664fbSralph 	case '\2':
1500aca7acf4Sralph 		nerrs++;
1501156664fbSralph 		if (*s != '\n') {
1502d49851feSralph 			if (!iamremote) {
1503d49851feSralph 				fflush(stdout);
1504156664fbSralph 				(void) write(2, s, cp - s);
1505d49851feSralph 			}
1506978e44c9Sralph 			if (lfp != NULL)
1507156664fbSralph 				(void) fwrite(s, 1, cp - s, lfp);
1508978e44c9Sralph 		}
150915867ad7Sralph 		if (resp[0] == '\2')
1510*bdfadfffSbostic 			lostconn(0);
151101de96ecSralph 		return(-1);
1512978e44c9Sralph 	}
1513978e44c9Sralph }
1514978e44c9Sralph 
1515aca7acf4Sralph /*
1516aca7acf4Sralph  * Remove temporary files and do any cleanup operations before exiting.
1517aca7acf4Sralph  */
151821439bbcSbostic void
1519*bdfadfffSbostic cleanup(signo)
1520*bdfadfffSbostic 	int signo;
1521978e44c9Sralph {
152216580127Seric 	(void) unlink(tempfile);
1523aca7acf4Sralph 	exit(1);
1524978e44c9Sralph }
152526ce117eSsklower 
1526*bdfadfffSbostic static void
1527*bdfadfffSbostic #if __STDC__
1528*bdfadfffSbostic note(const char *fmt, ...)
1529*bdfadfffSbostic #else
1530*bdfadfffSbostic note(fmt, va_alist)
15316d57652cStorek 	char *fmt;
1532*bdfadfffSbostic         va_dcl
1533*bdfadfffSbostic #endif
153426ce117eSsklower {
153526ce117eSsklower 	static char buf[BUFSIZ];
1536*bdfadfffSbostic 	va_list ap;
1537*bdfadfffSbostic #if __STDC__
1538*bdfadfffSbostic 	va_start(ap, fmt);
1539*bdfadfffSbostic #else
1540*bdfadfffSbostic 	va_start(ap);
1541*bdfadfffSbostic #endif
1542*bdfadfffSbostic 	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
1543*bdfadfffSbostic 	va_end(ap);
154426ce117eSsklower 	comment(buf);
154526ce117eSsklower }
154626ce117eSsklower 
1547*bdfadfffSbostic static void
154826ce117eSsklower comment(s)
154926ce117eSsklower 	char *s;
155026ce117eSsklower {
1551*bdfadfffSbostic 	char c;
1552*bdfadfffSbostic 
1553*bdfadfffSbostic 	c = '\3';
155426ce117eSsklower 	write(rem, &c, 1);
155526ce117eSsklower 	write(rem, s, strlen(s));
155626ce117eSsklower 	c = '\n';
155726ce117eSsklower 	write(rem, &c, 1);
155826ce117eSsklower }
1559