xref: /original-bsd/usr.bin/rdist/server.c (revision 7a864411)
1babdf84bSdist /*
2babdf84bSdist  * Copyright (c) 1983 Regents of the University of California.
3*7a864411Sbostic  * All rights reserved.
4*7a864411Sbostic  *
5*7a864411Sbostic  * Redistribution and use in source and binary forms are permitted
6*7a864411Sbostic  * provided that this notice is preserved and that due credit is given
7*7a864411Sbostic  * to the University of California at Berkeley. The name of the University
8*7a864411Sbostic  * may not be used to endorse or promote products derived from this
9*7a864411Sbostic  * software without specific prior written permission. This software
10*7a864411Sbostic  * is provided ``as is'' without express or implied warranty.
11babdf84bSdist  */
12babdf84bSdist 
13978e44c9Sralph #ifndef lint
14*7a864411Sbostic static char sccsid[] = "@(#)server.c	5.6 (Berkeley) 02/01/88";
15*7a864411Sbostic #endif /* not lint */
16978e44c9Sralph 
17978e44c9Sralph #include "defs.h"
18978e44c9Sralph 
196d401060Sralph #define	ack() 	(void) write(rem, "\0\n", 2)
206d401060Sralph #define	err() 	(void) write(rem, "\1\n", 2)
21978e44c9Sralph 
222cb25675Sralph struct	linkbuf *ihead;		/* list of files with more than one link */
23d49851feSralph char	buf[BUFSIZ];		/* general purpose buffer */
24978e44c9Sralph char	target[BUFSIZ];		/* target/source directory name */
25978e44c9Sralph char	*tp;			/* pointer to end of target name */
2626ce117eSsklower char	*Tdest;			/* pointer to last T dest*/
27978e44c9Sralph int	catname;		/* cat name to target name */
2865a0d230Sralph char	*stp[32];		/* stack of saved tp's for directories */
2901de96ecSralph int	oumask;			/* old umask for creating files */
30d49851feSralph 
31d49851feSralph extern	FILE *lfp;		/* log file for mailing changes */
32d49851feSralph 
33aca7acf4Sralph int	cleanup();
342cb25675Sralph struct	linkbuf *savelink();
35aca7acf4Sralph 
36978e44c9Sralph /*
37978e44c9Sralph  * Server routine to read requests and process them.
38978e44c9Sralph  * Commands are:
39978e44c9Sralph  *	Tname	- Transmit file if out of date
40978e44c9Sralph  *	Vname	- Verify if file out of date or not
41978e44c9Sralph  *	Qname	- Query if file exists. Return mtime & size if it does.
42978e44c9Sralph  */
43978e44c9Sralph server()
44978e44c9Sralph {
45978e44c9Sralph 	char cmdbuf[BUFSIZ];
46978e44c9Sralph 	register char *cp;
47978e44c9Sralph 
48aca7acf4Sralph 	signal(SIGHUP, cleanup);
49aca7acf4Sralph 	signal(SIGINT, cleanup);
50aca7acf4Sralph 	signal(SIGQUIT, cleanup);
51aca7acf4Sralph 	signal(SIGTERM, cleanup);
52aca7acf4Sralph 	signal(SIGPIPE, cleanup);
53aca7acf4Sralph 
54aca7acf4Sralph 	rem = 0;
5501de96ecSralph 	oumask = umask(0);
5636a9f5b1Sralph 	(void) sprintf(buf, "V%d\n", VERSION);
5736a9f5b1Sralph 	(void) write(rem, buf, strlen(buf));
58978e44c9Sralph 
59978e44c9Sralph 	for (;;) {
60978e44c9Sralph 		cp = cmdbuf;
61978e44c9Sralph 		if (read(rem, cp, 1) <= 0)
62978e44c9Sralph 			return;
63978e44c9Sralph 		if (*cp++ == '\n') {
6401de96ecSralph 			error("server: expected control record\n");
65978e44c9Sralph 			continue;
66978e44c9Sralph 		}
67978e44c9Sralph 		do {
68978e44c9Sralph 			if (read(rem, cp, 1) != 1)
69aca7acf4Sralph 				cleanup();
70d49851feSralph 		} while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
71978e44c9Sralph 		*--cp = '\0';
72978e44c9Sralph 		cp = cmdbuf;
73978e44c9Sralph 		switch (*cp++) {
74978e44c9Sralph 		case 'T':  /* init target file/directory name */
75d49851feSralph 			catname = 1;	/* target should be directory */
76d49851feSralph 			goto dotarget;
77d49851feSralph 
78d49851feSralph 		case 't':  /* init target file/directory name */
79978e44c9Sralph 			catname = 0;
80d49851feSralph 		dotarget:
8101de96ecSralph 			if (exptilde(target, cp) == NULL)
8201de96ecSralph 				continue;
83978e44c9Sralph 			tp = target;
84978e44c9Sralph 			while (*tp)
85978e44c9Sralph 				tp++;
866d401060Sralph 			ack();
87978e44c9Sralph 			continue;
88978e44c9Sralph 
892cb25675Sralph 		case 'R':  /* Transfer a regular file. */
902cb25675Sralph 			recvf(cp, S_IFREG);
91978e44c9Sralph 			continue;
92978e44c9Sralph 
932cb25675Sralph 		case 'D':  /* Transfer a directory. */
942cb25675Sralph 			recvf(cp, S_IFDIR);
952cb25675Sralph 			continue;
962cb25675Sralph 
972cb25675Sralph 		case 'K':  /* Transfer symbolic link. */
982cb25675Sralph 			recvf(cp, S_IFLNK);
992cb25675Sralph 			continue;
1002cb25675Sralph 
1012cb25675Sralph 		case 'k':  /* Transfer hard link. */
1022cb25675Sralph 			hardlink(cp);
103978e44c9Sralph 			continue;
104978e44c9Sralph 
105978e44c9Sralph 		case 'E':  /* End. (of directory) */
106978e44c9Sralph 			*tp = '\0';
10701de96ecSralph 			if (catname <= 0) {
10801de96ecSralph 				error("server: too many 'E's\n");
109978e44c9Sralph 				continue;
110978e44c9Sralph 			}
11101de96ecSralph 			tp = stp[--catname];
11265a0d230Sralph 			*tp = '\0';
1136d401060Sralph 			ack();
114978e44c9Sralph 			continue;
115978e44c9Sralph 
116c27256c0Sralph 		case 'C':  /* Clean. Cleanup a directory */
1176d401060Sralph 			clean(cp);
118c27256c0Sralph 			continue;
119c27256c0Sralph 
12001de96ecSralph 		case 'Q':  /* Query. Does the file/directory exist? */
12101de96ecSralph 			query(cp);
12265a0d230Sralph 			continue;
12365a0d230Sralph 
1246d401060Sralph 		case 'S':  /* Special. Execute commands */
1256d401060Sralph 			dospecial(cp);
1266d401060Sralph 			continue;
1276d401060Sralph 
12801de96ecSralph #ifdef notdef
12901de96ecSralph 		/*
13001de96ecSralph 		 * These entries are reserved but not currently used.
13101de96ecSralph 		 * The intent is to allow remote hosts to have master copies.
13201de96ecSralph 		 * Currently, only the host rdist runs on can have masters.
13301de96ecSralph 		 */
13401de96ecSralph 		case 'X':  /* start a new list of files to exclude */
13501de96ecSralph 			except = bp = NULL;
13601de96ecSralph 		case 'x':  /* add name to list of files to exclude */
13701de96ecSralph 			if (*cp == '\0') {
1386d401060Sralph 				ack();
13901de96ecSralph 				continue;
14001de96ecSralph 			}
14101de96ecSralph 			if (*cp == '~') {
14201de96ecSralph 				if (exptilde(buf, cp) == NULL)
14301de96ecSralph 					continue;
14401de96ecSralph 				cp = buf;
14501de96ecSralph 			}
14601de96ecSralph 			if (bp == NULL)
1476d401060Sralph 				except = bp = expand(makeblock(NAME, cp), E_VARS);
14801de96ecSralph 			else
1496d401060Sralph 				bp->b_next = expand(makeblock(NAME, cp), E_VARS);
15001de96ecSralph 			while (bp->b_next != NULL)
15101de96ecSralph 				bp = bp->b_next;
1526d401060Sralph 			ack();
15301de96ecSralph 			continue;
15401de96ecSralph 
1556d401060Sralph 		case 'I':  /* Install. Transfer file if out of date. */
15601de96ecSralph 			opts = 0;
15701de96ecSralph 			while (*cp >= '0' && *cp <= '7')
15801de96ecSralph 				opts = (opts << 3) | (*cp++ - '0');
15901de96ecSralph 			if (*cp++ != ' ') {
16001de96ecSralph 				error("server: options not delimited\n");
16101de96ecSralph 				return;
16201de96ecSralph 			}
1636d401060Sralph 			install(cp, opts);
164978e44c9Sralph 			continue;
165978e44c9Sralph 
166978e44c9Sralph 		case 'L':  /* Log. save message in log file */
16765a0d230Sralph 			log(lfp, cp);
168978e44c9Sralph 			continue;
16901de96ecSralph #endif
170978e44c9Sralph 
171d49851feSralph 		case '\1':
172aca7acf4Sralph 			nerrs++;
173d49851feSralph 			continue;
174d49851feSralph 
175d49851feSralph 		case '\2':
176d49851feSralph 			return;
177d49851feSralph 
178978e44c9Sralph 		default:
17901de96ecSralph 			error("server: unknown command '%s'\n", cp);
180978e44c9Sralph 		case '\0':
181978e44c9Sralph 			continue;
182978e44c9Sralph 		}
183978e44c9Sralph 	}
184978e44c9Sralph }
185978e44c9Sralph 
186978e44c9Sralph /*
18701de96ecSralph  * Update the file(s) if they are different.
18801de96ecSralph  * destdir = 1 if destination should be a directory
18901de96ecSralph  * (i.e., more than one source is being copied to the same destination).
190978e44c9Sralph  */
19101de96ecSralph install(src, dest, destdir, opts)
19201de96ecSralph 	char *src, *dest;
19301de96ecSralph 	int destdir, opts;
19401de96ecSralph {
19501de96ecSralph 	char *rname;
19626ce117eSsklower 	char destcopy[BUFSIZ];
19701de96ecSralph 
19801de96ecSralph 	if (dest == NULL) {
19901de96ecSralph 		opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
20001de96ecSralph 		dest = src;
20101de96ecSralph 	}
20201de96ecSralph 
20301de96ecSralph 	if (nflag || debug) {
20401de96ecSralph 		printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
20501de96ecSralph 			opts & WHOLE ? " -w" : "",
20601de96ecSralph 			opts & YOUNGER ? " -y" : "",
20701de96ecSralph 			opts & COMPARE ? " -b" : "",
2086d401060Sralph 			opts & REMOVE ? " -R" : "", src, dest);
20901de96ecSralph 		if (nflag)
21001de96ecSralph 			return;
21101de96ecSralph 	}
21201de96ecSralph 
21301de96ecSralph 	rname = exptilde(target, src);
21401de96ecSralph 	if (rname == NULL)
21501de96ecSralph 		return;
21601de96ecSralph 	tp = target;
21701de96ecSralph 	while (*tp)
21801de96ecSralph 		tp++;
21901de96ecSralph 	/*
22001de96ecSralph 	 * If we are renaming a directory and we want to preserve
2216d401060Sralph 	 * the directory heirarchy (-w), we must strip off the leading
22201de96ecSralph 	 * directory name and preserve the rest.
22301de96ecSralph 	 */
22401de96ecSralph 	if (opts & WHOLE) {
2256d401060Sralph 		while (*rname == '/')
22601de96ecSralph 			rname++;
2276d401060Sralph 		destdir = 1;
22801de96ecSralph 	} else {
22901de96ecSralph 		rname = rindex(target, '/');
23001de96ecSralph 		if (rname == NULL)
23101de96ecSralph 			rname = target;
23201de96ecSralph 		else
23301de96ecSralph 			rname++;
23401de96ecSralph 	}
23501de96ecSralph 	if (debug)
23601de96ecSralph 		printf("target = %s, rname = %s\n", target, rname);
23701de96ecSralph 	/*
23801de96ecSralph 	 * Pass the destination file/directory name to remote.
23901de96ecSralph 	 */
24001de96ecSralph 	(void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
24101de96ecSralph 	if (debug)
24201de96ecSralph 		printf("buf = %s", buf);
24301de96ecSralph 	(void) write(rem, buf, strlen(buf));
24401de96ecSralph 	if (response() < 0)
24501de96ecSralph 		return;
24601de96ecSralph 
24726ce117eSsklower 	if (destdir) {
24826ce117eSsklower 		strcpy(destcopy, dest);
24926ce117eSsklower 		Tdest = destcopy;
25026ce117eSsklower 	}
25101de96ecSralph 	sendf(rname, opts);
25226ce117eSsklower 	Tdest = 0;
25301de96ecSralph }
25401de96ecSralph 
25526ce117eSsklower #define protoname() (pw ? pw->pw_name : user)
25626ce117eSsklower #define protogroup() (gr ? gr->gr_name : group)
25701de96ecSralph /*
25801de96ecSralph  * Transfer the file or directory in target[].
25901de96ecSralph  * rname is the name of the file on the remote host.
26001de96ecSralph  */
26101de96ecSralph sendf(rname, opts)
26201de96ecSralph 	char *rname;
263ad3d87d9Sralph 	int opts;
264978e44c9Sralph {
265aca7acf4Sralph 	register struct subcmd *sc;
266978e44c9Sralph 	struct stat stb;
2672cb25675Sralph 	int sizerr, f, u, len;
268978e44c9Sralph 	off_t i;
2692cb25675Sralph 	DIR *d;
2702cb25675Sralph 	struct direct *dp;
2712cb25675Sralph 	char *otp, *cp;
27258fc31f7Sralph 	extern struct subcmd *subcmds;
27326ce117eSsklower 	static char user[15], group[15];
274978e44c9Sralph 
275978e44c9Sralph 	if (debug)
27601de96ecSralph 		printf("sendf(%s, %x)\n", rname, opts);
277978e44c9Sralph 
27858fc31f7Sralph 	if (except(target))
27965a0d230Sralph 		return;
280e980046fSralph 	if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
28101de96ecSralph 		error("%s: %s\n", target, sys_errlist[errno]);
282978e44c9Sralph 		return;
283978e44c9Sralph 	}
2842cb25675Sralph 	if ((u = update(rname, opts, &stb)) == 0) {
2852cb25675Sralph 		if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
2862cb25675Sralph 			(void) savelink(&stb);
287978e44c9Sralph 		return;
2882cb25675Sralph 	}
289978e44c9Sralph 
29001de96ecSralph 	if (pw == NULL || pw->pw_uid != stb.st_uid)
29101de96ecSralph 		if ((pw = getpwuid(stb.st_uid)) == NULL) {
2926dfcab80Ssklower 			log(lfp, "%s: no password entry for uid %d \n",
2936dfcab80Ssklower 				target, stb.st_uid);
29426ce117eSsklower 			pw = NULL;
29526ce117eSsklower 			sprintf(user, ":%d", stb.st_uid);
296978e44c9Sralph 		}
29701de96ecSralph 	if (gr == NULL || gr->gr_gid != stb.st_gid)
29801de96ecSralph 		if ((gr = getgrgid(stb.st_gid)) == NULL) {
2996dfcab80Ssklower 			log(lfp, "%s: no name for group %d\n",
3006dfcab80Ssklower 				target, stb.st_gid);
30126ce117eSsklower 			gr = NULL;
30226ce117eSsklower 			sprintf(group, ":%d", stb.st_gid);
303978e44c9Sralph 		}
304156664fbSralph 	if (u == 1) {
3056d401060Sralph 		if (opts & VERIFY) {
3066d401060Sralph 			log(lfp, "need to install: %s\n", target);
3076d401060Sralph 			goto dospecial;
3086d401060Sralph 		}
30901de96ecSralph 		log(lfp, "installing: %s\n", target);
3106d401060Sralph 		opts &= ~(COMPARE|REMOVE);
311156664fbSralph 	}
312978e44c9Sralph 
313978e44c9Sralph 	switch (stb.st_mode & S_IFMT) {
3142cb25675Sralph 	case S_IFDIR:
3152cb25675Sralph 		if ((d = opendir(target)) == NULL) {
3162cb25675Sralph 			error("%s: %s\n", target, sys_errlist[errno]);
3172cb25675Sralph 			return;
3182cb25675Sralph 		}
3192cb25675Sralph 		(void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
32026ce117eSsklower 			stb.st_mode & 07777, protoname(), protogroup(), rname);
3212cb25675Sralph 		if (debug)
32215867ad7Sralph 			printf("buf = %s", buf);
3232cb25675Sralph 		(void) write(rem, buf, strlen(buf));
3242cb25675Sralph 		if (response() < 0) {
3252cb25675Sralph 			closedir(d);
3262cb25675Sralph 			return;
3272cb25675Sralph 		}
3282cb25675Sralph 
3292cb25675Sralph 		if (opts & REMOVE)
3302cb25675Sralph 			rmchk(opts);
3312cb25675Sralph 
3322cb25675Sralph 		otp = tp;
3332cb25675Sralph 		len = tp - target;
3342cb25675Sralph 		while (dp = readdir(d)) {
3352cb25675Sralph 			if (!strcmp(dp->d_name, ".") ||
3362cb25675Sralph 			    !strcmp(dp->d_name, ".."))
3372cb25675Sralph 				continue;
3382cb25675Sralph 			if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
3392cb25675Sralph 				error("%s/%s: Name too long\n", target,
3402cb25675Sralph 					dp->d_name);
3412cb25675Sralph 				continue;
3422cb25675Sralph 			}
3432cb25675Sralph 			tp = otp;
3442cb25675Sralph 			*tp++ = '/';
3452cb25675Sralph 			cp = dp->d_name;
3462cb25675Sralph 			while (*tp++ = *cp++)
3472cb25675Sralph 				;
3482cb25675Sralph 			tp--;
3492cb25675Sralph 			sendf(dp->d_name, opts);
3502cb25675Sralph 		}
3512cb25675Sralph 		closedir(d);
3522cb25675Sralph 		(void) write(rem, "E\n", 2);
3532cb25675Sralph 		(void) response();
3542cb25675Sralph 		tp = otp;
3552cb25675Sralph 		*tp = '\0';
3562cb25675Sralph 		return;
3572cb25675Sralph 
3582cb25675Sralph 	case S_IFLNK:
3592cb25675Sralph 		if (u != 1)
3602cb25675Sralph 			opts |= COMPARE;
36126ce117eSsklower 		if (stb.st_nlink > 1) {
36226ce117eSsklower 			struct linkbuf *lp;
36326ce117eSsklower 
36426ce117eSsklower 			if ((lp = savelink(&stb)) != NULL) {
36526ce117eSsklower 				/* install link */
36626ce117eSsklower 				if (*lp->target == 0)
36726ce117eSsklower 				(void) sprintf(buf, "k%o %s %s\n", opts,
36826ce117eSsklower 					lp->pathname, rname);
36926ce117eSsklower 				else
37026ce117eSsklower 				(void) sprintf(buf, "k%o %s/%s %s\n", opts,
37126ce117eSsklower 					lp->target, lp->pathname, rname);
37226ce117eSsklower 				if (debug)
37326ce117eSsklower 					printf("buf = %s", buf);
37426ce117eSsklower 				(void) write(rem, buf, strlen(buf));
37526ce117eSsklower 				(void) response();
37626ce117eSsklower 				return;
37726ce117eSsklower 			}
37826ce117eSsklower 		}
379e980046fSralph 		(void) sprintf(buf, "K%o %o %ld %ld %s %s %s\n", opts,
3802cb25675Sralph 			stb.st_mode & 07777, stb.st_size, stb.st_mtime,
38126ce117eSsklower 			protoname(), protogroup(), rname);
3822cb25675Sralph 		if (debug)
3832cb25675Sralph 			printf("buf = %s", buf);
3842cb25675Sralph 		(void) write(rem, buf, strlen(buf));
3852cb25675Sralph 		if (response() < 0)
3862cb25675Sralph 			return;
3872cb25675Sralph 		sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
3882cb25675Sralph 		(void) write(rem, buf, stb.st_size);
3892cb25675Sralph 		if (debug)
390e157bfacSbostic 			printf("readlink = %.*s\n", (int)stb.st_size, buf);
3912cb25675Sralph 		goto done;
3922cb25675Sralph 
393978e44c9Sralph 	case S_IFREG:
394978e44c9Sralph 		break;
395978e44c9Sralph 
396978e44c9Sralph 	default:
3972cb25675Sralph 		error("%s: not a file or directory\n", target);
398978e44c9Sralph 		return;
399978e44c9Sralph 	}
400978e44c9Sralph 
401156664fbSralph 	if (u == 2) {
4026d401060Sralph 		if (opts & VERIFY) {
4036d401060Sralph 			log(lfp, "need to update: %s\n", target);
4046d401060Sralph 			goto dospecial;
405156664fbSralph 		}
4066d401060Sralph 		log(lfp, "updating: %s\n", target);
4076d401060Sralph 	}
4082cb25675Sralph 
4092cb25675Sralph 	if (stb.st_nlink > 1) {
4102cb25675Sralph 		struct linkbuf *lp;
4112cb25675Sralph 
4122cb25675Sralph 		if ((lp = savelink(&stb)) != NULL) {
4132cb25675Sralph 			/* install link */
41426ce117eSsklower 			if (*lp->target == 0)
4152cb25675Sralph 			(void) sprintf(buf, "k%o %s %s\n", opts,
4162cb25675Sralph 				lp->pathname, rname);
41726ce117eSsklower 			else
41826ce117eSsklower 			(void) sprintf(buf, "k%o %s/%s %s\n", opts,
41926ce117eSsklower 				lp->target, lp->pathname, rname);
4202cb25675Sralph 			if (debug)
4212cb25675Sralph 				printf("buf = %s", buf);
4222cb25675Sralph 			(void) write(rem, buf, strlen(buf));
4232cb25675Sralph 			(void) response();
4242cb25675Sralph 			return;
4252cb25675Sralph 		}
4262cb25675Sralph 	}
427978e44c9Sralph 
42801de96ecSralph 	if ((f = open(target, 0)) < 0) {
42901de96ecSralph 		error("%s: %s\n", target, sys_errlist[errno]);
430d49851feSralph 		return;
431d49851feSralph 	}
432e980046fSralph 	(void) sprintf(buf, "R%o %o %ld %ld %s %s %s\n", opts,
433ad3d87d9Sralph 		stb.st_mode & 07777, stb.st_size, stb.st_mtime,
43426ce117eSsklower 		protoname(), protogroup(), rname);
435978e44c9Sralph 	if (debug)
436978e44c9Sralph 		printf("buf = %s", buf);
437978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
438978e44c9Sralph 	if (response() < 0) {
439978e44c9Sralph 		(void) close(f);
440978e44c9Sralph 		return;
441978e44c9Sralph 	}
442978e44c9Sralph 	sizerr = 0;
443978e44c9Sralph 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
444978e44c9Sralph 		int amt = BUFSIZ;
445978e44c9Sralph 		if (i + amt > stb.st_size)
446978e44c9Sralph 			amt = stb.st_size - i;
447978e44c9Sralph 		if (sizerr == 0 && read(f, buf, amt) != amt)
448978e44c9Sralph 			sizerr = 1;
449978e44c9Sralph 		(void) write(rem, buf, amt);
450978e44c9Sralph 	}
451978e44c9Sralph 	(void) close(f);
4522cb25675Sralph done:
4536d401060Sralph 	if (sizerr) {
45401de96ecSralph 		error("%s: file changed size\n", target);
4556d401060Sralph 		err();
4566d401060Sralph 	} else
4576d401060Sralph 		ack();
4582cb25675Sralph 	f = response();
4592cb25675Sralph 	if (f < 0 || f == 0 && (opts & COMPARE))
4606d401060Sralph 		return;
4616d401060Sralph dospecial:
46258fc31f7Sralph 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
463aca7acf4Sralph 		if (sc->sc_type != SPECIAL)
4646d401060Sralph 			continue;
465e980046fSralph 		if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
4666d401060Sralph 			continue;
467aca7acf4Sralph 		log(lfp, "special \"%s\"\n", sc->sc_name);
4686d401060Sralph 		if (opts & VERIFY)
4696d401060Sralph 			continue;
470e980046fSralph 		(void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
4716d401060Sralph 		if (debug)
4726d401060Sralph 			printf("buf = %s", buf);
4736d401060Sralph 		(void) write(rem, buf, strlen(buf));
4746d401060Sralph 		while (response() > 0)
4756d401060Sralph 			;
4766d401060Sralph 	}
477978e44c9Sralph }
478978e44c9Sralph 
4792cb25675Sralph struct linkbuf *
4802cb25675Sralph savelink(stp)
4812cb25675Sralph 	struct stat *stp;
482978e44c9Sralph {
4832cb25675Sralph 	struct linkbuf *lp;
4842cb25675Sralph 	int found = 0;
485978e44c9Sralph 
4862cb25675Sralph 	for (lp = ihead; lp != NULL; lp = lp->nextp)
4872cb25675Sralph 		if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
4882cb25675Sralph 			lp->count--;
4892cb25675Sralph 			return(lp);
490978e44c9Sralph 		}
4912cb25675Sralph 	lp = (struct linkbuf *) malloc(sizeof(*lp));
4922cb25675Sralph 	if (lp == NULL)
4932cb25675Sralph 		log(lfp, "out of memory, link information lost\n");
4942cb25675Sralph 	else {
4952cb25675Sralph 		lp->nextp = ihead;
4962cb25675Sralph 		ihead = lp;
4972cb25675Sralph 		lp->inum = stp->st_ino;
4982cb25675Sralph 		lp->devnum = stp->st_dev;
4992cb25675Sralph 		lp->count = stp->st_nlink - 1;
5002cb25675Sralph 		strcpy(lp->pathname, target);
50126ce117eSsklower 		if (Tdest)
50226ce117eSsklower 			strcpy(lp->target, Tdest);
50326ce117eSsklower 		else
50426ce117eSsklower 			*lp->target = 0;
505978e44c9Sralph 	}
5062cb25675Sralph 	return(NULL);
507978e44c9Sralph }
508978e44c9Sralph 
509978e44c9Sralph /*
510978e44c9Sralph  * Check to see if file needs to be updated on the remote machine.
51101de96ecSralph  * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
51201de96ecSralph  * and 3 if comparing binaries to determine if out of date.
513978e44c9Sralph  */
5142cb25675Sralph update(rname, opts, stp)
51501de96ecSralph 	char *rname;
516ad3d87d9Sralph 	int opts;
5172cb25675Sralph 	struct stat *stp;
518978e44c9Sralph {
51901de96ecSralph 	register char *cp, *s;
520978e44c9Sralph 	register off_t size;
521978e44c9Sralph 	register time_t mtime;
522978e44c9Sralph 
523978e44c9Sralph 	if (debug)
5242cb25675Sralph 		printf("update(%s, %x, %x)\n", rname, opts, stp);
525978e44c9Sralph 
526978e44c9Sralph 	/*
527978e44c9Sralph 	 * Check to see if the file exists on the remote machine.
528978e44c9Sralph 	 */
52901de96ecSralph 	(void) sprintf(buf, "Q%s\n", rname);
530978e44c9Sralph 	if (debug)
531978e44c9Sralph 		printf("buf = %s", buf);
532978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
53326ce117eSsklower again:
53401de96ecSralph 	cp = s = buf;
535978e44c9Sralph 	do {
536978e44c9Sralph 		if (read(rem, cp, 1) != 1)
537d58796b1Sralph 			lostconn();
538d49851feSralph 	} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
539978e44c9Sralph 
54001de96ecSralph 	switch (*s++) {
541978e44c9Sralph 	case 'Y':
542978e44c9Sralph 		break;
543978e44c9Sralph 
544d49851feSralph 	case 'N':  /* file doesn't exist so install it */
545978e44c9Sralph 		return(1);
546978e44c9Sralph 
547978e44c9Sralph 	case '\1':
548aca7acf4Sralph 		nerrs++;
54901de96ecSralph 		if (*s != '\n') {
550d49851feSralph 			if (!iamremote) {
551d49851feSralph 				fflush(stdout);
55201de96ecSralph 				(void) write(2, s, cp - s);
553d49851feSralph 			}
554d49851feSralph 			if (lfp != NULL)
55501de96ecSralph 				(void) fwrite(s, 1, cp - s, lfp);
556d49851feSralph 		}
557978e44c9Sralph 		return(0);
558978e44c9Sralph 
55926ce117eSsklower 	case '\3':
56026ce117eSsklower 		*--cp = '\0';
56126ce117eSsklower 		if (lfp != NULL)
56226ce117eSsklower 			log(lfp, "update: note: %s\n", s);
56326ce117eSsklower 		goto again;
56426ce117eSsklower 
565978e44c9Sralph 	default:
56615867ad7Sralph 		*--cp = '\0';
56726ce117eSsklower 		error("update: unexpected response '%s'\n", s);
568978e44c9Sralph 		return(0);
569978e44c9Sralph 	}
570978e44c9Sralph 
57101de96ecSralph 	if (*s == '\n')
572d49851feSralph 		return(2);
573978e44c9Sralph 
574156664fbSralph 	if (opts & COMPARE)
575156664fbSralph 		return(3);
576156664fbSralph 
577978e44c9Sralph 	size = 0;
57801de96ecSralph 	while (isdigit(*s))
57901de96ecSralph 		size = size * 10 + (*s++ - '0');
58001de96ecSralph 	if (*s++ != ' ') {
58101de96ecSralph 		error("update: size not delimited\n");
582978e44c9Sralph 		return(0);
583978e44c9Sralph 	}
584978e44c9Sralph 	mtime = 0;
58501de96ecSralph 	while (isdigit(*s))
58601de96ecSralph 		mtime = mtime * 10 + (*s++ - '0');
58701de96ecSralph 	if (*s != '\n') {
58801de96ecSralph 		error("update: mtime not delimited\n");
589978e44c9Sralph 		return(0);
590978e44c9Sralph 	}
591978e44c9Sralph 	/*
592978e44c9Sralph 	 * File needs to be updated?
593978e44c9Sralph 	 */
594ad3d87d9Sralph 	if (opts & YOUNGER) {
5952cb25675Sralph 		if (stp->st_mtime == mtime)
59665a0d230Sralph 			return(0);
5972cb25675Sralph 		if (stp->st_mtime < mtime) {
59801de96ecSralph 			log(lfp, "Warning: %s: remote copy is newer\n", target);
59965a0d230Sralph 			return(0);
60065a0d230Sralph 		}
6012cb25675Sralph 	} else if (stp->st_mtime == mtime && stp->st_size == size)
602978e44c9Sralph 		return(0);
603d49851feSralph 	return(2);
604978e44c9Sralph }
605978e44c9Sralph 
606978e44c9Sralph /*
607978e44c9Sralph  * Query. Check to see if file exists. Return one of the following:
608978e44c9Sralph  *	N\n		- doesn't exist
609978e44c9Sralph  *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
6102cb25675Sralph  *	Y\n		- exists and its a directory or symbolic link
611978e44c9Sralph  *	^Aerror message\n
612978e44c9Sralph  */
61301de96ecSralph query(name)
614978e44c9Sralph 	char *name;
615978e44c9Sralph {
616978e44c9Sralph 	struct stat stb;
617978e44c9Sralph 
618978e44c9Sralph 	if (catname)
619978e44c9Sralph 		(void) sprintf(tp, "/%s", name);
62065a0d230Sralph 
6212cb25675Sralph 	if (lstat(target, &stb) < 0) {
622e980046fSralph 		if (errno == ENOENT)
623978e44c9Sralph 			(void) write(rem, "N\n", 2);
624e980046fSralph 		else
625e980046fSralph 			error("%s:%s: %s\n", host, target, sys_errlist[errno]);
626978e44c9Sralph 		*tp = '\0';
627978e44c9Sralph 		return;
628978e44c9Sralph 	}
629978e44c9Sralph 
630978e44c9Sralph 	switch (stb.st_mode & S_IFMT) {
631978e44c9Sralph 	case S_IFREG:
632e980046fSralph 		(void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
633978e44c9Sralph 		(void) write(rem, buf, strlen(buf));
634978e44c9Sralph 		break;
635978e44c9Sralph 
6362cb25675Sralph 	case S_IFLNK:
637978e44c9Sralph 	case S_IFDIR:
638978e44c9Sralph 		(void) write(rem, "Y\n", 2);
639978e44c9Sralph 		break;
640978e44c9Sralph 
641978e44c9Sralph 	default:
6422cb25675Sralph 		error("%s: not a file or directory\n", name);
643978e44c9Sralph 		break;
644978e44c9Sralph 	}
645978e44c9Sralph 	*tp = '\0';
646978e44c9Sralph }
647978e44c9Sralph 
6482cb25675Sralph recvf(cmd, type)
649978e44c9Sralph 	char *cmd;
6502cb25675Sralph 	int type;
651978e44c9Sralph {
652978e44c9Sralph 	register char *cp;
65344f1d1c2Sralph 	int f, mode, opts, wrerr, olderrno;
654978e44c9Sralph 	off_t i, size;
655978e44c9Sralph 	time_t mtime;
656978e44c9Sralph 	struct stat stb;
657978e44c9Sralph 	struct timeval tvp[2];
658e980046fSralph 	char *owner, *group;
659978e44c9Sralph 	char new[BUFSIZ];
660d49851feSralph 	extern char *tmpname;
661978e44c9Sralph 
662ad3d87d9Sralph 	cp = cmd;
663156664fbSralph 	opts = 0;
664156664fbSralph 	while (*cp >= '0' && *cp <= '7')
665156664fbSralph 		opts = (opts << 3) | (*cp++ - '0');
666ad3d87d9Sralph 	if (*cp++ != ' ') {
66701de96ecSralph 		error("recvf: options not delimited\n");
668ad3d87d9Sralph 		return;
669ad3d87d9Sralph 	}
670978e44c9Sralph 	mode = 0;
671156664fbSralph 	while (*cp >= '0' && *cp <= '7')
672ad3d87d9Sralph 		mode = (mode << 3) | (*cp++ - '0');
673978e44c9Sralph 	if (*cp++ != ' ') {
67401de96ecSralph 		error("recvf: mode not delimited\n");
675978e44c9Sralph 		return;
676978e44c9Sralph 	}
677978e44c9Sralph 	size = 0;
678978e44c9Sralph 	while (isdigit(*cp))
679978e44c9Sralph 		size = size * 10 + (*cp++ - '0');
680978e44c9Sralph 	if (*cp++ != ' ') {
68101de96ecSralph 		error("recvf: size not delimited\n");
682978e44c9Sralph 		return;
683978e44c9Sralph 	}
684978e44c9Sralph 	mtime = 0;
685978e44c9Sralph 	while (isdigit(*cp))
686978e44c9Sralph 		mtime = mtime * 10 + (*cp++ - '0');
687978e44c9Sralph 	if (*cp++ != ' ') {
68801de96ecSralph 		error("recvf: mtime not delimited\n");
689978e44c9Sralph 		return;
690978e44c9Sralph 	}
691978e44c9Sralph 	owner = cp;
692978e44c9Sralph 	while (*cp && *cp != ' ')
693978e44c9Sralph 		cp++;
694978e44c9Sralph 	if (*cp != ' ') {
69501de96ecSralph 		error("recvf: owner name not delimited\n");
696978e44c9Sralph 		return;
697978e44c9Sralph 	}
698978e44c9Sralph 	*cp++ = '\0';
699978e44c9Sralph 	group = cp;
700978e44c9Sralph 	while (*cp && *cp != ' ')
701978e44c9Sralph 		cp++;
702978e44c9Sralph 	if (*cp != ' ') {
70301de96ecSralph 		error("recvf: group name not delimited\n");
704978e44c9Sralph 		return;
705978e44c9Sralph 	}
706978e44c9Sralph 	*cp++ = '\0';
707978e44c9Sralph 
7082cb25675Sralph 	if (type == S_IFDIR) {
70965a0d230Sralph 		if (catname >= sizeof(stp)) {
7102cb25675Sralph 			error("%s:%s: too many directory levels\n",
7112cb25675Sralph 				host, target);
71265a0d230Sralph 			return;
71365a0d230Sralph 		}
71465a0d230Sralph 		stp[catname] = tp;
715978e44c9Sralph 		if (catname++) {
716978e44c9Sralph 			*tp++ = '/';
717978e44c9Sralph 			while (*tp++ = *cp++)
718978e44c9Sralph 				;
719978e44c9Sralph 			tp--;
720978e44c9Sralph 		}
721ad3d87d9Sralph 		if (opts & VERIFY) {
7226d401060Sralph 			ack();
723d49851feSralph 			return;
724d49851feSralph 		}
7252cb25675Sralph 		if (lstat(target, &stb) == 0) {
7266d401060Sralph 			if (ISDIR(stb.st_mode)) {
727e980046fSralph 				if ((stb.st_mode & 07777) == mode) {
7286d401060Sralph 					ack();
729978e44c9Sralph 					return;
730d49851feSralph 				}
7313e1cad88Sralph 				buf[0] = '\0';
7323e1cad88Sralph 				(void) sprintf(buf + 1,
733d66beb1aSlepreau 					"%s: Warning: remote mode %o != local mode %o\n",
734d66beb1aSlepreau 					target, stb.st_mode & 07777, mode);
7353e1cad88Sralph 				(void) write(rem, buf, strlen(buf + 1) + 1);
7363e1cad88Sralph 				return;
7373e1cad88Sralph 			}
738e980046fSralph 			errno = ENOTDIR;
739e980046fSralph 		} else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
740e980046fSralph 		    chkparent(target) == 0 && mkdir(target, mode) == 0)) {
741e980046fSralph 			if (chog(target, owner, group, mode) == 0)
7426d401060Sralph 				ack();
7436d401060Sralph 			return;
7446d401060Sralph 		}
745e980046fSralph 		error("%s:%s: %s\n", host, target, sys_errlist[errno]);
7466d401060Sralph 		tp = stp[--catname];
7476d401060Sralph 		*tp = '\0';
748978e44c9Sralph 		return;
749978e44c9Sralph 	}
750978e44c9Sralph 
751978e44c9Sralph 	if (catname)
752978e44c9Sralph 		(void) sprintf(tp, "/%s", cp);
753978e44c9Sralph 	cp = rindex(target, '/');
754978e44c9Sralph 	if (cp == NULL)
755e980046fSralph 		strcpy(new, tmpname);
756e980046fSralph 	else if (cp == target)
757e980046fSralph 		(void) sprintf(new, "/%s", tmpname);
758e980046fSralph 	else {
759978e44c9Sralph 		*cp = '\0';
760e980046fSralph 		(void) sprintf(new, "%s/%s", target, tmpname);
761d49851feSralph 		*cp = '/';
762e980046fSralph 	}
7632cb25675Sralph 
7642cb25675Sralph 	if (type == S_IFLNK) {
7652cb25675Sralph 		int j;
7662cb25675Sralph 
7672cb25675Sralph 		ack();
7682cb25675Sralph 		cp = buf;
7692cb25675Sralph 		for (i = 0; i < size; i += j) {
7702cb25675Sralph 			if ((j = read(rem, cp, size - i)) <= 0)
7712cb25675Sralph 				cleanup();
7722cb25675Sralph 			cp += j;
7732cb25675Sralph 		}
7742cb25675Sralph 		*cp = '\0';
775e980046fSralph 		if (response() < 0) {
776e980046fSralph 			err();
777e980046fSralph 			return;
778e980046fSralph 		}
77975527c89Sralph 		if (symlink(buf, new) < 0) {
78075527c89Sralph 			if (errno != ENOENT || chkparent(new) < 0 ||
78175527c89Sralph 			    symlink(buf, new) < 0)
782e980046fSralph 				goto badn;
78375527c89Sralph 		}
7842cb25675Sralph 		mode &= 0777;
7852cb25675Sralph 		if (opts & COMPARE) {
7862cb25675Sralph 			char tbuf[BUFSIZ];
7872cb25675Sralph 
78875527c89Sralph 			if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
78975527c89Sralph 			    i == size && strncmp(buf, tbuf, size) == 0) {
7902cb25675Sralph 				(void) unlink(new);
7912cb25675Sralph 				ack();
7922cb25675Sralph 				return;
7932cb25675Sralph 			}
79415867ad7Sralph 			if (opts & VERIFY)
79515867ad7Sralph 				goto differ;
7962cb25675Sralph 		}
7972cb25675Sralph 		goto fixup;
7982cb25675Sralph 	}
7992cb25675Sralph 
800e980046fSralph 	if ((f = creat(new, mode)) < 0) {
801e980046fSralph 		if (errno != ENOENT || chkparent(new) < 0 ||
802e980046fSralph 		    (f = creat(new, mode)) < 0)
80375527c89Sralph 			goto badn;
804e980046fSralph 	}
805e980046fSralph 
8066d401060Sralph 	ack();
807978e44c9Sralph 	wrerr = 0;
808978e44c9Sralph 	for (i = 0; i < size; i += BUFSIZ) {
809978e44c9Sralph 		int amt = BUFSIZ;
810978e44c9Sralph 
81165a0d230Sralph 		cp = buf;
812978e44c9Sralph 		if (i + amt > size)
813978e44c9Sralph 			amt = size - i;
814978e44c9Sralph 		do {
815978e44c9Sralph 			int j = read(rem, cp, amt);
816978e44c9Sralph 
817d49851feSralph 			if (j <= 0) {
818d49851feSralph 				(void) close(f);
819d49851feSralph 				(void) unlink(new);
820978e44c9Sralph 				cleanup();
821d49851feSralph 			}
822978e44c9Sralph 			amt -= j;
823978e44c9Sralph 			cp += j;
824978e44c9Sralph 		} while (amt > 0);
825978e44c9Sralph 		amt = BUFSIZ;
826978e44c9Sralph 		if (i + amt > size)
827978e44c9Sralph 			amt = size - i;
828978e44c9Sralph 		if (wrerr == 0 && write(f, buf, amt) != amt) {
829978e44c9Sralph 			olderrno = errno;
830978e44c9Sralph 			wrerr++;
831978e44c9Sralph 		}
832978e44c9Sralph 	}
833156664fbSralph 	(void) close(f);
834e980046fSralph 	if (response() < 0) {
835e980046fSralph 		err();
836e980046fSralph 		(void) unlink(new);
837e980046fSralph 		return;
838e980046fSralph 	}
839978e44c9Sralph 	if (wrerr) {
840ed12aba6Sralph 		error("%s:%s: %s\n", host, new, sys_errlist[olderrno]);
841d49851feSralph 		(void) unlink(new);
842978e44c9Sralph 		return;
843978e44c9Sralph 	}
844156664fbSralph 	if (opts & COMPARE) {
845156664fbSralph 		FILE *f1, *f2;
846156664fbSralph 		int c;
847156664fbSralph 
848156664fbSralph 		if ((f1 = fopen(target, "r")) == NULL)
849e980046fSralph 			goto badt;
85026ce117eSsklower 		if ((f2 = fopen(new, "r")) == NULL) {
85126ce117eSsklower 		badn:
85226ce117eSsklower 			error("%s:%s: %s\n", host, new, sys_errlist[errno]);
85326ce117eSsklower 			(void) unlink(new);
85426ce117eSsklower 			return;
85526ce117eSsklower 		}
856156664fbSralph 		while ((c = getc(f1)) == getc(f2))
857156664fbSralph 			if (c == EOF) {
858156664fbSralph 				(void) fclose(f1);
859156664fbSralph 				(void) fclose(f2);
860156664fbSralph 				(void) unlink(new);
8616d401060Sralph 				ack();
862156664fbSralph 				return;
863156664fbSralph 			}
864156664fbSralph 		(void) fclose(f1);
865156664fbSralph 		(void) fclose(f2);
866156664fbSralph 		if (opts & VERIFY) {
8672cb25675Sralph 		differ:
868156664fbSralph 			(void) unlink(new);
869156664fbSralph 			buf[0] = '\0';
870e980046fSralph 			(void) sprintf(buf + 1, "need to update: %s\n",target);
871156664fbSralph 			(void) write(rem, buf, strlen(buf + 1) + 1);
872156664fbSralph 			return;
873156664fbSralph 		}
874156664fbSralph 	}
875978e44c9Sralph 
876978e44c9Sralph 	/*
877978e44c9Sralph 	 * Set last modified time
878978e44c9Sralph 	 */
8792cb25675Sralph 	tvp[0].tv_sec = stb.st_atime;	/* old atime from target */
880978e44c9Sralph 	tvp[0].tv_usec = 0;
881978e44c9Sralph 	tvp[1].tv_sec = mtime;
882978e44c9Sralph 	tvp[1].tv_usec = 0;
88398722ed6Sralph 	if (utimes(new, tvp) < 0) {
88426ce117eSsklower 		note("%s:utimes failed %s: %s\n", host, new, sys_errlist[errno]);
88598722ed6Sralph 	}
88698722ed6Sralph 	if (chog(new, owner, group, mode) < 0) {
887d49851feSralph 		(void) unlink(new);
888978e44c9Sralph 		return;
889978e44c9Sralph 	}
89075527c89Sralph fixup:
891978e44c9Sralph 	if (rename(new, target) < 0) {
892e980046fSralph badt:
8932cb25675Sralph 		error("%s:%s: %s\n", host, target, sys_errlist[errno]);
894d49851feSralph 		(void) unlink(new);
895978e44c9Sralph 		return;
896978e44c9Sralph 	}
897156664fbSralph 	if (opts & COMPARE) {
898156664fbSralph 		buf[0] = '\0';
899d66beb1aSlepreau 		(void) sprintf(buf + 1, "updated %s\n", target);
900156664fbSralph 		(void) write(rem, buf, strlen(buf + 1) + 1);
901156664fbSralph 	} else
9026d401060Sralph 		ack();
903978e44c9Sralph }
904978e44c9Sralph 
905978e44c9Sralph /*
9062cb25675Sralph  * Creat a hard link to existing file.
9072cb25675Sralph  */
9082cb25675Sralph hardlink(cmd)
9092cb25675Sralph 	char *cmd;
9102cb25675Sralph {
9112cb25675Sralph 	register char *cp;
9122cb25675Sralph 	struct stat stb;
9132cb25675Sralph 	char *oldname;
9142cb25675Sralph 	int opts, exists = 0;
9152cb25675Sralph 
9162cb25675Sralph 	cp = cmd;
9172cb25675Sralph 	opts = 0;
9182cb25675Sralph 	while (*cp >= '0' && *cp <= '7')
9192cb25675Sralph 		opts = (opts << 3) | (*cp++ - '0');
9202cb25675Sralph 	if (*cp++ != ' ') {
9212cb25675Sralph 		error("hardlink: options not delimited\n");
9222cb25675Sralph 		return;
9232cb25675Sralph 	}
9242cb25675Sralph 	oldname = cp;
9252cb25675Sralph 	while (*cp && *cp != ' ')
9262cb25675Sralph 		cp++;
9272cb25675Sralph 	if (*cp != ' ') {
9282cb25675Sralph 		error("hardlink: oldname name not delimited\n");
9292cb25675Sralph 		return;
9302cb25675Sralph 	}
9312cb25675Sralph 	*cp++ = '\0';
9322cb25675Sralph 
93326ce117eSsklower 	if (catname) {
9342cb25675Sralph 		(void) sprintf(tp, "/%s", cp);
93526ce117eSsklower 	}
9362cb25675Sralph 	if (lstat(target, &stb) == 0) {
93726ce117eSsklower 		int mode = stb.st_mode & S_IFMT;
93826ce117eSsklower 		if (mode != S_IFREG && mode != S_IFLNK) {
9392cb25675Sralph 			error("%s:%s: not a regular file\n", host, target);
9402cb25675Sralph 			return;
9412cb25675Sralph 		}
9422cb25675Sralph 		exists = 1;
9432cb25675Sralph 	}
94426ce117eSsklower 	if (chkparent(target) < 0 ) {
94526ce117eSsklower 		error("%s:%s: %s (no parent)\n",
94626ce117eSsklower 			host, target, sys_errlist[errno]);
94726ce117eSsklower 		return;
94826ce117eSsklower 	}
94926ce117eSsklower 	if (exists && (unlink(target) < 0)) {
95026ce117eSsklower 		error("%s:%s: %s (unlink)\n",
95126ce117eSsklower 			host, target, sys_errlist[errno]);
95226ce117eSsklower 		return;
95326ce117eSsklower 	}
95426ce117eSsklower 	if (link(oldname, target) < 0) {
95526ce117eSsklower 		error("%s:can't link %s to %s\n",
95626ce117eSsklower 			host, target, oldname);
9572cb25675Sralph 		return;
9582cb25675Sralph 	}
9592cb25675Sralph 	ack();
9602cb25675Sralph }
9612cb25675Sralph 
9622cb25675Sralph /*
963e980046fSralph  * Check to see if parent directory exists and create one if not.
96465a0d230Sralph  */
96565a0d230Sralph chkparent(name)
96665a0d230Sralph 	char *name;
96765a0d230Sralph {
968e980046fSralph 	register char *cp;
969e980046fSralph 	struct stat stb;
97065a0d230Sralph 
97165a0d230Sralph 	cp = rindex(name, '/');
972e980046fSralph 	if (cp == NULL || cp == name)
973e980046fSralph 		return(0);
97465a0d230Sralph 	*cp = '\0';
975e980046fSralph 	if (lstat(name, &stb) < 0) {
976e980046fSralph 		if (errno == ENOENT && chkparent(name) >= 0 &&
977e980046fSralph 		    mkdir(name, 0777 & ~oumask) >= 0) {
97865a0d230Sralph 			*cp = '/';
97965a0d230Sralph 			return(0);
98065a0d230Sralph 		}
981e980046fSralph 	} else if (ISDIR(stb.st_mode)) {
98265a0d230Sralph 		*cp = '/';
98365a0d230Sralph 		return(0);
98465a0d230Sralph 	}
98565a0d230Sralph 	*cp = '/';
98665a0d230Sralph 	return(-1);
98765a0d230Sralph }
98865a0d230Sralph 
98965a0d230Sralph /*
9906d401060Sralph  * Change owner, group and mode of file.
991978e44c9Sralph  */
992d49851feSralph chog(file, owner, group, mode)
993978e44c9Sralph 	char *file, *owner, *group;
994d49851feSralph 	int mode;
995978e44c9Sralph {
996978e44c9Sralph 	register int i;
997978e44c9Sralph 	int uid, gid;
998e980046fSralph 	extern char user[];
999e980046fSralph 	extern int userid;
1000978e44c9Sralph 
1001978e44c9Sralph 	uid = userid;
1002978e44c9Sralph 	if (userid == 0) {
100326ce117eSsklower 		if (*owner == ':') {
100426ce117eSsklower 			uid = atoi(owner + 1);
100526ce117eSsklower 		} else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
100601de96ecSralph 			if ((pw = getpwnam(owner)) == NULL) {
1007d49851feSralph 				if (mode & 04000) {
100826ce117eSsklower 					note("%s:%s: unknown login name, clearing setuid",
10092cb25675Sralph 						host, owner);
101026ce117eSsklower 					mode &= ~04000;
101126ce117eSsklower 					uid = 0;
1012978e44c9Sralph 				}
1013d49851feSralph 			} else
101401de96ecSralph 				uid = pw->pw_uid;
1015d49851feSralph 		} else
101601de96ecSralph 			uid = pw->pw_uid;
101726ce117eSsklower 		if (*group == ':') {
101826ce117eSsklower 			gid = atoi(group + 1);
101926ce117eSsklower 			goto ok;
102026ce117eSsklower 		}
10216d401060Sralph 	} else if ((mode & 04000) && strcmp(user, owner) != 0)
10226d401060Sralph 		mode &= ~04000;
1023e980046fSralph 	gid = -1;
102401de96ecSralph 	if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
102526ce117eSsklower 		if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
102626ce117eSsklower 		   || ((gr = getgrnam(group)) == NULL)) {
1027d49851feSralph 			if (mode & 02000) {
102826ce117eSsklower 				note("%s:%s: unknown group", host, group);
102926ce117eSsklower 				mode &= ~02000;
1030978e44c9Sralph 			}
1031d49851feSralph 		} else
103201de96ecSralph 			gid = gr->gr_gid;
1033d49851feSralph 	} else
103401de96ecSralph 		gid = gr->gr_gid;
1035e980046fSralph 	if (userid && gid >= 0) {
103626ce117eSsklower 		if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
103701de96ecSralph 			if (!(strcmp(user, gr->gr_mem[i])))
1038978e44c9Sralph 				goto ok;
10396d401060Sralph 		mode &= ~02000;
1040e980046fSralph 		gid = -1;
1041978e44c9Sralph 	}
1042978e44c9Sralph ok:
1043e980046fSralph 	if (userid)
1044e980046fSralph 		setreuid(userid, 0);
1045e980046fSralph 	if (chown(file, uid, gid) < 0 ||
1046e980046fSralph 	    (mode & 06000) && chmod(file, mode) < 0) {
104726ce117eSsklower 		note("%s: chown or chmod failed: file %s:  %s",
104826ce117eSsklower 			     host, file, sys_errlist[errno]);
1049978e44c9Sralph 	}
1050e980046fSralph 	if (userid)
1051e980046fSralph 		setreuid(0, userid);
1052978e44c9Sralph 	return(0);
1053978e44c9Sralph }
1054978e44c9Sralph 
1055c27256c0Sralph /*
1056c27256c0Sralph  * Check for files on the machine being updated that are not on the master
1057c27256c0Sralph  * machine and remove them.
1058c27256c0Sralph  */
10596d401060Sralph rmchk(opts)
1060c27256c0Sralph 	int opts;
1061c27256c0Sralph {
106201de96ecSralph 	register char *cp, *s;
1063c27256c0Sralph 	struct stat stb;
1064c27256c0Sralph 
1065c27256c0Sralph 	if (debug)
10666d401060Sralph 		printf("rmchk()\n");
1067156664fbSralph 
1068c27256c0Sralph 	/*
1069c27256c0Sralph 	 * Tell the remote to clean the files from the last directory sent.
1070c27256c0Sralph 	 */
10716d401060Sralph 	(void) sprintf(buf, "C%o\n", opts & VERIFY);
1072c27256c0Sralph 	if (debug)
1073c27256c0Sralph 		printf("buf = %s", buf);
1074c27256c0Sralph 	(void) write(rem, buf, strlen(buf));
1075c27256c0Sralph 	if (response() < 0)
1076c27256c0Sralph 		return;
1077c27256c0Sralph 	for (;;) {
107801de96ecSralph 		cp = s = buf;
1079c27256c0Sralph 		do {
1080c27256c0Sralph 			if (read(rem, cp, 1) != 1)
1081d58796b1Sralph 				lostconn();
1082c27256c0Sralph 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1083c27256c0Sralph 
108401de96ecSralph 		switch (*s++) {
10856d401060Sralph 		case 'Q': /* Query if file should be removed */
1086156664fbSralph 			/*
1087156664fbSralph 			 * Return the following codes to remove query.
10886d401060Sralph 			 * N\n -- file exists - DON'T remove.
10896d401060Sralph 			 * Y\n -- file doesn't exist - REMOVE.
1090156664fbSralph 			 */
1091c27256c0Sralph 			*--cp = '\0';
109201de96ecSralph 			(void) sprintf(tp, "/%s", s);
1093c27256c0Sralph 			if (debug)
1094c27256c0Sralph 				printf("check %s\n", target);
109558fc31f7Sralph 			if (except(target))
1096c27256c0Sralph 				(void) write(rem, "N\n", 2);
10972cb25675Sralph 			else if (lstat(target, &stb) < 0)
1098c27256c0Sralph 				(void) write(rem, "Y\n", 2);
10996d401060Sralph 			else
11006d401060Sralph 				(void) write(rem, "N\n", 2);
1101c27256c0Sralph 			break;
1102c27256c0Sralph 
1103c27256c0Sralph 		case '\0':
1104c27256c0Sralph 			*--cp = '\0';
110501de96ecSralph 			if (*s != '\0')
110601de96ecSralph 				log(lfp, "%s\n", s);
1107c27256c0Sralph 			break;
1108c27256c0Sralph 
11096d401060Sralph 		case 'E':
11106d401060Sralph 			*tp = '\0';
11116d401060Sralph 			ack();
11126d401060Sralph 			return;
11136d401060Sralph 
1114c27256c0Sralph 		case '\1':
1115c27256c0Sralph 		case '\2':
1116aca7acf4Sralph 			nerrs++;
111701de96ecSralph 			if (*s != '\n') {
1118c27256c0Sralph 				if (!iamremote) {
1119c27256c0Sralph 					fflush(stdout);
112001de96ecSralph 					(void) write(2, s, cp - s);
1121c27256c0Sralph 				}
1122c27256c0Sralph 				if (lfp != NULL)
112301de96ecSralph 					(void) fwrite(s, 1, cp - s, lfp);
1124c27256c0Sralph 			}
1125c27256c0Sralph 			if (buf[0] == '\2')
1126d58796b1Sralph 				lostconn();
1127c27256c0Sralph 			break;
1128c27256c0Sralph 
1129c27256c0Sralph 		default:
11306d401060Sralph 			error("rmchk: unexpected response '%s'\n", buf);
11316d401060Sralph 			err();
1132c27256c0Sralph 		}
1133c27256c0Sralph 	}
1134c27256c0Sralph }
1135c27256c0Sralph 
1136c27256c0Sralph /*
11376d401060Sralph  * Check the current directory (initialized by the 'T' command to server())
1138156664fbSralph  * for extraneous files and remove them.
1139c27256c0Sralph  */
114000fd8e74Sralph clean(cp)
114100fd8e74Sralph 	register char *cp;
1142c27256c0Sralph {
1143c27256c0Sralph 	DIR *d;
11446d401060Sralph 	register struct direct *dp;
1145c27256c0Sralph 	struct stat stb;
11466d401060Sralph 	char *otp;
11476d401060Sralph 	int len, opts;
1148c27256c0Sralph 
11496d401060Sralph 	opts = 0;
11506d401060Sralph 	while (*cp >= '0' && *cp <= '7')
11516d401060Sralph 		opts = (opts << 3) | (*cp++ - '0');
11526d401060Sralph 	if (*cp != '\0') {
11536d401060Sralph 		error("clean: options not delimited\n");
1154c27256c0Sralph 		return;
1155c27256c0Sralph 	}
1156e980046fSralph 	if ((d = opendir(target)) == NULL) {
11572cb25675Sralph 		error("%s:%s: %s\n", host, target, sys_errlist[errno]);
115801de96ecSralph 		return;
115901de96ecSralph 	}
11606d401060Sralph 	ack();
1161c27256c0Sralph 
1162c27256c0Sralph 	otp = tp;
1163c27256c0Sralph 	len = tp - target;
1164c27256c0Sralph 	while (dp = readdir(d)) {
1165c27256c0Sralph 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1166c27256c0Sralph 			continue;
1167c27256c0Sralph 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
11682cb25675Sralph 			error("%s:%s/%s: Name too long\n",
11692cb25675Sralph 				host, target, dp->d_name);
1170c27256c0Sralph 			continue;
1171c27256c0Sralph 		}
1172c27256c0Sralph 		tp = otp;
1173c27256c0Sralph 		*tp++ = '/';
1174c27256c0Sralph 		cp = dp->d_name;;
1175c27256c0Sralph 		while (*tp++ = *cp++)
1176c27256c0Sralph 			;
1177c27256c0Sralph 		tp--;
11782cb25675Sralph 		if (lstat(target, &stb) < 0) {
11792cb25675Sralph 			error("%s:%s: %s\n", host, target, sys_errlist[errno]);
1180c27256c0Sralph 			continue;
1181c27256c0Sralph 		}
11826d401060Sralph 		(void) sprintf(buf, "Q%s\n", dp->d_name);
1183c27256c0Sralph 		(void) write(rem, buf, strlen(buf));
1184c27256c0Sralph 		cp = buf;
1185c27256c0Sralph 		do {
1186c27256c0Sralph 			if (read(rem, cp, 1) != 1)
1187aca7acf4Sralph 				cleanup();
1188c27256c0Sralph 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1189c27256c0Sralph 		*--cp = '\0';
1190c27256c0Sralph 		cp = buf;
11916d401060Sralph 		if (*cp != 'Y')
1192c27256c0Sralph 			continue;
119301de96ecSralph 		if (opts & VERIFY) {
119401de96ecSralph 			cp = buf;
119501de96ecSralph 			*cp++ = '\0';
1196e980046fSralph 			(void) sprintf(cp, "need to remove: %s\n", target);
119701de96ecSralph 			(void) write(rem, buf, strlen(cp) + 1);
119801de96ecSralph 		} else
1199c27256c0Sralph 			remove(&stb);
1200c27256c0Sralph 	}
1201c27256c0Sralph 	closedir(d);
1202c27256c0Sralph 	(void) write(rem, "E\n", 2);
1203c27256c0Sralph 	(void) response();
12046d401060Sralph 	tp = otp;
120501de96ecSralph 	*tp = '\0';
1206c27256c0Sralph }
1207c27256c0Sralph 
120801de96ecSralph /*
120901de96ecSralph  * Remove a file or directory (recursively) and send back an acknowledge
121001de96ecSralph  * or an error message.
121101de96ecSralph  */
12122cb25675Sralph remove(stp)
12132cb25675Sralph 	struct stat *stp;
1214c27256c0Sralph {
1215c27256c0Sralph 	DIR *d;
1216c27256c0Sralph 	struct direct *dp;
1217c27256c0Sralph 	register char *cp;
1218c27256c0Sralph 	struct stat stb;
1219c27256c0Sralph 	char *otp;
1220c27256c0Sralph 	int len;
1221c27256c0Sralph 
12222cb25675Sralph 	switch (stp->st_mode & S_IFMT) {
1223c27256c0Sralph 	case S_IFREG:
12242cb25675Sralph 	case S_IFLNK:
1225c27256c0Sralph 		if (unlink(target) < 0)
1226c27256c0Sralph 			goto bad;
1227c27256c0Sralph 		goto removed;
1228c27256c0Sralph 
1229c27256c0Sralph 	case S_IFDIR:
1230c27256c0Sralph 		break;
1231c27256c0Sralph 
1232c27256c0Sralph 	default:
12332cb25675Sralph 		error("%s:%s: not a plain file\n", host, target);
1234c27256c0Sralph 		return;
1235c27256c0Sralph 	}
1236c27256c0Sralph 
1237e980046fSralph 	if ((d = opendir(target)) == NULL)
1238c27256c0Sralph 		goto bad;
1239c27256c0Sralph 
1240c27256c0Sralph 	otp = tp;
1241c27256c0Sralph 	len = tp - target;
1242c27256c0Sralph 	while (dp = readdir(d)) {
1243c27256c0Sralph 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1244c27256c0Sralph 			continue;
1245c27256c0Sralph 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
12462cb25675Sralph 			error("%s:%s/%s: Name too long\n",
12472cb25675Sralph 				host, target, dp->d_name);
1248c27256c0Sralph 			continue;
1249c27256c0Sralph 		}
1250c27256c0Sralph 		tp = otp;
1251c27256c0Sralph 		*tp++ = '/';
1252c27256c0Sralph 		cp = dp->d_name;;
1253c27256c0Sralph 		while (*tp++ = *cp++)
1254c27256c0Sralph 			;
1255c27256c0Sralph 		tp--;
12562cb25675Sralph 		if (lstat(target, &stb) < 0) {
12572cb25675Sralph 			error("%s:%s: %s\n", host, target, sys_errlist[errno]);
1258c27256c0Sralph 			continue;
1259c27256c0Sralph 		}
1260c27256c0Sralph 		remove(&stb);
1261c27256c0Sralph 	}
1262c27256c0Sralph 	closedir(d);
1263c27256c0Sralph 	tp = otp;
1264c27256c0Sralph 	*tp = '\0';
1265c27256c0Sralph 	if (rmdir(target) < 0) {
1266c27256c0Sralph bad:
12672cb25675Sralph 		error("%s:%s: %s\n", host, target, sys_errlist[errno]);
1268c27256c0Sralph 		return;
1269c27256c0Sralph 	}
1270c27256c0Sralph removed:
1271c27256c0Sralph 	cp = buf;
1272c27256c0Sralph 	*cp++ = '\0';
1273c27256c0Sralph 	(void) sprintf(cp, "removed %s\n", target);
1274c27256c0Sralph 	(void) write(rem, buf, strlen(cp) + 1);
1275c27256c0Sralph }
1276c27256c0Sralph 
12776d401060Sralph /*
12786d401060Sralph  * Execute a shell command to handle special cases.
12796d401060Sralph  */
12806d401060Sralph dospecial(cmd)
12816d401060Sralph 	char *cmd;
12826d401060Sralph {
12836d401060Sralph 	int fd[2], status, pid, i;
12846d401060Sralph 	register char *cp, *s;
12856d401060Sralph 	char sbuf[BUFSIZ];
1286e980046fSralph 	extern int userid, groupid;
12876d401060Sralph 
12886d401060Sralph 	if (pipe(fd) < 0) {
12896d401060Sralph 		error("%s\n", sys_errlist[errno]);
12906d401060Sralph 		return;
12916d401060Sralph 	}
12926d401060Sralph 	if ((pid = fork()) == 0) {
12936d401060Sralph 		/*
12946d401060Sralph 		 * Return everything the shell commands print.
12956d401060Sralph 		 */
12966d401060Sralph 		(void) close(0);
12976d401060Sralph 		(void) close(1);
12986d401060Sralph 		(void) close(2);
12996d401060Sralph 		(void) open("/dev/null", 0);
13006d401060Sralph 		(void) dup(fd[1]);
13016d401060Sralph 		(void) dup(fd[1]);
13026d401060Sralph 		(void) close(fd[0]);
13036d401060Sralph 		(void) close(fd[1]);
1304e980046fSralph 		setgid(groupid);
1305a5437550Sralph 		setuid(userid);
13066d401060Sralph 		execl("/bin/sh", "sh", "-c", cmd, 0);
13076d401060Sralph 		_exit(127);
13086d401060Sralph 	}
13096d401060Sralph 	(void) close(fd[1]);
13106d401060Sralph 	s = sbuf;
13116d401060Sralph 	*s++ = '\0';
13126d401060Sralph 	while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
13136d401060Sralph 		cp = buf;
13146d401060Sralph 		do {
13156d401060Sralph 			*s++ = *cp++;
13166d401060Sralph 			if (cp[-1] != '\n') {
13176d401060Sralph 				if (s < &sbuf[sizeof(sbuf)-1])
13186d401060Sralph 					continue;
13196d401060Sralph 				*s++ = '\n';
13206d401060Sralph 			}
13216d401060Sralph 			/*
13226d401060Sralph 			 * Throw away blank lines.
13236d401060Sralph 			 */
13246d401060Sralph 			if (s == &sbuf[2]) {
13256d401060Sralph 				s--;
13266d401060Sralph 				continue;
13276d401060Sralph 			}
13286d401060Sralph 			(void) write(rem, sbuf, s - sbuf);
13296d401060Sralph 			s = &sbuf[1];
13306d401060Sralph 		} while (--i);
13316d401060Sralph 	}
13326d401060Sralph 	if (s > &sbuf[1]) {
13336d401060Sralph 		*s++ = '\n';
13346d401060Sralph 		(void) write(rem, sbuf, s - sbuf);
13356d401060Sralph 	}
13366d401060Sralph 	while ((i = wait(&status)) != pid && i != -1)
13376d401060Sralph 		;
13386d401060Sralph 	if (i == -1)
13396d401060Sralph 		status = -1;
1340e980046fSralph 	(void) close(fd[0]);
13416d401060Sralph 	if (status)
13426d401060Sralph 		error("shell returned %d\n", status);
13436d401060Sralph 	else
13446d401060Sralph 		ack();
13456d401060Sralph }
13466d401060Sralph 
134765a0d230Sralph /*VARARGS2*/
1348d49851feSralph log(fp, fmt, a1, a2, a3)
1349d49851feSralph 	FILE *fp;
1350978e44c9Sralph 	char *fmt;
1351978e44c9Sralph 	int a1, a2, a3;
1352978e44c9Sralph {
1353978e44c9Sralph 	/* Print changes locally if not quiet mode */
1354978e44c9Sralph 	if (!qflag)
1355978e44c9Sralph 		printf(fmt, a1, a2, a3);
1356978e44c9Sralph 
1357978e44c9Sralph 	/* Save changes (for mailing) if really updating files */
1358ad3d87d9Sralph 	if (!(options & VERIFY) && fp != NULL)
1359d49851feSralph 		fprintf(fp, fmt, a1, a2, a3);
1360978e44c9Sralph }
1361978e44c9Sralph 
136265a0d230Sralph /*VARARGS1*/
1363978e44c9Sralph error(fmt, a1, a2, a3)
1364978e44c9Sralph 	char *fmt;
1365978e44c9Sralph 	int a1, a2, a3;
1366978e44c9Sralph {
1367aca7acf4Sralph 	nerrs++;
1368978e44c9Sralph 	strcpy(buf, "\1rdist: ");
1369978e44c9Sralph 	(void) sprintf(buf+8, fmt, a1, a2, a3);
1370d49851feSralph 	if (!iamremote) {
1371d49851feSralph 		fflush(stdout);
1372978e44c9Sralph 		(void) write(2, buf+1, strlen(buf+1));
13736d401060Sralph 	} else
13746d401060Sralph 		(void) write(rem, buf, strlen(buf));
1375978e44c9Sralph 	if (lfp != NULL)
1376978e44c9Sralph 		(void) fwrite(buf+1, 1, strlen(buf+1), lfp);
1377978e44c9Sralph }
1378978e44c9Sralph 
137965a0d230Sralph /*VARARGS1*/
1380978e44c9Sralph fatal(fmt, a1, a2,a3)
1381978e44c9Sralph 	char *fmt;
1382978e44c9Sralph 	int a1, a2, a3;
1383978e44c9Sralph {
1384aca7acf4Sralph 	nerrs++;
1385978e44c9Sralph 	strcpy(buf, "\2rdist: ");
1386978e44c9Sralph 	(void) sprintf(buf+8, fmt, a1, a2, a3);
1387d49851feSralph 	if (!iamremote) {
1388d49851feSralph 		fflush(stdout);
1389978e44c9Sralph 		(void) write(2, buf+1, strlen(buf+1));
13906d401060Sralph 	} else
13916d401060Sralph 		(void) write(rem, buf, strlen(buf));
1392978e44c9Sralph 	if (lfp != NULL)
1393978e44c9Sralph 		(void) fwrite(buf+1, 1, strlen(buf+1), lfp);
1394978e44c9Sralph 	cleanup();
1395978e44c9Sralph }
1396978e44c9Sralph 
1397978e44c9Sralph response()
1398978e44c9Sralph {
1399156664fbSralph 	char *cp, *s;
140015867ad7Sralph 	char resp[BUFSIZ];
1401978e44c9Sralph 
1402978e44c9Sralph 	if (debug)
1403978e44c9Sralph 		printf("response()\n");
1404978e44c9Sralph 
140515867ad7Sralph 	cp = s = resp;
1406978e44c9Sralph 	do {
1407978e44c9Sralph 		if (read(rem, cp, 1) != 1)
1408d58796b1Sralph 			lostconn();
140915867ad7Sralph 	} while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
1410156664fbSralph 
1411156664fbSralph 	switch (*s++) {
1412156664fbSralph 	case '\0':
1413156664fbSralph 		*--cp = '\0';
14146d401060Sralph 		if (*s != '\0') {
1415156664fbSralph 			log(lfp, "%s\n", s);
14166d401060Sralph 			return(1);
14176d401060Sralph 		}
1418156664fbSralph 		return(0);
141926ce117eSsklower 	case '\3':
142026ce117eSsklower 		*--cp = '\0';
142126ce117eSsklower 		log(lfp, "Note: %s\n",s);
142226ce117eSsklower 		return(response());
1423156664fbSralph 
1424156664fbSralph 	default:
1425156664fbSralph 		s--;
1426156664fbSralph 		/* fall into... */
1427156664fbSralph 	case '\1':
1428156664fbSralph 	case '\2':
1429aca7acf4Sralph 		nerrs++;
1430156664fbSralph 		if (*s != '\n') {
1431d49851feSralph 			if (!iamremote) {
1432d49851feSralph 				fflush(stdout);
1433156664fbSralph 				(void) write(2, s, cp - s);
1434d49851feSralph 			}
1435978e44c9Sralph 			if (lfp != NULL)
1436156664fbSralph 				(void) fwrite(s, 1, cp - s, lfp);
1437978e44c9Sralph 		}
143815867ad7Sralph 		if (resp[0] == '\2')
1439d58796b1Sralph 			lostconn();
144001de96ecSralph 		return(-1);
1441978e44c9Sralph 	}
1442978e44c9Sralph }
1443978e44c9Sralph 
1444aca7acf4Sralph /*
1445aca7acf4Sralph  * Remove temporary files and do any cleanup operations before exiting.
1446aca7acf4Sralph  */
1447aca7acf4Sralph cleanup()
1448978e44c9Sralph {
1449aca7acf4Sralph 	(void) unlink(tmpfile);
1450aca7acf4Sralph 	exit(1);
1451978e44c9Sralph }
145226ce117eSsklower 
145326ce117eSsklower note(fmt, a1, a2, a3)
145426ce117eSsklower {
145526ce117eSsklower 	static char buf[BUFSIZ];
145626ce117eSsklower 	sprintf(buf, fmt, a1, a2, a3);
145726ce117eSsklower 	comment(buf);
145826ce117eSsklower }
145926ce117eSsklower 
146026ce117eSsklower comment(s)
146126ce117eSsklower char *s;
146226ce117eSsklower {
146326ce117eSsklower 	char c = '\3';
146426ce117eSsklower 	write(rem, &c, 1);
146526ce117eSsklower 	write(rem, s, strlen(s));
146626ce117eSsklower 	c = '\n';
146726ce117eSsklower 	write(rem, &c, 1);
146826ce117eSsklower }
1469