xref: /original-bsd/usr.bin/rdist/server.c (revision d49851fe)
1978e44c9Sralph #ifndef lint
2*d49851feSralph static	char *sccsid = "@(#)server.c	4.2 (Berkeley) 83/09/27";
3978e44c9Sralph #endif
4978e44c9Sralph 
5978e44c9Sralph #include "defs.h"
6978e44c9Sralph 
7978e44c9Sralph #define	ga() 	(void) write(rem, "", 1)
8978e44c9Sralph 
9*d49851feSralph char	buf[BUFSIZ];		/* general purpose buffer */
10978e44c9Sralph char	target[BUFSIZ];		/* target/source directory name */
11978e44c9Sralph char	*tp;			/* pointer to end of target name */
12978e44c9Sralph int	catname;		/* cat name to target name */
13978e44c9Sralph 
14*d49851feSralph static struct passwd *p = NULL;
15*d49851feSralph static struct group *g = NULL;
16*d49851feSralph 
17*d49851feSralph extern	FILE *lfp;		/* log file for mailing changes */
18*d49851feSralph 
19978e44c9Sralph /*
20978e44c9Sralph  * Server routine to read requests and process them.
21978e44c9Sralph  * Commands are:
22978e44c9Sralph  *	Tname	- Transmit file if out of date
23978e44c9Sralph  *	Vname	- Verify if file out of date or not
24978e44c9Sralph  *	Qname	- Query if file exists. Return mtime & size if it does.
25978e44c9Sralph  */
26978e44c9Sralph server()
27978e44c9Sralph {
28978e44c9Sralph 	char cmdbuf[BUFSIZ];
29978e44c9Sralph 	register char *cp;
30978e44c9Sralph 	register struct block *bp, *last = NULL;
31978e44c9Sralph 	register int n;
32978e44c9Sralph 	static struct block cmdblk = { EXCEPT };
33978e44c9Sralph 
34978e44c9Sralph 	(void) umask(0);
35978e44c9Sralph 	ga();
36978e44c9Sralph 
37978e44c9Sralph 	for (;;) {
38978e44c9Sralph 		cp = cmdbuf;
39978e44c9Sralph 		if (read(rem, cp, 1) <= 0)
40978e44c9Sralph 			return;
41978e44c9Sralph 		if (*cp++ == '\n') {
42978e44c9Sralph 			error("expected control record\n");
43978e44c9Sralph 			continue;
44978e44c9Sralph 		}
45978e44c9Sralph 		do {
46978e44c9Sralph 			if (read(rem, cp, 1) != 1)
47978e44c9Sralph 				lostconn();
48*d49851feSralph 		} while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
49978e44c9Sralph 		*--cp = '\0';
50978e44c9Sralph 		cp = cmdbuf;
51978e44c9Sralph 		switch (*cp++) {
52978e44c9Sralph 		case 'X':  /* add name to list of files to exclude */
53978e44c9Sralph 			if (*cp == '\0')
54978e44c9Sralph 				continue;
55978e44c9Sralph 			bp = ALLOC(block);
56978e44c9Sralph 			if (bp == NULL)
57978e44c9Sralph 				fatal("ran out of memory\n");
58978e44c9Sralph 			bp->b_type = NAME;
59978e44c9Sralph 			bp->b_next = bp->b_args = NULL;
60978e44c9Sralph 			bp->b_name = cp = (char *) malloc(strlen(cp) + 1);
61978e44c9Sralph 			if (cp == NULL)
62978e44c9Sralph 				fatal("ran out of memory\n");
63978e44c9Sralph 			strcpy(cp, &cmdbuf[1]);
64978e44c9Sralph 			if (last == NULL) {
65978e44c9Sralph 				except = &cmdblk;
66978e44c9Sralph 				cmdblk.b_args = last = bp;
67978e44c9Sralph 			} else {
68978e44c9Sralph 				last->b_next = bp;
69978e44c9Sralph 				last = bp;
70978e44c9Sralph 			}
71978e44c9Sralph 			continue;
72978e44c9Sralph 
73978e44c9Sralph 		case 'T':  /* init target file/directory name */
74*d49851feSralph 			catname = 1;	/* target should be directory */
75*d49851feSralph 			goto dotarget;
76*d49851feSralph 
77*d49851feSralph 		case 't':  /* init target file/directory name */
78978e44c9Sralph 			catname = 0;
79*d49851feSralph 		dotarget:
80*d49851feSralph 			exptilde(target, cp);
81978e44c9Sralph 			tp = target;
82978e44c9Sralph 			while (*tp)
83978e44c9Sralph 				tp++;
84978e44c9Sralph 			continue;
85978e44c9Sralph 
86978e44c9Sralph 		case 'S':  /* Send. Transfer file if out of date. */
87978e44c9Sralph 			tp = NULL;
88978e44c9Sralph 			sendf(cp, 0);
89978e44c9Sralph 			continue;
90978e44c9Sralph 
91978e44c9Sralph 		case 'V':  /* Verify. See if file is out of date. */
92978e44c9Sralph 			tp = NULL;
93978e44c9Sralph 			sendf(cp, 1);
94978e44c9Sralph 			continue;
95978e44c9Sralph 
96978e44c9Sralph 		case 'R':  /* Receive. Transfer file. */
97978e44c9Sralph 			recvf(cp, 0);
98978e44c9Sralph 			continue;
99978e44c9Sralph 
100978e44c9Sralph 		case 'D':  /* Directory. Transfer file. */
101978e44c9Sralph 			recvf(cp, 1);
102978e44c9Sralph 			continue;
103978e44c9Sralph 
104978e44c9Sralph 		case 'E':  /* End. (of directory) */
105978e44c9Sralph 			*tp = '\0';
106*d49851feSralph 			if (--catname < 0) {
107978e44c9Sralph 				error("too many 'E's\n");
108978e44c9Sralph 				continue;
109978e44c9Sralph 			}
110*d49851feSralph 			cp = rindex(target, '/');
111*d49851feSralph 			if (cp == NULL)
112*d49851feSralph 				tp = NULL;
113*d49851feSralph 			else {
114978e44c9Sralph 				*cp = '\0';
115978e44c9Sralph 				tp = cp;
116*d49851feSralph 			}
117978e44c9Sralph 			ga();
118978e44c9Sralph 			continue;
119978e44c9Sralph 
120978e44c9Sralph 		case 'Q':  /* Query. Does file exist? */
121978e44c9Sralph 			query(cp);
122978e44c9Sralph 			continue;
123978e44c9Sralph 
124978e44c9Sralph 		case 'L':  /* Log. save message in log file */
125978e44c9Sralph 			query(cp);
126978e44c9Sralph 			continue;
127978e44c9Sralph 
128*d49851feSralph 		case '\1':
129*d49851feSralph 			errs++;
130*d49851feSralph 			continue;
131*d49851feSralph 
132*d49851feSralph 		case '\2':
133*d49851feSralph 			return;
134*d49851feSralph 
135978e44c9Sralph 		default:
136978e44c9Sralph 			error("unknown command type %s\n", cp);
137978e44c9Sralph 		case '\0':
138978e44c9Sralph 			continue;
139978e44c9Sralph 		}
140978e44c9Sralph 	}
141978e44c9Sralph }
142978e44c9Sralph 
143978e44c9Sralph /*
144978e44c9Sralph  * Transfer the file or directory 'name'.
145978e44c9Sralph  */
146978e44c9Sralph sendf(name, verify)
147978e44c9Sralph 	char *name;
148978e44c9Sralph 	int verify;
149978e44c9Sralph {
150978e44c9Sralph 	register char *last;
151978e44c9Sralph 	struct stat stb;
152*d49851feSralph 	int sizerr, f, u;
153978e44c9Sralph 	off_t i;
154978e44c9Sralph 
155978e44c9Sralph 	if (debug)
156978e44c9Sralph 		printf("sendf(%s, %d)\n", name, verify);
157978e44c9Sralph 
158978e44c9Sralph 	if (exclude(name))
159978e44c9Sralph 		return;
160978e44c9Sralph 
161*d49851feSralph 	/*
162*d49851feSralph 	 * first time sendf() is called?
163*d49851feSralph 	 */
164*d49851feSralph 	if (tp == NULL) {
165*d49851feSralph 		exptilde(target, name);
166*d49851feSralph 		tp = name = target;
167*d49851feSralph 		while (*tp)
168*d49851feSralph 			tp++;
169*d49851feSralph 	}
170978e44c9Sralph 	if (access(name, 4) < 0 || stat(name, &stb) < 0) {
171978e44c9Sralph 		error("%s: %s\n", name, sys_errlist[errno]);
172978e44c9Sralph 		return;
173978e44c9Sralph 	}
174978e44c9Sralph 	last = rindex(name, '/');
175978e44c9Sralph 	if (last == NULL)
176978e44c9Sralph 		last = name;
177978e44c9Sralph 	else
178978e44c9Sralph 		last++;
179*d49851feSralph 	if ((u = update(last, &stb)) == 0)
180978e44c9Sralph 		return;
181978e44c9Sralph 
182*d49851feSralph 	if (p == NULL || p->pw_uid != stb.st_uid)
183978e44c9Sralph 		if ((p = getpwuid(stb.st_uid)) == NULL) {
184978e44c9Sralph 			error("no password entry for uid %d\n", stb.st_uid);
185978e44c9Sralph 			return;
186978e44c9Sralph 		}
187*d49851feSralph 	if (g == NULL || g->gr_gid != stb.st_gid)
188978e44c9Sralph 		if ((g = getgrgid(stb.st_gid)) == NULL) {
189978e44c9Sralph 			error("no name for group %d\n", stb.st_gid);
190978e44c9Sralph 			return;
191978e44c9Sralph 		}
192978e44c9Sralph 
193978e44c9Sralph 	switch (stb.st_mode & S_IFMT) {
194978e44c9Sralph 	case S_IFREG:
195978e44c9Sralph 		break;
196978e44c9Sralph 
197978e44c9Sralph 	case S_IFDIR:
198978e44c9Sralph 		rsendf(name, verify, &stb, p->pw_name, g->gr_name);
199978e44c9Sralph 		return;
200978e44c9Sralph 
201978e44c9Sralph 	default:
202978e44c9Sralph 		error("%s: not a plain file\n", name);
203978e44c9Sralph 		return;
204978e44c9Sralph 	}
205978e44c9Sralph 
206*d49851feSralph 	log(lfp, "%s: %s\n", u == 2 ? "updating" : "installing", name);
207978e44c9Sralph 
208978e44c9Sralph 	if (verify || vflag)
209978e44c9Sralph 		return;
210978e44c9Sralph 
211*d49851feSralph 	if ((f = open(name, 0)) < 0) {
212*d49851feSralph 		error("%s: %s\n", name, sys_errlist[errno]);
213*d49851feSralph 		return;
214*d49851feSralph 	}
215978e44c9Sralph 	(void) sprintf(buf, "R%04o %D %D %s %s %s\n", stb.st_mode & 07777,
216978e44c9Sralph 		stb.st_size, stb.st_mtime, p->pw_name, g->gr_name, last);
217978e44c9Sralph 	if (debug)
218978e44c9Sralph 		printf("buf = %s", buf);
219978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
220978e44c9Sralph 	if (response() < 0) {
221978e44c9Sralph 		(void) close(f);
222978e44c9Sralph 		return;
223978e44c9Sralph 	}
224978e44c9Sralph 	sizerr = 0;
225978e44c9Sralph 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
226978e44c9Sralph 		int amt = BUFSIZ;
227978e44c9Sralph 		if (i + amt > stb.st_size)
228978e44c9Sralph 			amt = stb.st_size - i;
229978e44c9Sralph 		if (sizerr == 0 && read(f, buf, amt) != amt)
230978e44c9Sralph 			sizerr = 1;
231978e44c9Sralph 		(void) write(rem, buf, amt);
232978e44c9Sralph 	}
233978e44c9Sralph 	(void) close(f);
234978e44c9Sralph 	if (sizerr)
235978e44c9Sralph 		error("%s: file changed size\n", name);
236978e44c9Sralph 	else
237978e44c9Sralph 		ga();
238978e44c9Sralph 	(void) response();
239978e44c9Sralph }
240978e44c9Sralph 
241978e44c9Sralph rsendf(name, verify, st, owner, group)
242978e44c9Sralph 	char *name;
243978e44c9Sralph 	int verify;
244978e44c9Sralph 	struct stat *st;
245978e44c9Sralph 	char *owner, *group;
246978e44c9Sralph {
247978e44c9Sralph 	DIR *d;
248978e44c9Sralph 	struct direct *dp;
249978e44c9Sralph 	register char *last;
250978e44c9Sralph 	char *otp;
251*d49851feSralph 	int len;
252978e44c9Sralph 
253978e44c9Sralph 	if (debug)
254978e44c9Sralph 		printf("rsendf(%s, %d, %x, %s, %s)\n", name, verify, st,
255978e44c9Sralph 			owner, group);
256978e44c9Sralph 
257978e44c9Sralph 	if ((d = opendir(name)) == NULL) {
258978e44c9Sralph 		error("%s: %s\n", name, sys_errlist[errno]);
259978e44c9Sralph 		return;
260978e44c9Sralph 	}
261978e44c9Sralph 	last = rindex(name, '/');
262978e44c9Sralph 	if (last == NULL)
263978e44c9Sralph 		last = name;
264978e44c9Sralph 	else
265978e44c9Sralph 		last++;
266978e44c9Sralph 	(void) sprintf(buf, "D%04o 0 0 %s %s %s\n", st->st_mode & 07777,
267978e44c9Sralph 		owner, group, last);
268978e44c9Sralph 	if (debug)
269978e44c9Sralph 		printf("buf = %s", buf);
270978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
271978e44c9Sralph 	if (response() < 0) {
272978e44c9Sralph 		closedir(d);
273978e44c9Sralph 		return;
274978e44c9Sralph 	}
275978e44c9Sralph 	otp = tp;
276*d49851feSralph 	len = tp - target;
277978e44c9Sralph 	while (dp = readdir(d)) {
278978e44c9Sralph 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
279978e44c9Sralph 			continue;
280*d49851feSralph 		if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
281978e44c9Sralph 			error("%s/%s: Name too long\n", name, dp->d_name);
282978e44c9Sralph 			continue;
283978e44c9Sralph 		}
284978e44c9Sralph 		tp = otp;
285978e44c9Sralph 		*tp++ = '/';
286978e44c9Sralph 		last = dp->d_name;
287978e44c9Sralph 		while (*tp++ = *last++)
288978e44c9Sralph 			;
289978e44c9Sralph 		tp--;
290978e44c9Sralph 		sendf(target, verify);
291978e44c9Sralph 	}
292978e44c9Sralph 	closedir(d);
293978e44c9Sralph 	(void) write(rem, "E\n", 2);
294978e44c9Sralph 	(void) response();
295978e44c9Sralph 	tp = otp;
296978e44c9Sralph 	*tp = '\0';
297978e44c9Sralph }
298978e44c9Sralph 
299978e44c9Sralph /*
300978e44c9Sralph  * Check to see if file needs to be updated on the remote machine.
301*d49851feSralph  * Returns 0 if no update, 1 if remote doesn't exist, and 2 if out of date.
302978e44c9Sralph  */
303978e44c9Sralph update(name, st)
304978e44c9Sralph 	char *name;
305978e44c9Sralph 	struct stat *st;
306978e44c9Sralph {
307978e44c9Sralph 	register char *cp;
308978e44c9Sralph 	register off_t size;
309978e44c9Sralph 	register time_t mtime;
310978e44c9Sralph 
311978e44c9Sralph 	if (debug)
312978e44c9Sralph 		printf("update(%s, %x)\n", name, st);
313978e44c9Sralph 
314978e44c9Sralph 	/*
315978e44c9Sralph 	 * Check to see if the file exists on the remote machine.
316978e44c9Sralph 	 */
317978e44c9Sralph 	(void) sprintf(buf, "Q%s\n", name);
318978e44c9Sralph 	if (debug)
319978e44c9Sralph 		printf("buf = %s", buf);
320978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
321978e44c9Sralph 	cp = buf;
322978e44c9Sralph 	do {
323978e44c9Sralph 		if (read(rem, cp, 1) != 1)
324978e44c9Sralph 			lostconn();
325*d49851feSralph 	} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
326978e44c9Sralph 	*--cp = '\0';
327978e44c9Sralph 	cp = buf;
328978e44c9Sralph 	if (debug)
329978e44c9Sralph 		printf("resp = %s\n", cp);
330978e44c9Sralph 
331978e44c9Sralph 	switch (*cp++) {
332978e44c9Sralph 	case 'Y':
333978e44c9Sralph 		break;
334978e44c9Sralph 
335*d49851feSralph 	case 'N':  /* file doesn't exist so install it */
336978e44c9Sralph 		return(1);
337978e44c9Sralph 
338978e44c9Sralph 	case '\1':
339*d49851feSralph 		errs++;
340*d49851feSralph 		if (*cp != '\0') {
341*d49851feSralph 			if (!iamremote) {
342*d49851feSralph 				fflush(stdout);
343*d49851feSralph 				(void) write(2, cp, strlen(cp));
344*d49851feSralph 			}
345*d49851feSralph 			if (lfp != NULL)
346*d49851feSralph 				(void) fwrite(cp, 1, strlen(cp), lfp);
347*d49851feSralph 		}
348978e44c9Sralph 		return(0);
349978e44c9Sralph 
350978e44c9Sralph 	default:
351978e44c9Sralph 		error("unexpected response '%c' to query\n", *--cp);
352978e44c9Sralph 		return(0);
353978e44c9Sralph 	}
354978e44c9Sralph 
355978e44c9Sralph 	if (*cp == '\0') {
356978e44c9Sralph 		if ((st->st_mode & S_IFMT) == S_IFDIR)
357*d49851feSralph 			return(2);
358978e44c9Sralph 		return(1);
359978e44c9Sralph 	}
360978e44c9Sralph 
361978e44c9Sralph 	size = 0;
362978e44c9Sralph 	while (isdigit(*cp))
363978e44c9Sralph 		size = size * 10 + (*cp++ - '0');
364978e44c9Sralph 	if (*cp++ != ' ') {
365978e44c9Sralph 		error("size not delimited\n");
366978e44c9Sralph 		return(0);
367978e44c9Sralph 	}
368978e44c9Sralph 	mtime = 0;
369978e44c9Sralph 	while (isdigit(*cp))
370978e44c9Sralph 		mtime = mtime * 10 + (*cp++ - '0');
371978e44c9Sralph 	if (*cp != '\0') {
372978e44c9Sralph 		error("mtime not delimited\n");
373978e44c9Sralph 		return(0);
374978e44c9Sralph 	}
375978e44c9Sralph 	/*
376978e44c9Sralph 	 * File needs to be updated?
377978e44c9Sralph 	 */
378978e44c9Sralph 	if (st->st_mtime == mtime && st->st_size == size ||
379*d49851feSralph 	    yflag && st->st_mtime < mtime)
380978e44c9Sralph 		return(0);
381*d49851feSralph 	return(2);
382978e44c9Sralph }
383978e44c9Sralph 
384978e44c9Sralph /*
385978e44c9Sralph  * Query. Check to see if file exists. Return one of the following:
386978e44c9Sralph  *	N\n		- doesn't exist
387978e44c9Sralph  *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
388978e44c9Sralph  *	Y\n		- exists and its a directory
389978e44c9Sralph  *	^Aerror message\n
390978e44c9Sralph  */
391978e44c9Sralph query(name)
392978e44c9Sralph 	char *name;
393978e44c9Sralph {
394978e44c9Sralph 	struct stat stb;
395978e44c9Sralph 
396978e44c9Sralph 	if (catname)
397978e44c9Sralph 		(void) sprintf(tp, "/%s", name);
398978e44c9Sralph 	if (stat(target, &stb) < 0) {
399978e44c9Sralph 		(void) write(rem, "N\n", 2);
400978e44c9Sralph 		*tp = '\0';
401978e44c9Sralph 		return;
402978e44c9Sralph 	}
403978e44c9Sralph 
404978e44c9Sralph 	switch (stb.st_mode & S_IFMT) {
405978e44c9Sralph 	case S_IFREG:
406978e44c9Sralph 		(void) sprintf(buf, "Y%D %D\n", stb.st_size, stb.st_mtime);
407978e44c9Sralph 		(void) write(rem, buf, strlen(buf));
408978e44c9Sralph 		break;
409978e44c9Sralph 
410978e44c9Sralph 	case S_IFDIR:
411978e44c9Sralph 		(void) write(rem, "Y\n", 2);
412978e44c9Sralph 		break;
413978e44c9Sralph 
414978e44c9Sralph 	default:
415978e44c9Sralph 		error("%s: not a plain file\n", name);
416978e44c9Sralph 		break;
417978e44c9Sralph 	}
418978e44c9Sralph 	*tp = '\0';
419978e44c9Sralph }
420978e44c9Sralph 
421978e44c9Sralph recvf(cmd, isdir)
422978e44c9Sralph 	char *cmd;
423978e44c9Sralph 	int isdir;
424978e44c9Sralph {
425978e44c9Sralph 	register char *cp;
426978e44c9Sralph 	int f, mode, wrerr, exists, olderrno;
427978e44c9Sralph 	off_t i, size;
428978e44c9Sralph 	time_t mtime;
429978e44c9Sralph 	struct stat stb;
430978e44c9Sralph 	struct timeval tvp[2];
431978e44c9Sralph 	char *owner, *group, *dir;
432978e44c9Sralph 	char new[BUFSIZ];
433*d49851feSralph 	extern char *tmpname;
434978e44c9Sralph 
435978e44c9Sralph 	mode = 0;
436978e44c9Sralph 	for (cp = cmd; cp < cmd+4; cp++) {
437978e44c9Sralph 		if (*cp < '0' || *cp > '7') {
438978e44c9Sralph 			error("bad mode\n");
439978e44c9Sralph 			return;
440978e44c9Sralph 		}
441978e44c9Sralph 		mode = (mode << 3) | (*cp - '0');
442978e44c9Sralph 	}
443978e44c9Sralph 	if (*cp++ != ' ') {
444978e44c9Sralph 		error("mode not delimited\n");
445978e44c9Sralph 		return;
446978e44c9Sralph 	}
447978e44c9Sralph 	size = 0;
448978e44c9Sralph 	while (isdigit(*cp))
449978e44c9Sralph 		size = size * 10 + (*cp++ - '0');
450978e44c9Sralph 	if (*cp++ != ' ') {
451978e44c9Sralph 		error("size not delimited\n");
452978e44c9Sralph 		return;
453978e44c9Sralph 	}
454978e44c9Sralph 	mtime = 0;
455978e44c9Sralph 	while (isdigit(*cp))
456978e44c9Sralph 		mtime = mtime * 10 + (*cp++ - '0');
457978e44c9Sralph 	if (*cp++ != ' ') {
458978e44c9Sralph 		error("mtime not delimited\n");
459978e44c9Sralph 		return;
460978e44c9Sralph 	}
461978e44c9Sralph 	owner = cp;
462978e44c9Sralph 	while (*cp && *cp != ' ')
463978e44c9Sralph 		cp++;
464978e44c9Sralph 	if (*cp != ' ') {
465978e44c9Sralph 		error("owner name not delimited\n");
466978e44c9Sralph 		return;
467978e44c9Sralph 	}
468978e44c9Sralph 	*cp++ = '\0';
469978e44c9Sralph 	group = cp;
470978e44c9Sralph 	while (*cp && *cp != ' ')
471978e44c9Sralph 		cp++;
472978e44c9Sralph 	if (*cp != ' ') {
473978e44c9Sralph 		error("group name not delimited\n");
474978e44c9Sralph 		return;
475978e44c9Sralph 	}
476978e44c9Sralph 	*cp++ = '\0';
477978e44c9Sralph 
478*d49851feSralph 	new[0] = '\0';
479978e44c9Sralph 	if (isdir) {
480978e44c9Sralph 		if (catname++) {
481978e44c9Sralph 			*tp++ = '/';
482978e44c9Sralph 			while (*tp++ = *cp++)
483978e44c9Sralph 				;
484978e44c9Sralph 			tp--;
485978e44c9Sralph 		}
486*d49851feSralph 		if (vflag) {
487*d49851feSralph 			ga();
488*d49851feSralph 			return;
489*d49851feSralph 		}
490978e44c9Sralph 		if (stat(target, &stb) == 0) {
491978e44c9Sralph 			if ((stb.st_mode & S_IFMT) != S_IFDIR) {
492978e44c9Sralph 				errno = ENOTDIR;
493978e44c9Sralph 				goto bad;
494978e44c9Sralph 			}
495978e44c9Sralph 		} else {
496978e44c9Sralph 			/*
497978e44c9Sralph 			 * Check parent directory for write permission.
498978e44c9Sralph 			 */
499978e44c9Sralph 			cp = rindex(target, '/');
500978e44c9Sralph 			if (cp == NULL)
501978e44c9Sralph 				dir = ".";
502978e44c9Sralph 			else if (cp == target) {
503978e44c9Sralph 				dir = "/";
504978e44c9Sralph 				cp = NULL;
505978e44c9Sralph 			} else {
506978e44c9Sralph 				dir = target;
507978e44c9Sralph 				*cp = '\0';
508978e44c9Sralph 			}
509*d49851feSralph 			if (access(dir, 2) < 0)
510*d49851feSralph 				goto bad2;
511978e44c9Sralph 			if (cp != NULL)
512978e44c9Sralph 				*cp = '/';
513978e44c9Sralph 			if (mkdir(target, mode) < 0)
514978e44c9Sralph 				goto bad;
515*d49851feSralph 			if (chog(target, owner, group, mode) < 0)
516978e44c9Sralph 				return;
517*d49851feSralph 		}
518978e44c9Sralph 		ga();
519978e44c9Sralph 		return;
520978e44c9Sralph 	}
521978e44c9Sralph 
522978e44c9Sralph 	if (catname)
523978e44c9Sralph 		(void) sprintf(tp, "/%s", cp);
524978e44c9Sralph 	if (stat(target, &stb) == 0) {
525978e44c9Sralph 		switch (stb.st_mode & S_IFMT) {
526978e44c9Sralph 		case S_IFREG:
527978e44c9Sralph 			break;
528978e44c9Sralph 
529978e44c9Sralph 		case S_IFDIR:
530978e44c9Sralph 			if (!catname) {
531978e44c9Sralph 				(void) sprintf(tp, "/%s", cp);
532978e44c9Sralph 				break;
533978e44c9Sralph 			}
534978e44c9Sralph 
535978e44c9Sralph 		default:
536978e44c9Sralph 			error("%s: not a regular file\n", target);
537978e44c9Sralph 			return;
538978e44c9Sralph 		}
539978e44c9Sralph 	}
540978e44c9Sralph 	/*
541978e44c9Sralph 	 * Check parent directory for write permission.
542978e44c9Sralph 	 */
543978e44c9Sralph 	cp = rindex(target, '/');
544978e44c9Sralph 	if (cp == NULL)
545978e44c9Sralph 		dir = ".";
546978e44c9Sralph 	else if (cp == target) {
547978e44c9Sralph 		dir = "/";
548978e44c9Sralph 		cp = NULL;
549978e44c9Sralph 	} else {
550978e44c9Sralph 		dir = target;
551978e44c9Sralph 		*cp = '\0';
552978e44c9Sralph 	}
553*d49851feSralph 	if (access(dir, 2) < 0) {
554*d49851feSralph bad2:
555*d49851feSralph 		error("%s: %s\n", dir, sys_errlist[errno]);
556978e44c9Sralph 		if (cp != NULL)
557978e44c9Sralph 			*cp = '/';
558*d49851feSralph 		return;
559*d49851feSralph 	}
560*d49851feSralph 	(void) sprintf(new, "%s/%s", dir, tmpname);
561*d49851feSralph 	if (cp != NULL)
562*d49851feSralph 		*cp = '/';
563978e44c9Sralph 	if ((f = creat(new, mode)) < 0)
564978e44c9Sralph 		goto bad1;
565*d49851feSralph 	if (chog(new, owner, group, mode) < 0) {
566*d49851feSralph 		(void) close(f);
567*d49851feSralph 		(void) unlink(new);
568978e44c9Sralph 		return;
569*d49851feSralph 	}
570978e44c9Sralph 	ga();
571978e44c9Sralph 
572978e44c9Sralph 	wrerr = 0;
573978e44c9Sralph 	for (i = 0; i < size; i += BUFSIZ) {
574978e44c9Sralph 		int amt = BUFSIZ;
575978e44c9Sralph 		char *cp = buf;
576978e44c9Sralph 
577978e44c9Sralph 		if (i + amt > size)
578978e44c9Sralph 			amt = size - i;
579978e44c9Sralph 		do {
580978e44c9Sralph 			int j = read(rem, cp, amt);
581978e44c9Sralph 
582*d49851feSralph 			if (j <= 0) {
583*d49851feSralph 				(void) close(f);
584*d49851feSralph 				(void) unlink(new);
585978e44c9Sralph 				cleanup();
586*d49851feSralph 			}
587978e44c9Sralph 			amt -= j;
588978e44c9Sralph 			cp += j;
589978e44c9Sralph 		} while (amt > 0);
590978e44c9Sralph 		amt = BUFSIZ;
591978e44c9Sralph 		if (i + amt > size)
592978e44c9Sralph 			amt = size - i;
593978e44c9Sralph 		if (wrerr == 0 && write(f, buf, amt) != amt) {
594978e44c9Sralph 			olderrno = errno;
595978e44c9Sralph 			wrerr++;
596978e44c9Sralph 		}
597978e44c9Sralph 	}
598978e44c9Sralph 	(void) response();
599978e44c9Sralph 	if (wrerr) {
600978e44c9Sralph 		error("%s: %s\n", cp, sys_errlist[olderrno]);
601*d49851feSralph 		(void) close(f);
602*d49851feSralph 		(void) unlink(new);
603978e44c9Sralph 		return;
604978e44c9Sralph 	}
605978e44c9Sralph 
606978e44c9Sralph 	/*
607978e44c9Sralph 	 * Set last modified time
608978e44c9Sralph 	 */
609978e44c9Sralph 	(void) fstat(f, &stb);
610978e44c9Sralph 	(void) close(f);
611978e44c9Sralph 	tvp[0].tv_sec = stb.st_atime;
612978e44c9Sralph 	tvp[0].tv_usec = 0;
613978e44c9Sralph 	tvp[1].tv_sec = mtime;
614978e44c9Sralph 	tvp[1].tv_usec = 0;
615978e44c9Sralph 	if (utimes(new, tvp) < 0) {
616978e44c9Sralph bad1:
617978e44c9Sralph 		error("%s: %s\n", new, sys_errlist[errno]);
618*d49851feSralph 		if (new[0])
619*d49851feSralph 			(void) unlink(new);
620978e44c9Sralph 		return;
621978e44c9Sralph 	}
622978e44c9Sralph 
623978e44c9Sralph 	if (rename(new, target) < 0) {
624978e44c9Sralph bad:
625978e44c9Sralph 		error("%s: %s\n", target, sys_errlist[errno]);
626*d49851feSralph 		if (new[0])
627*d49851feSralph 			(void) unlink(new);
628978e44c9Sralph 		return;
629978e44c9Sralph 	}
630978e44c9Sralph 	ga();
631978e44c9Sralph }
632978e44c9Sralph 
633978e44c9Sralph /*
634978e44c9Sralph  * Change owner and group of file.
635978e44c9Sralph  */
636*d49851feSralph chog(file, owner, group, mode)
637978e44c9Sralph 	char *file, *owner, *group;
638*d49851feSralph 	int mode;
639978e44c9Sralph {
640*d49851feSralph 	extern int userid, groupid;
641*d49851feSralph 	extern char user[];
642978e44c9Sralph 	register int i;
643978e44c9Sralph 	int uid, gid;
644978e44c9Sralph 
645978e44c9Sralph 	uid = userid;
646978e44c9Sralph 	if (userid == 0) {
647*d49851feSralph 		if (p == NULL || strcmp(owner, p->pw_name) != 0) {
648*d49851feSralph 			if ((p = getpwnam(owner)) == NULL) {
649*d49851feSralph 				if (mode & 04000) {
650978e44c9Sralph 					error("%s: unknown login name\n", owner);
651978e44c9Sralph 					return(-1);
652978e44c9Sralph 				}
653*d49851feSralph 			} else
654*d49851feSralph 				uid = p->pw_uid;
655*d49851feSralph 		} else
656978e44c9Sralph 			uid = p->pw_uid;
657978e44c9Sralph 	}
658*d49851feSralph 	gid = groupid;
659*d49851feSralph 	if (g == NULL || strcmp(group, g->gr_name) != 0) {
660*d49851feSralph 		if ((g = getgrnam(group)) == NULL) {
661*d49851feSralph 			if (mode & 02000) {
662978e44c9Sralph 				error("%s: unknown group\n", group);
663978e44c9Sralph 				return(-1);
664978e44c9Sralph 			}
665*d49851feSralph 		} else
666978e44c9Sralph 			gid = g->gr_gid;
667*d49851feSralph 	} else
668*d49851feSralph 		gid = g->gr_gid;
669*d49851feSralph 	if (userid && groupid != gid) {
670*d49851feSralph 		for (i = 0; g->gr_mem[i] != NULL; i++)
671978e44c9Sralph 			if (!(strcmp(user, g->gr_mem[i])))
672978e44c9Sralph 				goto ok;
673*d49851feSralph 		gid = groupid;
674978e44c9Sralph 	}
675978e44c9Sralph ok:
676978e44c9Sralph 	if (chown(file, uid, gid) < 0) {
677978e44c9Sralph 		error("%s: %s\n", file, sys_errlist[errno]);
678978e44c9Sralph 		return(-1);
679978e44c9Sralph 	}
680978e44c9Sralph 	return(0);
681978e44c9Sralph }
682978e44c9Sralph 
683978e44c9Sralph /*VARARGS*/
684*d49851feSralph log(fp, fmt, a1, a2, a3)
685*d49851feSralph 	FILE *fp;
686978e44c9Sralph 	char *fmt;
687978e44c9Sralph 	int a1, a2, a3;
688978e44c9Sralph {
689978e44c9Sralph 	/* Print changes locally if not quiet mode */
690978e44c9Sralph 	if (!qflag)
691978e44c9Sralph 		printf(fmt, a1, a2, a3);
692978e44c9Sralph 
693978e44c9Sralph 	/* Save changes (for mailing) if really updating files */
694*d49851feSralph 	if (!vflag && fp != NULL)
695*d49851feSralph 		fprintf(fp, fmt, a1, a2, a3);
696978e44c9Sralph }
697978e44c9Sralph 
698978e44c9Sralph /*VARARGS*/
699978e44c9Sralph error(fmt, a1, a2, a3)
700978e44c9Sralph 	char *fmt;
701978e44c9Sralph 	int a1, a2, a3;
702978e44c9Sralph {
703978e44c9Sralph 	errs++;
704978e44c9Sralph 	strcpy(buf, "\1rdist: ");
705978e44c9Sralph 	(void) sprintf(buf+8, fmt, a1, a2, a3);
706978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
707978e44c9Sralph 	if (buf[1] != '\0') {
708*d49851feSralph 		if (!iamremote) {
709*d49851feSralph 			fflush(stdout);
710978e44c9Sralph 			(void) write(2, buf+1, strlen(buf+1));
711*d49851feSralph 		}
712978e44c9Sralph 		if (lfp != NULL)
713978e44c9Sralph 			(void) fwrite(buf+1, 1, strlen(buf+1), lfp);
714978e44c9Sralph 	}
715978e44c9Sralph }
716978e44c9Sralph 
717978e44c9Sralph /*VARARGS*/
718978e44c9Sralph fatal(fmt, a1, a2,a3)
719978e44c9Sralph 	char *fmt;
720978e44c9Sralph 	int a1, a2, a3;
721978e44c9Sralph {
722978e44c9Sralph 	errs++;
723978e44c9Sralph 	strcpy(buf, "\2rdist: ");
724978e44c9Sralph 	(void) sprintf(buf+8, fmt, a1, a2, a3);
725978e44c9Sralph 	(void) write(rem, buf, strlen(buf));
726978e44c9Sralph 	if (buf[1] != '\0') {
727*d49851feSralph 		if (!iamremote) {
728*d49851feSralph 			fflush(stdout);
729978e44c9Sralph 			(void) write(2, buf+1, strlen(buf+1));
730*d49851feSralph 		}
731978e44c9Sralph 		if (lfp != NULL)
732978e44c9Sralph 			(void) fwrite(buf+1, 1, strlen(buf+1), lfp);
733978e44c9Sralph 	}
734978e44c9Sralph 	cleanup();
735978e44c9Sralph }
736978e44c9Sralph 
737978e44c9Sralph response()
738978e44c9Sralph {
739978e44c9Sralph 	char resp, c, *cp = buf;
740978e44c9Sralph 
741978e44c9Sralph 	if (debug)
742978e44c9Sralph 		printf("response()\n");
743978e44c9Sralph 
744978e44c9Sralph 	if (read(rem, &resp, 1) != 1)
745978e44c9Sralph 		lostconn();
746978e44c9Sralph 
747978e44c9Sralph 	switch (resp) {
748978e44c9Sralph 	case '\0':
749978e44c9Sralph 		return(0);
750978e44c9Sralph 
751978e44c9Sralph 	default:
752978e44c9Sralph 		*cp++ = resp;
753978e44c9Sralph 		/* fall into... */
754978e44c9Sralph 	case '\1':
755978e44c9Sralph 	case '\2':
756978e44c9Sralph 		errs++;
757978e44c9Sralph 		do {
758978e44c9Sralph 			if (read(rem, cp, 1) != 1)
759978e44c9Sralph 				lostconn();
760*d49851feSralph 		} while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
761*d49851feSralph 		if (buf[0] != '\n') {
762*d49851feSralph 			if (!iamremote) {
763*d49851feSralph 				fflush(stdout);
764978e44c9Sralph 				(void) write(2, buf, cp - buf);
765*d49851feSralph 			}
766978e44c9Sralph 			if (lfp != NULL)
767978e44c9Sralph 				(void) fwrite(buf, 1, cp - buf, lfp);
768978e44c9Sralph 		}
769978e44c9Sralph 		if (resp == '\1')
770978e44c9Sralph 			return(-1);
771978e44c9Sralph 		cleanup();
772978e44c9Sralph 	}
773978e44c9Sralph 	/*NOTREACHED*/
774978e44c9Sralph }
775978e44c9Sralph 
776978e44c9Sralph lostconn()
777978e44c9Sralph {
778978e44c9Sralph 	if (!iamremote)
779978e44c9Sralph 		fprintf(stderr, "rdist: lost connection\n");
780978e44c9Sralph 	cleanup();
781978e44c9Sralph }
782