xref: /openbsd-src/usr.bin/rdistd/server.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: server.c,v 1.16 2003/07/07 14:39:26 mpech Exp $	*/
2 
3 /*
4  * Copyright (c) 1983 Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 #include "defs.h"
32 
33 #ifndef lint
34 #if 0
35 static char RCSid[] __attribute__((__unused__)) =
36 "$From: server.c,v 1.10 1999/08/04 15:57:33 christos Exp $";
37 #else
38 static char RCSid[] __attribute__((__unused__)) =
39 "$OpenBSD: server.c,v 1.16 2003/07/07 14:39:26 mpech Exp $";
40 #endif
41 
42 static char sccsid[] __attribute__((__unused__)) =
43 "@(#)server.c	5.3 (Berkeley) 6/7/86";
44 
45 static char copyright[] __attribute__((__unused__)) =
46 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
47  All rights reserved.\n";
48 #endif /* not lint */
49 
50 /*
51  * Server routines
52  */
53 
54 char	tempname[sizeof _RDIST_TMP + 1]; /* Tmp file name */
55 char	buf[BUFSIZ];		/* general purpose buffer */
56 char	target[MAXPATHLEN];	/* target/source directory name */
57 char	*ptarget;		/* pointer to end of target name */
58 int	catname = 0;		/* cat name to target name */
59 char	*sptarget[32];		/* stack of saved ptarget's for directories */
60 char   *fromhost = NULL;	/* Client hostname */
61 static long min_freespace = 0;	/* Minimium free space on a filesystem */
62 static long min_freefiles = 0;	/* Minimium free # files on a filesystem */
63 int	oumask;			/* Old umask */
64 
65 static int cattarget(char *);
66 static int setownership(char *, int, UID_T, GID_T, int);
67 static int setfilemode(char *, int, int, int);
68 static int fchog(int, char *, char *, char *, int);
69 static int removefile(struct stat *, int);
70 static void doclean(char *);
71 static void clean(char *);
72 static void dospecial(char *);
73 static void docmdspecial(void);
74 static void query(char *);
75 static int chkparent(char *, opt_t);
76 static char *savetarget(char *, opt_t);
77 static void recvfile(char *, opt_t, int, char *, char *, time_t, time_t, off_t);
78 static void recvdir(opt_t, int, char *, char *);
79 static void recvlink(char *, opt_t, int, off_t);
80 static void hardlink(char *);
81 static void setconfig(char *);
82 static void recvit(char *, int);
83 static void dochmog(char *);
84 static void settarget(char *, int);
85 
86 /*
87  * Cat "string" onto the target buffer with error checking.
88  */
89 static int
90 cattarget(char *string)
91 {
92 	if (strlen(string) + strlen(target) + 2 > sizeof(target)) {
93 		message(MT_INFO, "target buffer is not large enough.");
94 		return(-1);
95 	}
96 	if (!ptarget) {
97 		message(MT_INFO, "NULL target pointer set.");
98 		return(-10);
99 	}
100 
101 	(void) snprintf(ptarget, sizeof(target) - (ptarget - target),
102 			"/%s", string);
103 
104 	return(0);
105 }
106 
107 /*
108  * Set uid and gid ownership of a file.
109  */
110 static int
111 setownership(char *file, int fd, UID_T uid, GID_T gid, int link)
112 {
113 	int status = -1;
114 
115 	/*
116 	 * We assume only the Superuser can change uid ownership.
117 	 */
118 	if (getuid() != 0)
119 		uid = -1;
120 
121 	/*
122 	 * If we are dealing with a symlink, only try to change it if
123 	 * we have lchown, if we don't leave it alone.
124 	 */
125 #if	defined(HAVE_LCHOWN)
126 	if (link)
127 		status = lchown(file, (CHOWN_UID_T) uid,
128 				(CHOWN_GID_T) gid);
129 #else
130 	if (link)
131 		return 0;
132 #endif
133 
134 #if	defined(HAVE_FCHOWN)
135 	if (fd != -1 && !link)
136 		status = fchown(fd, (CHOWN_UID_T) uid,
137 				(CHOWN_GID_T) gid);
138 #endif
139 	if (status < 0 && !link)
140 		status = chown(file, (CHOWN_UID_T) uid,
141 			       (CHOWN_GID_T) gid);
142 
143 	if (status < 0) {
144 		if (uid == (UID_T) -1)
145 			message(MT_NOTICE, "%s: chgrp %d failed: %s",
146 				target, gid, SYSERR);
147 		else
148 			message(MT_NOTICE, "%s: chown %d.%d failed: %s",
149 				target, uid, gid, SYSERR);
150 		return(-1);
151 	}
152 
153 	return(0);
154 }
155 
156 /*
157  * Set mode of a file
158  */
159 static int
160 setfilemode(char *file, int fd, int mode, int link)
161 {
162 	int status = -1;
163 
164 	if (mode == -1)
165 		return(0);
166 
167 	/*
168 	 * If we are dealing with a symlink, only try to change it if
169 	 * we have lchown, if we don't leave it alone.
170 	 */
171 #if	defined(HAVE_LCHMOD)
172 	if (link)
173 		status = lchmod(file, mode);
174 #else
175 	if (link)
176 		return 0;
177 #endif
178 
179 #if	defined(HAVE_FCHMOD)
180 	if (fd != -1 && !link)
181 		status = fchmod(fd, mode);
182 #endif
183 
184 	if (status < 0 && !link)
185 		status = chmod(file, mode);
186 
187 	if (status < 0) {
188 		message(MT_NOTICE, "%s: chmod failed: %s", target, SYSERR);
189 		return(-1);
190 	}
191 
192 	return(0);
193 }
194 /*
195  * Change owner, group and mode of file.
196  */
197 static int
198 fchog(int fd, char *file, char *owner, char *group, int mode)
199 {
200 	static struct group *gr = NULL;
201 	extern char *locuser;
202 	int i;
203 	struct stat st;
204 	UID_T uid;
205 	GID_T gid;
206 	GID_T primegid = (GID_T)-2;
207 
208 	uid = userid;
209 	if (userid == 0) {	/* running as root; take anything */
210 		if (*owner == ':') {
211 			uid = (UID_T) atoi(owner + 1);
212 		} else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
213 			if ((pw = getpwnam(owner)) == NULL) {
214 				if (mode != -1 && IS_ON(mode, S_ISUID)) {
215 					message(MT_NOTICE,
216 			      "%s: unknown login name \"%s\", clearing setuid",
217 						target, owner);
218 					mode &= ~S_ISUID;
219 					uid = 0;
220 				} else
221 					message(MT_NOTICE,
222 					"%s: unknown login name \"%s\"",
223 						target, owner);
224 			} else
225 				uid = pw->pw_uid;
226 		} else {
227 			uid = pw->pw_uid;
228 			primegid = pw->pw_gid;
229 		}
230 		if (*group == ':') {
231 			gid = (GID_T) atoi(group + 1);
232 			goto ok;
233 		}
234 	} else {	/* not root, setuid only if user==owner */
235 		struct passwd *lupw;
236 
237 		if (mode != -1) {
238 			if (IS_ON(mode, S_ISUID) &&
239 			    strcmp(locuser, owner) != 0)
240 				mode &= ~S_ISUID;
241 			if (mode)
242 				mode &= ~S_ISVTX; /* and strip sticky too */
243 		}
244 
245 		if ((lupw = getpwnam(locuser)) != NULL)
246 			primegid = lupw->pw_gid;
247 	}
248 
249 	gid = (GID_T) -1;
250 	if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
251 		if ((*group == ':' &&
252 		     (getgrgid(gid = atoi(group + 1)) == NULL))
253 		    || ((gr = (struct group *)getgrnam(group)) == NULL)) {
254 			if (mode != -1 && IS_ON(mode, S_ISGID)) {
255 				message(MT_NOTICE,
256 				"%s: unknown group \"%s\", clearing setgid",
257 					target, group);
258 				mode &= ~S_ISGID;
259 			} else
260 				message(MT_NOTICE,
261 					"%s: unknown group \"%s\"",
262 					target, group);
263 		} else
264 			gid = gr->gr_gid;
265 	} else
266 		gid = gr->gr_gid;
267 
268 	if (userid && gid >= 0 && gid != primegid) {
269 		if (gr)
270 			for (i = 0; gr->gr_mem[i] != NULL; i++)
271 				if (strcmp(locuser, gr->gr_mem[i]) == 0)
272 					goto ok;
273 		if (mode != -1 && IS_ON(mode, S_ISGID)) {
274 			message(MT_NOTICE,
275 				"%s: user %s not in group %s, clearing setgid",
276 				target, locuser, group);
277 			mode &= ~S_ISGID;
278 		}
279 		gid = (GID_T) -1;
280 	}
281 ok:
282 	if (stat(file, &st) == -1) {
283 		error("%s: Stat failed %s", file, SYSERR);
284 		return -1;
285 	}
286 	/*
287 	 * Set uid and gid ownership.  If that fails, strip setuid and
288 	 * setgid bits from mode.  Once ownership is set, successful
289 	 * or otherwise, set the new file mode.
290 	 */
291 	if (setownership(file, fd, uid, gid, S_ISLNK(st.st_mode)) < 0) {
292 		if (mode != -1 && IS_ON(mode, S_ISUID)) {
293 			message(MT_NOTICE,
294 				"%s: chown failed, clearing setuid", target);
295 			mode &= ~S_ISUID;
296 		}
297 		if (mode != -1 && IS_ON(mode, S_ISGID)) {
298 			message(MT_NOTICE,
299 				"%s: chown failed, clearing setgid", target);
300 			mode &= ~S_ISGID;
301 		}
302 	}
303 	(void) setfilemode(file, fd, mode, S_ISLNK(st.st_mode));
304 
305 
306 	return(0);
307 }
308 
309 /*
310  * Remove a file or directory (recursively) and send back an acknowledge
311  * or an error message.
312  */
313 static int
314 removefile(struct stat *statb, int silent)
315 {
316 	DIR *d;
317 	static DIRENTRY *dp;
318 	char *cp;
319 	struct stat stb;
320 	char *optarget;
321 	int len, failures = 0;
322 
323 	switch (statb->st_mode & S_IFMT) {
324 	case S_IFREG:
325 	case S_IFLNK:
326 	case S_IFCHR:
327 	case S_IFBLK:
328 #ifdef S_IFSOCK
329 	case S_IFSOCK:
330 #endif
331 #ifdef S_IFIFO
332 	case S_IFIFO:
333 #endif
334 		if (unlink(target) < 0) {
335 			if (errno == ETXTBSY) {
336 				if (!silent)
337 					message(MT_REMOTE|MT_NOTICE,
338 						"%s: unlink failed: %s",
339 						target, SYSERR);
340 				return(0);
341 			} else {
342 				error("%s: unlink failed: %s", target, SYSERR);
343 				return(-1);
344 			}
345 		}
346 		goto removed;
347 
348 	case S_IFDIR:
349 		break;
350 
351 	default:
352 		error("%s: not a plain file", target);
353 		return(-1);
354 	}
355 
356 	errno = 0;
357 	if ((d = opendir(target)) == NULL) {
358 		error("%s: opendir failed: %s", target, SYSERR);
359 		return(-1);
360 	}
361 
362 	optarget = ptarget;
363 	len = ptarget - target;
364 	while ((dp = readdir(d)) != NULL) {
365 		if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
366 		    (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
367 		     dp->d_name[1] == '.'))
368 			continue;
369 
370 		if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
371 			if (!silent)
372 				message(MT_REMOTE|MT_WARNING,
373 					"%s/%s: Name too long",
374 					target, dp->d_name);
375 			continue;
376 		}
377 		ptarget = optarget;
378 		*ptarget++ = '/';
379 		cp = dp->d_name;;
380 		while ((*ptarget++ = *cp++) != '\0')
381 			continue;
382 		ptarget--;
383 		if (lstat(target, &stb) < 0) {
384 			if (!silent)
385 				message(MT_REMOTE|MT_WARNING,
386 					"%s: lstat failed: %s",
387 					target, SYSERR);
388 			continue;
389 		}
390 		if (removefile(&stb, 0) < 0)
391 			++failures;
392 	}
393 	(void) closedir(d);
394 	ptarget = optarget;
395 	*ptarget = CNULL;
396 
397 	if (failures)
398 		return(-1);
399 
400 	if (rmdir(target) < 0) {
401 		error("%s: rmdir failed: %s", target, SYSERR);
402 		return(-1);
403 	}
404 removed:
405 #if NEWWAY
406 	if (!silent)
407 		message(MT_CHANGE|MT_REMOTE, "%s: removed", target);
408 #else
409 	/*
410 	 * We use MT_NOTICE instead of MT_CHANGE because this function is
411 	 * sometimes called by other functions that are suppose to return a
412 	 * single ack() back to the client (rdist).  This is a kludge until
413 	 * the Rdist protocol is re-done.  Sigh.
414 	 */
415 	message(MT_NOTICE|MT_REMOTE, "%s: removed", target);
416 #endif
417 	return(0);
418 }
419 
420 /*
421  * Check the current directory (initialized by the 'T' command to server())
422  * for extraneous files and remove them.
423  */
424 static void
425 doclean(char *cp)
426 {
427 	DIR *d;
428 	DIRENTRY *dp;
429 	struct stat stb;
430 	char *optarget, *ep;
431 	int len;
432 	opt_t opts;
433 	char targ[MAXPATHLEN*4];
434 
435 	opts = strtol(cp, &ep, 8);
436 	if (*ep != CNULL) {
437 		error("clean: options not delimited");
438 		return;
439 	}
440 	if ((d = opendir(target)) == NULL) {
441 		error("%s: opendir failed: %s", target, SYSERR);
442 		return;
443 	}
444 	ack();
445 
446 	optarget = ptarget;
447 	len = ptarget - target;
448 	while ((dp = readdir(d)) != NULL) {
449 		if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
450 		    (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
451 		     dp->d_name[1] == '.'))
452 			continue;
453 
454 		if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
455 			message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long",
456 				target, dp->d_name);
457 			continue;
458 		}
459 		ptarget = optarget;
460 		*ptarget++ = '/';
461 		cp = dp->d_name;;
462 		while ((*ptarget++ = *cp++) != '\0')
463 			continue;
464 		ptarget--;
465 		if (lstat(target, &stb) < 0) {
466 			message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s",
467 				target, SYSERR);
468 			continue;
469 		}
470 
471 		ENCODE(targ, dp->d_name);
472 		(void) sendcmd(CC_QUERY, "%s", targ);
473 		(void) remline(cp = buf, sizeof(buf), TRUE);
474 
475 		if (*cp != CC_YES)
476 			continue;
477 
478 		if (IS_ON(opts, DO_VERIFY))
479 			message(MT_REMOTE|MT_INFO, "%s: need to remove",
480 				target);
481 		else
482 			(void) removefile(&stb, 0);
483 	}
484 	(void) closedir(d);
485 
486 	ptarget = optarget;
487 	*ptarget = CNULL;
488 }
489 
490 /*
491  * Frontend to doclean().
492  */
493 static void
494 clean(char *cp)
495 {
496 	doclean(cp);
497 	(void) sendcmd(CC_END, NULL);
498 	(void) response();
499 }
500 
501 /*
502  * Execute a shell command to handle special cases.
503  * We can't really set an alarm timeout here since we
504  * have no idea how long the command should take.
505  */
506 static void
507 dospecial(char *xcmd)
508 {
509 	char cmd[BUFSIZ];
510 	if (DECODE(cmd, xcmd) == -1) {
511 		error("dospecial: Cannot decode command.");
512 		return;
513 	}
514 	runcommand(cmd);
515 }
516 
517 /*
518  * Do a special cmd command.  This differs from normal special
519  * commands in that it's done after an entire command has been updated.
520  * The list of updated target files is sent one at a time with RC_FILE
521  * commands.  Each one is added to an environment variable defined by
522  * E_FILES.  When an RC_COMMAND is finally received, the E_FILES variable
523  * is stuffed into our environment and a normal dospecial() command is run.
524  */
525 static void
526 docmdspecial(void)
527 {
528 	char *cp;
529 	char *cmd, *env = NULL;
530 	int n;
531 	size_t len;
532 
533 	/* We're ready */
534 	ack();
535 
536 	for ( ; ; ) {
537 		n = remline(cp = buf, sizeof(buf), FALSE);
538 		if (n <= 0) {
539 			error("cmdspecial: premature end of input.");
540 			return;
541 		}
542 
543 		switch (*cp++) {
544 		case RC_FILE:
545 			if (env == NULL) {
546 				len = (2 * sizeof(E_FILES)) + strlen(cp) + 10;
547 				env = (char *) xmalloc(len);
548 				(void) snprintf(env, len, "export %s;%s=%s",
549 					       E_FILES, E_FILES, cp);
550 			} else {
551 				len = strlen(env) + 1 + strlen(cp) + 1;
552 				env = (char *) xrealloc(env, len);
553 				(void) strlcat(env, ":", len);
554 				(void) strlcat(env, cp, len);
555 			}
556 			ack();
557 			break;
558 
559 		case RC_COMMAND:
560 			if (env) {
561 				len = strlen(env) + 1 + strlen(cp) + 1;
562 				env = (char *) xrealloc(env, len);
563 				(void) strlcat(env, ";", len);
564 				(void) strlcat(env, cp, len);
565 				cmd = env;
566 			} else
567 				cmd = cp;
568 
569 			dospecial(cmd);
570 			if (env)
571 				(void) free(env);
572 			return;
573 
574 		default:
575 			error("Unknown cmdspecial command '%s'.", cp);
576 			return;
577 		}
578 	}
579 }
580 
581 /*
582  * Query. Check to see if file exists. Return one of the following:
583  *
584 #ifdef NFS_CHECK
585  *  QC_ONNFS		- resides on a NFS
586 #endif NFS_CHECK
587 #ifdef RO_CHECK
588  *  QC_ONRO		- resides on a Read-Only filesystem
589 #endif RO_CHECK
590  *  QC_NO		- doesn't exist
591  *  QC_YESsize mtime 	- exists and its a regular file (size & mtime of file)
592  *  QC_YES		- exists and its a directory or symbolic link
593  *  QC_ERRMSGmessage 	- error message
594  */
595 static void
596 query(char *xname)
597 {
598 	static struct stat stb;
599 	int s = -1, stbvalid = 0;
600 	char name[MAXPATHLEN];
601 
602 	if (DECODE(name, xname) == -1) {
603 		error("query: Cannot decode filename");
604 		return;
605 	}
606 
607 	if (catname && cattarget(name) < 0)
608 		return;
609 
610 #if	defined(NFS_CHECK)
611 	if (IS_ON(options, DO_CHKNFS)) {
612 		s = is_nfs_mounted(target, &stb, &stbvalid);
613 		if (s > 0)
614 			(void) sendcmd(QC_ONNFS, NULL);
615 
616 		/* Either the above check was true or an error occurred */
617 		/* and is_nfs_mounted sent the error message */
618 		if (s != 0) {
619 			*ptarget = CNULL;
620 			return;
621 		}
622 	}
623 #endif 	/* NFS_CHECK */
624 
625 #if	defined(RO_CHECK)
626 	if (IS_ON(options, DO_CHKREADONLY)) {
627 		s = is_ro_mounted(target, &stb, &stbvalid);
628 		if (s > 0)
629 			(void) sendcmd(QC_ONRO, NULL);
630 
631 		/* Either the above check was true or an error occurred */
632 		/* and is_ro_mounted sent the error message */
633 		if (s != 0) {
634 			*ptarget = CNULL;
635 			return;
636 		}
637 	}
638 #endif 	/* RO_CHECK */
639 
640 	if (IS_ON(options, DO_CHKSYM)) {
641 		if (is_symlinked(target, &stb, &stbvalid) > 0) {
642 			(void) sendcmd(QC_SYM, NULL);
643 			return;
644 		}
645 	}
646 
647 	/*
648 	 * If stbvalid is false, "stb" is not valid because:
649 	 *	a) RO_CHECK and NFS_CHECK were not defined
650 	 *	b) The stat by is_*_mounted() either failed or
651 	 *	   does not match "target".
652 	 */
653 	if (!stbvalid && lstat(target, &stb) < 0) {
654 		if (errno == ENOENT)
655 			(void) sendcmd(QC_NO, NULL);
656 		else
657 			error("%s: lstat failed: %s", target, SYSERR);
658 		*ptarget = CNULL;
659 		return;
660 	}
661 
662 	switch (stb.st_mode & S_IFMT) {
663 	case S_IFLNK:
664 	case S_IFDIR:
665 	case S_IFREG:
666 #ifdef notyet
667 	case S_IFCHR:
668 	case S_IFBLK:
669 #ifdef S_IFSOCK
670 	case S_IFSOCK:
671 #endif
672 #ifdef S_IFIFO
673 	case S_IFIFO:
674 #endif
675 #endif
676 		(void) sendcmd(QC_YES, "%ld %ld %o %s %s",
677 			       (long) stb.st_size, stb.st_mtime,
678 			       stb.st_mode & 07777,
679 			       getusername(stb.st_uid, target, options),
680 			       getgroupname(stb.st_gid, target, options));
681 		break;
682 
683 	default:
684 		error("%s: not a file or directory", target);
685 		break;
686 	}
687 	*ptarget = CNULL;
688 }
689 
690 /*
691  * Check to see if parent directory exists and create one if not.
692  */
693 static int
694 chkparent(char *name, opt_t opts)
695 {
696 	char *cp;
697 	struct stat stb;
698 	int r = -1;
699 
700 	debugmsg(DM_CALL, "chkparent(%s, %o) start\n", name, opts);
701 
702 	cp = strrchr(name, '/');
703 	if (cp == NULL || cp == name)
704 		return(0);
705 
706 	*cp = CNULL;
707 
708 	if (lstat(name, &stb) < 0) {
709 		if (errno == ENOENT && chkparent(name, opts) >= 0) {
710 			if (mkdir(name, 0777 & ~oumask) == 0) {
711 				message(MT_NOTICE, "%s: mkdir", name);
712 				r = 0;
713 			} else
714 				debugmsg(DM_MISC,
715 					 "chkparent(%s, %o) mkdir fail: %s\n",
716 					 name, opts, SYSERR);
717 		}
718 	} else	/* It exists */
719 		r = 0;
720 
721 	/* Put back what we took away */
722 	*cp = '/';
723 
724 	return(r);
725 }
726 
727 /*
728  * Save a copy of 'file' by renaming it.
729  */
730 static char *
731 savetarget(char *file, opt_t opts)
732 {
733 	static char savefile[MAXPATHLEN];
734 
735 	if (strlen(file) + sizeof(SAVE_SUFFIX) + 1 > MAXPATHLEN) {
736 		error("%s: Cannot save: Save name too long", file);
737 		return(NULL);
738 	}
739 
740 	if (IS_ON(opts, DO_HISTORY)) {
741 		int i;
742 		struct stat st;
743 		/*
744 		 * There is a race here, but the worst that can happen
745 		 * is to lose a version of the file
746 		 */
747 		for (i = 1; i < 1000; i++) {
748 			(void) snprintf(savefile, sizeof(savefile),
749 					"%s;%.3d", file, i);
750 			if (stat(savefile, &st) == -1 && errno == ENOENT)
751 				break;
752 
753 		}
754 		if (i == 1000) {
755 			message(MT_NOTICE,
756 			    "%s: More than 1000 versions for %s; reusing 1\n",
757 				savefile, SYSERR);
758 			i = 1;
759 			(void) snprintf(savefile, sizeof(savefile),
760 					"%s;%.3d", file, i);
761 		}
762 	}
763 	else {
764 		(void) snprintf(savefile, sizeof(savefile), "%s%s",
765 				file, SAVE_SUFFIX);
766 
767 		if (unlink(savefile) != 0 && errno != ENOENT) {
768 			message(MT_NOTICE, "%s: remove failed: %s",
769 				savefile, SYSERR);
770 			return(NULL);
771 		}
772 	}
773 
774 	if (rename(file, savefile) != 0 && errno != ENOENT) {
775 		error("%s -> %s: rename failed: %s",
776 		      file, savefile, SYSERR);
777 		return(NULL);
778 	}
779 
780 	return(savefile);
781 }
782 
783 /*
784  * Receive a file
785  */
786 static void
787 recvfile(char *new, opt_t opts, int mode, char *owner, char *group,
788 	 time_t mtime, time_t atime, off_t size)
789 {
790 	int f, wrerr, olderrno;
791 	off_t i;
792 	char *cp;
793 	char *savefile = NULL;
794 	static struct stat statbuff;
795 
796 	/*
797 	 * Create temporary file
798 	 */
799 	if ((f = open(new, O_CREAT|O_EXCL|O_WRONLY, mode)) < 0) {
800 		if (errno != ENOENT || chkparent(new, opts) < 0 ||
801 		    (f = open(new, O_CREAT|O_EXCL|O_WRONLY, mode)) < 0) {
802 			error("%s: create failed: %s", new, SYSERR);
803 			(void) unlink(new);
804 			return;
805 		}
806 	}
807 
808 	/*
809 	 * Receive the file itself
810 	 */
811 	ack();
812 	wrerr = 0;
813 	olderrno = 0;
814 	for (i = 0; i < size; i += BUFSIZ) {
815 		int amt = BUFSIZ;
816 
817 		cp = buf;
818 		if (i + amt > size)
819 			amt = size - i;
820 		do {
821 			int j;
822 
823 			j = readrem(cp, amt);
824 			if (j <= 0) {
825 				(void) close(f);
826 				(void) unlink(new);
827 				fatalerr(
828 				   "Read error occurred while receiving file.");
829 				finish();
830 			}
831 			amt -= j;
832 			cp += j;
833 		} while (amt > 0);
834 		amt = BUFSIZ;
835 		if (i + amt > size)
836 			amt = size - i;
837 		if (wrerr == 0 && xwrite(f, buf, amt) != amt) {
838 			olderrno = errno;
839 			wrerr++;
840 		}
841 	}
842 
843 	if (response() < 0) {
844 		(void) close(f);
845 		(void) unlink(new);
846 		return;
847 	}
848 
849 	if (wrerr) {
850 		error("%s: Write error: %s", new, strerror(olderrno));
851 		(void) close(f);
852 		(void) unlink(new);
853 		return;
854 	}
855 
856 	/*
857 	 * Do file comparison if enabled
858 	 */
859 	if (IS_ON(opts, DO_COMPARE)) {
860 		FILE *f1, *f2;
861 		int c;
862 
863 		errno = 0;	/* fopen is not a syscall */
864 		if ((f1 = fopen(target, "r")) == NULL) {
865 			error("%s: open for read failed: %s", target, SYSERR);
866 			(void) close(f);
867 			(void) unlink(new);
868 			return;
869 		}
870 		errno = 0;
871 		if ((f2 = fopen(new, "r")) == NULL) {
872 			error("%s: open for read failed: %s", new, SYSERR);
873 			(void) fclose(f1);
874 			(void) close(f);
875 			(void) unlink(new);
876 			return;
877 		}
878 		while ((c = getc(f1)) == getc(f2))
879 			if (c == EOF) {
880 				debugmsg(DM_MISC,
881 					 "Files are the same '%s' '%s'.",
882 					 target, new);
883 				(void) fclose(f1);
884 				(void) fclose(f2);
885 				(void) close(f);
886 				(void) unlink(new);
887 				/*
888 				 * This isn't an error per-se, but we
889 				 * need to indicate to the master that
890 				 * the file was not updated.
891 				 */
892 				error("");
893 				return;
894 			}
895 		debugmsg(DM_MISC, "Files are different '%s' '%s'.",
896 			 target, new);
897 		(void) fclose(f1);
898 		(void) fclose(f2);
899 		if (IS_ON(opts, DO_VERIFY)) {
900 			message(MT_REMOTE|MT_INFO, "%s: need to update",
901 				target);
902 			(void) close(f);
903 			(void) unlink(new);
904 			return;
905 		}
906 	}
907 
908 	/*
909 	 * Set owner, group, and file mode
910 	 */
911 	if (fchog(f, new, owner, group, mode) < 0) {
912 		(void) close(f);
913 		(void) unlink(new);
914 		return;
915 	}
916 	(void) close(f);
917 
918 	/*
919 	 * Perform utimes() after file is closed to make
920 	 * certain OS's, such as NeXT 2.1, happy.
921 	 */
922 	if (setfiletime(new, time((time_t *) 0), mtime) < 0)
923 		message(MT_NOTICE, "%s: utimes failed: %s", new, SYSERR);
924 
925 	/*
926 	 * Try to save target file from being over-written
927 	 */
928 	if (IS_ON(opts, DO_SAVETARGETS))
929 		if ((savefile = savetarget(target, opts)) == NULL) {
930 			(void) unlink(new);
931 			return;
932 		}
933 
934 	/*
935 	 * If the target is a directory, we need to remove it first
936 	 * before we can rename the new file.
937 	 */
938 	if ((stat(target, &statbuff) == 0) && S_ISDIR(statbuff.st_mode)) {
939 		char *saveptr = ptarget;
940 
941 		ptarget = &target[strlen(target)];
942 		removefile(&statbuff, 0);
943 		ptarget = saveptr;
944 	}
945 
946 	/*
947 	 * Install new (temporary) file as the actual target
948 	 */
949 	if (rename(new, target) < 0) {
950 		static char fmt[] = "%s -> %s: rename failed: %s";
951 		struct stat stb;
952 		/*
953 		 * If the rename failed due to "Text file busy", then
954 		 * try to rename the target file and retry the rename.
955 		 */
956 		switch (errno) {
957 		case ETXTBSY:
958 			/* Save the target */
959 			if ((savefile = savetarget(target, opts)) != NULL) {
960 				/* Retry installing new file as target */
961 				if (rename(new, target) < 0) {
962 					error(fmt, new, target, SYSERR);
963 					/* Try to put back save file */
964 					if (rename(savefile, target) < 0)
965 						error(fmt,
966 						      savefile, target, SYSERR);
967 					(void) unlink(new);
968 				} else
969 					message(MT_NOTICE, "%s: renamed to %s",
970 						target, savefile);
971 				/*
972 				 * XXX: We should remove the savefile here.
973 				 *	But we are nice to nfs clients and
974 				 *	we keep it.
975 				 */
976 			}
977 			break;
978 		case EISDIR:
979 			/*
980 			 * See if target is a directory and remove it if it is
981 			 */
982 			if (lstat(target, &stb) == 0) {
983 				if (S_ISDIR(stb.st_mode)) {
984 					char *optarget = ptarget;
985 					for (ptarget = target; *ptarget;
986 						ptarget++);
987 					/* If we failed to remove, we'll catch
988 					   it later */
989 					(void) removefile(&stb, 1);
990 					ptarget = optarget;
991 				}
992 			}
993 			if (rename(new, target) >= 0)
994 				break;
995 			/*FALLTHROUGH*/
996 
997 		default:
998 			error(fmt, new, target, SYSERR);
999 			(void) unlink(new);
1000 			break;
1001 		}
1002 	}
1003 
1004 	if (IS_ON(opts, DO_COMPARE))
1005 		message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1006 	else
1007 		ack();
1008 }
1009 
1010 /*
1011  * Receive a directory
1012  */
1013 static void
1014 recvdir(opt_t opts, int mode, char *owner, char *group)
1015 {
1016 	static char lowner[100], lgroup[100];
1017 	char *cp;
1018 	struct stat stb;
1019 	int s;
1020 
1021 	s = lstat(target, &stb);
1022 	if (s == 0) {
1023 		/*
1024 		 * If target is not a directory, remove it
1025 		 */
1026 		if (!S_ISDIR(stb.st_mode)) {
1027 			if (IS_ON(opts, DO_VERIFY))
1028 				message(MT_NOTICE, "%s: need to remove",
1029 					target);
1030 			else {
1031 				if (unlink(target) < 0) {
1032 					error("%s: remove failed: %s",
1033 					      target, SYSERR);
1034 					return;
1035 				}
1036 			}
1037 			s = -1;
1038 			errno = ENOENT;
1039 		} else {
1040 			if (!IS_ON(opts, DO_NOCHKMODE) &&
1041 			    (stb.st_mode & 07777) != mode) {
1042 				if (IS_ON(opts, DO_VERIFY))
1043 					message(MT_NOTICE,
1044 						"%s: need to chmod to %o",
1045 						target, mode);
1046 				else {
1047 					if (chmod(target, mode) != 0)
1048 						message(MT_NOTICE,
1049 					  "%s: chmod from %o to %o failed: %s",
1050 							target,
1051 							stb.st_mode & 07777,
1052 							mode,
1053 							SYSERR);
1054 					else
1055 						message(MT_NOTICE,
1056 						"%s: chmod from %o to %o",
1057 							target,
1058 							stb.st_mode & 07777,
1059 							mode);
1060 				}
1061 			}
1062 
1063 			/*
1064 			 * Check ownership and set if necessary
1065 			 */
1066 			lowner[0] = CNULL;
1067 			lgroup[0] = CNULL;
1068 
1069 			if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
1070 				int o;
1071 
1072 				o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER :
1073 					opts;
1074 				if ((cp = getusername(stb.st_uid, target, o))
1075 				    != NULL)
1076 					if (strcmp(owner, cp))
1077 						(void) strlcpy(lowner, cp,
1078 						    sizeof(lowner));
1079 			}
1080 			if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
1081 				int o;
1082 
1083 				o = (group[0] == ':') ? opts & DO_NUMCHKGROUP :
1084 					opts;
1085 				if ((cp = getgroupname(stb.st_gid, target, o))
1086 				    != NULL)
1087 					if (strcmp(group, cp))
1088 						(void) strlcpy(lgroup, cp,
1089 						    sizeof(lgroup));
1090 			}
1091 
1092 			/*
1093 			 * Need to set owner and/or group
1094 			 */
1095 #define PRN(n) ((n[0] == ':') ? n+1 : n)
1096 			if (lowner[0] != CNULL || lgroup[0] != CNULL) {
1097 				if (lowner[0] == CNULL &&
1098 				    (cp = getusername(stb.st_uid,
1099 						      target, opts)))
1100 					(void) strlcpy(lowner, cp,
1101 					    sizeof(lowner));
1102 				if (lgroup[0] == CNULL &&
1103 				    (cp = getgroupname(stb.st_gid,
1104 						       target, opts)))
1105 					(void) strlcpy(lgroup, cp,
1106 					    sizeof(lgroup));
1107 
1108 				if (IS_ON(opts, DO_VERIFY))
1109 					message(MT_NOTICE,
1110 				"%s: need to chown from %s:%s to %s:%s",
1111 						target,
1112 						PRN(lowner), PRN(lgroup),
1113 						PRN(owner), PRN(group));
1114 				else {
1115 					if (fchog(-1, target, owner,
1116 						  group, -1) == 0)
1117 						message(MT_NOTICE,
1118 					       "%s: chown from %s:%s to %s:%s",
1119 							target,
1120 							PRN(lowner),
1121 							PRN(lgroup),
1122 							PRN(owner),
1123 							PRN(group));
1124 				}
1125 			}
1126 #undef PRN
1127 			ack();
1128 			return;
1129 		}
1130 	}
1131 
1132 	if (IS_ON(opts, DO_VERIFY)) {
1133 		ack();
1134 		return;
1135 	}
1136 
1137 	/*
1138 	 * Create the directory
1139 	 */
1140 	if (s < 0) {
1141 		if (errno == ENOENT) {
1142 			if (mkdir(target, mode) == 0 ||
1143 			    (chkparent(target, opts) == 0 &&
1144 			    mkdir(target, mode) == 0)) {
1145 				message(MT_NOTICE, "%s: mkdir", target);
1146 				(void) fchog(-1, target, owner, group, mode);
1147 				ack();
1148 			} else {
1149 				error("%s: mkdir failed: %s", target, SYSERR);
1150 				ptarget = sptarget[--catname];
1151 				*ptarget = CNULL;
1152 			}
1153 			return;
1154 		}
1155 	}
1156 	error("%s: lstat failed: %s", target, SYSERR);
1157 	ptarget = sptarget[--catname];
1158 	*ptarget = CNULL;
1159 }
1160 
1161 /*
1162  * Receive a link
1163  */
1164 static void
1165 recvlink(char *new, opt_t opts, int mode, off_t size)
1166 {
1167 	char tbuf[MAXPATHLEN];
1168 	struct stat stb;
1169 	char *optarget;
1170 	int uptodate;
1171 	off_t i;
1172 
1173 	/*
1174 	 * Read basic link info
1175 	 */
1176 	ack();
1177 	(void) remline(buf, sizeof(buf), TRUE);
1178 
1179 	if (response() < 0) {
1180 		err();
1181 		return;
1182 	}
1183 
1184 	uptodate = 0;
1185 	if ((i = readlink(target, tbuf, sizeof(tbuf)-1)) != -1) {
1186 		tbuf[i] = '\0';
1187 		if (i == size && strncmp(buf, tbuf, (int) size) == 0)
1188 			uptodate = 1;
1189 	}
1190 	mode &= 0777;
1191 
1192 	if (IS_ON(opts, DO_VERIFY) || uptodate) {
1193 		if (uptodate)
1194 			message(MT_REMOTE|MT_INFO, "");
1195 		else
1196 			message(MT_REMOTE|MT_INFO, "%s: need to update",
1197 				target);
1198 		if (IS_ON(opts, DO_COMPARE))
1199 			return;
1200 		(void) sendcmd(C_END, NULL);
1201 		(void) response();
1202 		return;
1203 	}
1204 
1205 	/*
1206 	 * Make new symlink using a temporary name
1207 	 */
1208 	if (symlink(buf, new) < 0) {
1209 		if (errno != ENOENT || chkparent(new, opts) < 0 ||
1210 		    symlink(buf, new) < 0) {
1211 			error("%s -> %s: symlink failed: %s", new, buf,SYSERR);
1212 			(void) unlink(new);
1213 			return;
1214 		}
1215 	}
1216 
1217 	/*
1218 	 * See if target is a directory and remove it if it is
1219 	 */
1220 	if (lstat(target, &stb) == 0) {
1221 		if (S_ISDIR(stb.st_mode)) {
1222 			optarget = ptarget;
1223 			for (ptarget = target; *ptarget; ptarget++);
1224 			if (removefile(&stb, 0) < 0) {
1225 				ptarget = optarget;
1226 				(void) unlink(new);
1227 				(void) sendcmd(C_END, NULL);
1228 				(void) response();
1229 				return;
1230 			}
1231 			ptarget = optarget;
1232 		}
1233 	}
1234 
1235 	/*
1236 	 * Install link as the target
1237 	 */
1238 	if (rename(new, target) < 0) {
1239 		error("%s -> %s: symlink rename failed: %s",
1240 		      new, target, SYSERR);
1241 		(void) unlink(new);
1242 		(void) sendcmd(C_END, NULL);
1243 		(void) response();
1244 		return;
1245 	}
1246 
1247 	message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1248 
1249 	/*
1250 	 * Indicate end of receive operation
1251 	 */
1252 	(void) sendcmd(C_END, NULL);
1253 	(void) response();
1254 }
1255 
1256 /*
1257  * Creat a hard link to existing file.
1258  */
1259 static void
1260 hardlink(char *cmd)
1261 {
1262 	struct stat stb;
1263 	int exists = 0;
1264 	char *xoldname, *xnewname;
1265 	char *cp = cmd;
1266 	static char expbuf[BUFSIZ];
1267 	char oldname[BUFSIZ], newname[BUFSIZ];
1268 
1269 	/* Skip over opts */
1270 	(void) strtol(cp, &cp, 8);
1271 	if (*cp++ != ' ') {
1272 		error("hardlink: options not delimited");
1273 		return;
1274 	}
1275 
1276 	xoldname = strtok(cp, " ");
1277 	if (xoldname == NULL) {
1278 		error("hardlink: oldname name not delimited");
1279 		return;
1280 	}
1281 
1282 	if (DECODE(oldname, xoldname) == -1) {
1283 		error("hardlink: Cannot decode oldname");
1284 		return;
1285 	}
1286 
1287 	xnewname = strtok(NULL, " ");
1288 	if (xnewname == NULL) {
1289 		error("hardlink: new name not specified");
1290 		return;
1291 	}
1292 
1293 	if (DECODE(newname, xnewname) == -1) {
1294 		error("hardlink: Cannot decode newname");
1295 		return;
1296 	}
1297 
1298 	if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL) {
1299 		error("hardlink: tilde expansion failed");
1300 		return;
1301 	}
1302 
1303 	if (catname && cattarget(newname) < 0) {
1304 		error("Cannot set newname target.");
1305 		return;
1306 	}
1307 
1308 	if (lstat(target, &stb) == 0) {
1309 		int mode = stb.st_mode & S_IFMT;
1310 
1311 		if (mode != S_IFREG && mode != S_IFLNK) {
1312 			error("%s: not a regular file", target);
1313 			return;
1314 		}
1315 		exists = 1;
1316 	}
1317 
1318 	if (chkparent(target, options) < 0 ) {
1319 		error("%s: no parent: %s ", target, SYSERR);
1320 		return;
1321 	}
1322 	if (exists && (unlink(target) < 0)) {
1323 		error("%s: unlink failed: %s", target, SYSERR);
1324 		return;
1325 	}
1326 	if (link(expbuf, target) < 0) {
1327 		error("%s: cannot link to %s: %s", target, oldname, SYSERR);
1328 		return;
1329 	}
1330 	ack();
1331 }
1332 
1333 /*
1334  * Set configuration information.
1335  *
1336  * A key letter is followed immediately by the value
1337  * to set.  The keys are:
1338  *	SC_FREESPACE	- Set minimium free space of filesystem
1339  *	SC_FREEFILES	- Set minimium free number of files of filesystem
1340  */
1341 static void
1342 setconfig(char *cmd)
1343 {
1344 	char *cp = cmd;
1345 	char *estr;
1346 
1347 	switch (*cp++) {
1348 	case SC_HOSTNAME:	/* Set hostname */
1349 		/*
1350 		 * Only use info if we don't know who this is.
1351 		 */
1352 		if (!fromhost) {
1353 			fromhost = xstrdup(cp);
1354 			message(MT_SYSLOG, "startup for %s", fromhost);
1355 #if defined(SETARGS) || defined(HAVE_SETPROCTITLE)
1356 			setproctitle("serving %s", cp);
1357 #endif /* SETARGS || HAVE_SETPROCTITLE */
1358 		}
1359 		break;
1360 
1361 	case SC_FREESPACE: 	/* Minimium free space */
1362 		if (!isdigit((unsigned char)*cp)) {
1363 			fatalerr("Expected digit, got '%s'.", cp);
1364 			return;
1365 		}
1366 		min_freespace = (unsigned long) atoi(cp);
1367 		break;
1368 
1369 	case SC_FREEFILES: 	/* Minimium free files */
1370 		if (!isdigit((unsigned char)*cp)) {
1371 			fatalerr("Expected digit, got '%s'.", cp);
1372 			return;
1373 		}
1374 		min_freefiles = (unsigned long) atoi(cp);
1375 		break;
1376 
1377 	case SC_LOGGING:	/* Logging options */
1378 		if ((estr = msgparseopts(cp, TRUE)) != NULL) {
1379 			fatalerr("Bad message option string (%s): %s",
1380 				 cp, estr);
1381 			return;
1382 		}
1383 		break;
1384 
1385 	case SC_DEFOWNER:
1386 		(void) strlcpy(defowner, cp, sizeof(defowner));
1387 		break;
1388 
1389 	case SC_DEFGROUP:
1390 		(void) strlcpy(defgroup, cp, sizeof(defgroup));
1391 		break;
1392 
1393 	default:
1394 		message(MT_NOTICE, "Unknown config command \"%s\".", cp-1);
1395 		return;
1396 	}
1397 }
1398 
1399 /*
1400  * Receive something
1401  */
1402 static void
1403 recvit(char *cmd, int type)
1404 {
1405 	int mode;
1406 	opt_t opts;
1407 	off_t size;
1408 	time_t mtime, atime;
1409 	char *owner, *group, *file;
1410 	char new[MAXPATHLEN];
1411 	char fileb[MAXPATHLEN];
1412 	long freespace = -1, freefiles = -1;
1413 	char *cp = cmd;
1414 
1415 	/*
1416 	 * Get rdist option flags
1417 	 */
1418 	opts = strtol(cp, &cp, 8);
1419 	if (*cp++ != ' ') {
1420 		error("recvit: options not delimited");
1421 		return;
1422 	}
1423 
1424 	/*
1425 	 * Get file mode
1426 	 */
1427 	mode = strtol(cp, &cp, 8);
1428 	if (*cp++ != ' ') {
1429 		error("recvit: mode not delimited");
1430 		return;
1431 	}
1432 
1433 	/*
1434 	 * Get file size
1435 	 */
1436 	size = strtol(cp, &cp, 10);
1437 	if (*cp++ != ' ') {
1438 		error("recvit: size not delimited");
1439 		return;
1440 	}
1441 
1442 	/*
1443 	 * Get modification time
1444 	 */
1445 	mtime = strtol(cp, &cp, 10);
1446 	if (*cp++ != ' ') {
1447 		error("recvit: mtime not delimited");
1448 		return;
1449 	}
1450 
1451 	/*
1452 	 * Get access time
1453 	 */
1454 	atime = strtol(cp, &cp, 10);
1455 	if (*cp++ != ' ') {
1456 		error("recvit: atime not delimited");
1457 		return;
1458 	}
1459 
1460 	/*
1461 	 * Get file owner name
1462 	 */
1463 	owner = strtok(cp, " ");
1464 	if (owner == NULL) {
1465 		error("recvit: owner name not delimited");
1466 		return;
1467 	}
1468 
1469 	/*
1470 	 * Get file group name
1471 	 */
1472 	group = strtok(NULL, " ");
1473 	if (group == NULL) {
1474 		error("recvit: group name not delimited");
1475 		return;
1476 	}
1477 
1478 	/*
1479 	 * Get file name. Can't use strtok() since there could
1480 	 * be white space in the file name.
1481 	 */
1482 	if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1483 		error("recvit: Cannot decode file name");
1484 		return;
1485 	}
1486 
1487 	if (fileb[0] == '\0') {
1488 		error("recvit: no file name");
1489 		return;
1490 	}
1491 	file = fileb;
1492 
1493 	debugmsg(DM_MISC,
1494 		 "recvit: opts = %04o mode = %04o size = %d mtime = %d",
1495 		 opts, mode, size, mtime);
1496 	debugmsg(DM_MISC,
1497        "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
1498 		 owner, group, file, catname, (type == S_IFDIR) ? 1 : 0);
1499 
1500 	if (type == S_IFDIR) {
1501 		if ((size_t) catname >= sizeof(sptarget)) {
1502 			error("%s: too many directory levels", target);
1503 			return;
1504 		}
1505 		sptarget[catname] = ptarget;
1506 		if (catname++) {
1507 			*ptarget++ = '/';
1508 			while ((*ptarget++ = *file++) != '\0')
1509 			    continue;
1510 			ptarget--;
1511 		}
1512 	} else {
1513 		/*
1514 		 * Create name of temporary file
1515 		 */
1516 		if (catname && cattarget(file) < 0) {
1517 			error("Cannot set file name.");
1518 			return;
1519 		}
1520 		file = strrchr(target, '/');
1521 		if (file == NULL)
1522 			(void) strlcpy(new, tempname, sizeof(new));
1523 		else if (file == target)
1524 			(void) snprintf(new, sizeof(new), "/%s", tempname);
1525 		else {
1526 			*file = CNULL;
1527 			(void) snprintf(new, sizeof(new), "%s/%s", target,
1528 					tempname);
1529 			*file = '/';
1530 		}
1531 		(void) mktemp(new);
1532 	}
1533 
1534 	/*
1535 	 * Check to see if there is enough free space and inodes
1536 	 * to install this file.
1537 	 */
1538 	if (min_freespace || min_freefiles) {
1539 		/* Convert file size to kilobytes */
1540 		long fsize = (long) (size / 1024);
1541 
1542 		if (getfilesysinfo(target, &freespace, &freefiles) != 0)
1543 			return;
1544 
1545 		/*
1546 		 * filesystem values < 0 indicate unsupported or unavailable
1547 		 * information.
1548 		 */
1549 		if (min_freespace && (freespace >= 0) &&
1550 		    (freespace - fsize < min_freespace)) {
1551 			error(
1552 		     "%s: Not enough free space on filesystem: min %d free %d",
1553 			      target, min_freespace, freespace);
1554 			return;
1555 		}
1556 		if (min_freefiles && (freefiles >= 0) &&
1557 		    (freefiles - 1 < min_freefiles)) {
1558 			error(
1559 		     "%s: Not enough free files on filesystem: min %d free %d",
1560 			      target, min_freefiles, freefiles);
1561 			return;
1562 		}
1563 	}
1564 
1565 	/*
1566 	 * Call appropriate receive function to receive file
1567 	 */
1568 	switch (type) {
1569 	case S_IFDIR:
1570 		recvdir(opts, mode, owner, group);
1571 		break;
1572 
1573 	case S_IFLNK:
1574 		recvlink(new, opts, mode, size);
1575 		break;
1576 
1577 	case S_IFREG:
1578 		recvfile(new, opts, mode, owner, group, mtime, atime, size);
1579 		break;
1580 
1581 	default:
1582 		error("%d: unknown file type", type);
1583 		break;
1584 	}
1585 }
1586 
1587 /*
1588  * Chmog something
1589  */
1590 static void
1591 dochmog(char *cmd)
1592 {
1593 	int mode;
1594 	opt_t opts;
1595 	char *owner, *group, *file;
1596 	char *cp = cmd;
1597 	char fileb[MAXPATHLEN];
1598 
1599 	/*
1600 	 * Get rdist option flags
1601 	 */
1602 	opts = strtol(cp, &cp, 8);
1603 	if (*cp++ != ' ') {
1604 		error("dochmog: options not delimited");
1605 		return;
1606 	}
1607 
1608 	/*
1609 	 * Get file mode
1610 	 */
1611 	mode = strtol(cp, &cp, 8);
1612 	if (*cp++ != ' ') {
1613 		error("dochmog: mode not delimited");
1614 		return;
1615 	}
1616 
1617 	/*
1618 	 * Get file owner name
1619 	 */
1620 	owner = strtok(cp, " ");
1621 	if (owner == NULL) {
1622 		error("dochmog: owner name not delimited");
1623 		return;
1624 	}
1625 
1626 	/*
1627 	 * Get file group name
1628 	 */
1629 	group = strtok(NULL, " ");
1630 	if (group == NULL) {
1631 		error("dochmog: group name not delimited");
1632 		return;
1633 	}
1634 
1635 	/*
1636 	 * Get file name. Can't use strtok() since there could
1637 	 * be white space in the file name.
1638 	 */
1639 	if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1640 		error("dochmog: Cannot decode file name");
1641 		return;
1642 	}
1643 
1644 	if (fileb[0] == '\0') {
1645 		error("dochmog: no file name");
1646 		return;
1647 	}
1648 	file = fileb;
1649 
1650 	debugmsg(DM_MISC,
1651 		 "dochmog: opts = %04o mode = %04o", opts, mode);
1652 	debugmsg(DM_MISC,
1653 	         "dochmog: owner = '%s' group = '%s' file = '%s' catname = %d",
1654 		 owner, group, file, catname);
1655 
1656 	if (catname && cattarget(file) < 0) {
1657 		error("Cannot set newname target.");
1658 		return;
1659 	}
1660 
1661 	(void) fchog(-1, target, owner, group, mode);
1662 
1663 	ack();
1664 }
1665 
1666 /*
1667  * Set target information
1668  */
1669 static void
1670 settarget(char *cmd, int isdir)
1671 {
1672 	char *cp = cmd;
1673 	opt_t opts;
1674 	char file[BUFSIZ];
1675 
1676 	catname = isdir;
1677 
1678 	/*
1679 	 * Parse options for this target
1680 	 */
1681 	opts = strtol(cp, &cp, 8);
1682 	if (*cp++ != ' ') {
1683 		error("settarget: options not delimited");
1684 		return;
1685 	}
1686 	options = opts;
1687 
1688 	if (DECODE(file, cp) == -1) {
1689 		error("settarget: Cannot decode target name");
1690 		return;
1691 	}
1692 
1693 	/*
1694 	 * Handle target
1695 	 */
1696 	if (exptilde(target, cp, sizeof(target)) == NULL)
1697 		return;
1698 	ptarget = target;
1699 	while (*ptarget)
1700 		ptarget++;
1701 
1702 	ack();
1703 }
1704 
1705 /*
1706  * Cleanup in preparation for exiting.
1707  */
1708 void
1709 cleanup(int dummy)
1710 {
1711 	/* We don't need to do anything */
1712 }
1713 
1714 /*
1715  * Server routine to read requests and process them.
1716  */
1717 void
1718 server(void)
1719 {
1720 	static char cmdbuf[BUFSIZ];
1721 	char *cp;
1722 	int n;
1723 	extern jmp_buf finish_jmpbuf;
1724 
1725 	if (setjmp(finish_jmpbuf))
1726 		return;
1727 	(void) signal(SIGHUP, sighandler);
1728 	(void) signal(SIGINT, sighandler);
1729 	(void) signal(SIGQUIT, sighandler);
1730 	(void) signal(SIGTERM, sighandler);
1731 	(void) signal(SIGPIPE, sighandler);
1732 	(void) umask(oumask = umask(0));
1733 	(void) strlcpy(tempname, _RDIST_TMP, sizeof(tempname));
1734 	if (fromhost) {
1735 		message(MT_SYSLOG, "Startup for %s", fromhost);
1736 #if 	defined(SETARGS)
1737 		setproctitle("Serving %s", fromhost);
1738 #endif	/* SETARGS */
1739 	}
1740 
1741 	/*
1742 	 * Let client know we want it to send it's version number
1743 	 */
1744 	(void) sendcmd(S_VERSION, NULL);
1745 
1746 	if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) {
1747 		error("server: expected control record");
1748 		return;
1749 	}
1750 
1751 	if (cmdbuf[0] != S_VERSION || !isdigit((unsigned char)cmdbuf[1])) {
1752 		error("Expected version command, received: \"%s\".", cmdbuf);
1753 		return;
1754 	}
1755 
1756 	proto_version = atoi(&cmdbuf[1]);
1757 	if (proto_version != VERSION) {
1758 		error("Protocol version %d is not supported.", proto_version);
1759 		return;
1760 	}
1761 
1762 	/* Version number is okay */
1763 	ack();
1764 
1765 	/*
1766 	 * Main command loop
1767 	 */
1768 	for ( ; ; ) {
1769 		n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE);
1770 		if (n == -1)		/* EOF */
1771 			return;
1772 		if (n == 0) {
1773 			error("server: expected control record");
1774 			continue;
1775 		}
1776 
1777 		switch (*cp++) {
1778 		case C_SETCONFIG:  	/* Configuration info */
1779 		        setconfig(cp);
1780 			ack();
1781 			continue;
1782 
1783 		case C_DIRTARGET:  	/* init target file/directory name */
1784 			settarget(cp, TRUE);
1785 			continue;
1786 
1787 		case C_TARGET:  	/* init target file/directory name */
1788 			settarget(cp, FALSE);
1789 			continue;
1790 
1791 		case C_RECVREG:  	/* Transfer a regular file. */
1792 			recvit(cp, S_IFREG);
1793 			continue;
1794 
1795 		case C_RECVDIR:  	/* Transfer a directory. */
1796 			recvit(cp, S_IFDIR);
1797 			continue;
1798 
1799 		case C_RECVSYMLINK:  	/* Transfer symbolic link. */
1800 			recvit(cp, S_IFLNK);
1801 			continue;
1802 
1803 		case C_RECVHARDLINK:  	/* Transfer hard link. */
1804 			hardlink(cp);
1805 			continue;
1806 
1807 		case C_END:  		/* End of transfer */
1808 			*ptarget = CNULL;
1809 			if (catname <= 0) {
1810 				error("server: too many '%c's", C_END);
1811 				continue;
1812 			}
1813 			ptarget = sptarget[--catname];
1814 			*ptarget = CNULL;
1815 			ack();
1816 			continue;
1817 
1818 		case C_CLEAN:  		/* Clean. Cleanup a directory */
1819 			clean(cp);
1820 			continue;
1821 
1822 		case C_QUERY:  		/* Query file/directory */
1823 			query(cp);
1824 			continue;
1825 
1826 		case C_SPECIAL:  	/* Special. Execute commands */
1827 			dospecial(cp);
1828 			continue;
1829 
1830 		case C_CMDSPECIAL:  	/* Cmd Special. Execute commands */
1831 			docmdspecial();
1832 			continue;
1833 
1834 	        case C_CHMOG:  		/* Set owner, group, mode */
1835 			dochmog(cp);
1836 			continue;
1837 
1838 		case C_ERRMSG:		/* Normal error message */
1839 			if (cp && *cp)
1840 				message(MT_NERROR|MT_NOREMOTE, "%s", cp);
1841 			continue;
1842 
1843 		case C_FERRMSG:		/* Fatal error message */
1844 			if (cp && *cp)
1845 				message(MT_FERROR|MT_NOREMOTE, "%s", cp);
1846 			return;
1847 
1848 		default:
1849 			error("server: unknown command '%s'", cp - 1);
1850 		case CNULL:
1851 			continue;
1852 		}
1853 	}
1854 }
1855