xref: /openbsd-src/usr.bin/rdistd/server.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: server.c,v 1.20 2009/03/28 15:18:29 deraadt 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.20 2009/03/28 15:18:29 deraadt 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 = mkstemp(new)) < 0) {
800 		if (errno != ENOENT || chkparent(new, opts) < 0 ||
801 		    (f = mkstemp(new)) < 0) {
802 			error("%s: create failed: %s", new, SYSERR);
803 			return;
804 		}
805 	}
806 
807 	/*
808 	 * Receive the file itself
809 	 */
810 	ack();
811 	wrerr = 0;
812 	olderrno = 0;
813 	for (i = 0; i < size; i += BUFSIZ) {
814 		int amt = BUFSIZ;
815 
816 		cp = buf;
817 		if (i + amt > size)
818 			amt = size - i;
819 		do {
820 			int j;
821 
822 			j = readrem(cp, amt);
823 			if (j <= 0) {
824 				(void) close(f);
825 				(void) unlink(new);
826 				fatalerr(
827 				   "Read error occurred while receiving file.");
828 				finish();
829 			}
830 			amt -= j;
831 			cp += j;
832 		} while (amt > 0);
833 		amt = BUFSIZ;
834 		if (i + amt > size)
835 			amt = size - i;
836 		if (wrerr == 0 && xwrite(f, buf, amt) != amt) {
837 			olderrno = errno;
838 			wrerr++;
839 		}
840 	}
841 
842 	if (response() < 0) {
843 		(void) close(f);
844 		(void) unlink(new);
845 		return;
846 	}
847 
848 	if (wrerr) {
849 		error("%s: Write error: %s", new, strerror(olderrno));
850 		(void) close(f);
851 		(void) unlink(new);
852 		return;
853 	}
854 
855 	/*
856 	 * Do file comparison if enabled
857 	 */
858 	if (IS_ON(opts, DO_COMPARE)) {
859 		FILE *f1, *f2;
860 		int c;
861 
862 		errno = 0;	/* fopen is not a syscall */
863 		if ((f1 = fopen(target, "r")) == NULL) {
864 			error("%s: open for read failed: %s", target, SYSERR);
865 			(void) close(f);
866 			(void) unlink(new);
867 			return;
868 		}
869 		errno = 0;
870 		if ((f2 = fopen(new, "r")) == NULL) {
871 			error("%s: open for read failed: %s", new, SYSERR);
872 			(void) fclose(f1);
873 			(void) close(f);
874 			(void) unlink(new);
875 			return;
876 		}
877 		while ((c = getc(f1)) == getc(f2))
878 			if (c == EOF) {
879 				debugmsg(DM_MISC,
880 					 "Files are the same '%s' '%s'.",
881 					 target, new);
882 				(void) fclose(f1);
883 				(void) fclose(f2);
884 				(void) close(f);
885 				(void) unlink(new);
886 				/*
887 				 * This isn't an error per-se, but we
888 				 * need to indicate to the master that
889 				 * the file was not updated.
890 				 */
891 				error("");
892 				return;
893 			}
894 		debugmsg(DM_MISC, "Files are different '%s' '%s'.",
895 			 target, new);
896 		(void) fclose(f1);
897 		(void) fclose(f2);
898 		if (IS_ON(opts, DO_VERIFY)) {
899 			message(MT_REMOTE|MT_INFO, "%s: need to update",
900 				target);
901 			(void) close(f);
902 			(void) unlink(new);
903 			return;
904 		}
905 	}
906 
907 	/*
908 	 * Set owner, group, and file mode
909 	 */
910 	if (fchog(f, new, owner, group, mode) < 0) {
911 		(void) close(f);
912 		(void) unlink(new);
913 		return;
914 	}
915 	(void) close(f);
916 
917 	/*
918 	 * Perform utimes() after file is closed to make
919 	 * certain OS's, such as NeXT 2.1, happy.
920 	 */
921 	if (setfiletime(new, time((time_t *) 0), mtime) < 0)
922 		message(MT_NOTICE, "%s: utimes failed: %s", new, SYSERR);
923 
924 	/*
925 	 * Try to save target file from being over-written
926 	 */
927 	if (IS_ON(opts, DO_SAVETARGETS))
928 		if ((savefile = savetarget(target, opts)) == NULL) {
929 			(void) unlink(new);
930 			return;
931 		}
932 
933 	/*
934 	 * If the target is a directory, we need to remove it first
935 	 * before we can rename the new file.
936 	 */
937 	if ((stat(target, &statbuff) == 0) && S_ISDIR(statbuff.st_mode)) {
938 		char *saveptr = ptarget;
939 
940 		ptarget = &target[strlen(target)];
941 		removefile(&statbuff, 0);
942 		ptarget = saveptr;
943 	}
944 
945 	/*
946 	 * Install new (temporary) file as the actual target
947 	 */
948 	if (rename(new, target) < 0) {
949 		static char fmt[] = "%s -> %s: rename failed: %s";
950 		struct stat stb;
951 		/*
952 		 * If the rename failed due to "Text file busy", then
953 		 * try to rename the target file and retry the rename.
954 		 */
955 		switch (errno) {
956 		case ETXTBSY:
957 			/* Save the target */
958 			if ((savefile = savetarget(target, opts)) != NULL) {
959 				/* Retry installing new file as target */
960 				if (rename(new, target) < 0) {
961 					error(fmt, new, target, SYSERR);
962 					/* Try to put back save file */
963 					if (rename(savefile, target) < 0)
964 						error(fmt,
965 						      savefile, target, SYSERR);
966 					(void) unlink(new);
967 				} else
968 					message(MT_NOTICE, "%s: renamed to %s",
969 						target, savefile);
970 				/*
971 				 * XXX: We should remove the savefile here.
972 				 *	But we are nice to nfs clients and
973 				 *	we keep it.
974 				 */
975 			}
976 			break;
977 		case EISDIR:
978 			/*
979 			 * See if target is a directory and remove it if it is
980 			 */
981 			if (lstat(target, &stb) == 0) {
982 				if (S_ISDIR(stb.st_mode)) {
983 					char *optarget = ptarget;
984 					for (ptarget = target; *ptarget;
985 						ptarget++);
986 					/* If we failed to remove, we'll catch
987 					   it later */
988 					(void) removefile(&stb, 1);
989 					ptarget = optarget;
990 				}
991 			}
992 			if (rename(new, target) >= 0)
993 				break;
994 			/*FALLTHROUGH*/
995 
996 		default:
997 			error(fmt, new, target, SYSERR);
998 			(void) unlink(new);
999 			break;
1000 		}
1001 	}
1002 
1003 	if (IS_ON(opts, DO_COMPARE))
1004 		message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1005 	else
1006 		ack();
1007 }
1008 
1009 /*
1010  * Receive a directory
1011  */
1012 static void
1013 recvdir(opt_t opts, int mode, char *owner, char *group)
1014 {
1015 	static char lowner[100], lgroup[100];
1016 	char *cp;
1017 	struct stat stb;
1018 	int s;
1019 
1020 	s = lstat(target, &stb);
1021 	if (s == 0) {
1022 		/*
1023 		 * If target is not a directory, remove it
1024 		 */
1025 		if (!S_ISDIR(stb.st_mode)) {
1026 			if (IS_ON(opts, DO_VERIFY))
1027 				message(MT_NOTICE, "%s: need to remove",
1028 					target);
1029 			else {
1030 				if (unlink(target) < 0) {
1031 					error("%s: remove failed: %s",
1032 					      target, SYSERR);
1033 					return;
1034 				}
1035 			}
1036 			s = -1;
1037 			errno = ENOENT;
1038 		} else {
1039 			if (!IS_ON(opts, DO_NOCHKMODE) &&
1040 			    (stb.st_mode & 07777) != mode) {
1041 				if (IS_ON(opts, DO_VERIFY))
1042 					message(MT_NOTICE,
1043 						"%s: need to chmod to %o",
1044 						target, mode);
1045 				else {
1046 					if (chmod(target, mode) != 0)
1047 						message(MT_NOTICE,
1048 					  "%s: chmod from %o to %o failed: %s",
1049 							target,
1050 							stb.st_mode & 07777,
1051 							mode,
1052 							SYSERR);
1053 					else
1054 						message(MT_NOTICE,
1055 						"%s: chmod from %o to %o",
1056 							target,
1057 							stb.st_mode & 07777,
1058 							mode);
1059 				}
1060 			}
1061 
1062 			/*
1063 			 * Check ownership and set if necessary
1064 			 */
1065 			lowner[0] = CNULL;
1066 			lgroup[0] = CNULL;
1067 
1068 			if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
1069 				int o;
1070 
1071 				o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER :
1072 					opts;
1073 				if ((cp = getusername(stb.st_uid, target, o))
1074 				    != NULL)
1075 					if (strcmp(owner, cp))
1076 						(void) strlcpy(lowner, cp,
1077 						    sizeof(lowner));
1078 			}
1079 			if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
1080 				int o;
1081 
1082 				o = (group[0] == ':') ? opts & DO_NUMCHKGROUP :
1083 					opts;
1084 				if ((cp = getgroupname(stb.st_gid, target, o))
1085 				    != NULL)
1086 					if (strcmp(group, cp))
1087 						(void) strlcpy(lgroup, cp,
1088 						    sizeof(lgroup));
1089 			}
1090 
1091 			/*
1092 			 * Need to set owner and/or group
1093 			 */
1094 #define PRN(n) ((n[0] == ':') ? n+1 : n)
1095 			if (lowner[0] != CNULL || lgroup[0] != CNULL) {
1096 				if (lowner[0] == CNULL &&
1097 				    (cp = getusername(stb.st_uid,
1098 						      target, opts)))
1099 					(void) strlcpy(lowner, cp,
1100 					    sizeof(lowner));
1101 				if (lgroup[0] == CNULL &&
1102 				    (cp = getgroupname(stb.st_gid,
1103 						       target, opts)))
1104 					(void) strlcpy(lgroup, cp,
1105 					    sizeof(lgroup));
1106 
1107 				if (IS_ON(opts, DO_VERIFY))
1108 					message(MT_NOTICE,
1109 				"%s: need to chown from %s:%s to %s:%s",
1110 						target,
1111 						PRN(lowner), PRN(lgroup),
1112 						PRN(owner), PRN(group));
1113 				else {
1114 					if (fchog(-1, target, owner,
1115 						  group, -1) == 0)
1116 						message(MT_NOTICE,
1117 					       "%s: chown from %s:%s to %s:%s",
1118 							target,
1119 							PRN(lowner),
1120 							PRN(lgroup),
1121 							PRN(owner),
1122 							PRN(group));
1123 				}
1124 			}
1125 #undef PRN
1126 			ack();
1127 			return;
1128 		}
1129 	}
1130 
1131 	if (IS_ON(opts, DO_VERIFY)) {
1132 		ack();
1133 		return;
1134 	}
1135 
1136 	/*
1137 	 * Create the directory
1138 	 */
1139 	if (s < 0) {
1140 		if (errno == ENOENT) {
1141 			if (mkdir(target, mode) == 0 ||
1142 			    (chkparent(target, opts) == 0 &&
1143 			    mkdir(target, mode) == 0)) {
1144 				message(MT_NOTICE, "%s: mkdir", target);
1145 				(void) fchog(-1, target, owner, group, mode);
1146 				ack();
1147 			} else {
1148 				error("%s: mkdir failed: %s", target, SYSERR);
1149 				ptarget = sptarget[--catname];
1150 				*ptarget = CNULL;
1151 			}
1152 			return;
1153 		}
1154 	}
1155 	error("%s: lstat failed: %s", target, SYSERR);
1156 	ptarget = sptarget[--catname];
1157 	*ptarget = CNULL;
1158 }
1159 
1160 /*
1161  * Receive a link
1162  */
1163 static void
1164 recvlink(char *new, opt_t opts, int mode, off_t size)
1165 {
1166 	char tbuf[MAXPATHLEN], dbuf[BUFSIZ];
1167 	struct stat stb;
1168 	char *optarget;
1169 	int uptodate;
1170 	off_t i;
1171 
1172 	/*
1173 	 * Read basic link info
1174 	 */
1175 	ack();
1176 	(void) remline(buf, sizeof(buf), TRUE);
1177 
1178 	if (response() < 0) {
1179 		err();
1180 		return;
1181 	}
1182 
1183 	if (DECODE(dbuf, buf) == -1) {
1184 		error("recvlink: cannot decode symlink target");
1185 		return;
1186 	}
1187 
1188 	uptodate = 0;
1189 	if ((i = readlink(target, tbuf, sizeof(tbuf)-1)) != -1) {
1190 		tbuf[i] = '\0';
1191 		if (i == size && strncmp(dbuf, tbuf, (int) size) == 0)
1192 			uptodate = 1;
1193 	}
1194 	mode &= 0777;
1195 
1196 	if (IS_ON(opts, DO_VERIFY) || uptodate) {
1197 		if (uptodate)
1198 			message(MT_REMOTE|MT_INFO, "");
1199 		else
1200 			message(MT_REMOTE|MT_INFO, "%s: need to update",
1201 				target);
1202 		if (IS_ON(opts, DO_COMPARE))
1203 			return;
1204 		(void) sendcmd(C_END, NULL);
1205 		(void) response();
1206 		return;
1207 	}
1208 
1209 	/*
1210 	 * Make new symlink using a temporary name
1211 	 */
1212 	if (mktemp(new) == NULL || symlink(dbuf, new) < 0) {
1213 		if (errno != ENOENT || chkparent(new, opts) < 0 ||
1214 		    mktemp(new) == NULL || symlink(dbuf, new) < 0) {
1215 			error("%s -> %s: symlink failed: %s", new, dbuf,
1216 			    SYSERR);
1217 			return;
1218 		}
1219 	}
1220 
1221 	/*
1222 	 * See if target is a directory and remove it if it is
1223 	 */
1224 	if (lstat(target, &stb) == 0) {
1225 		if (S_ISDIR(stb.st_mode)) {
1226 			optarget = ptarget;
1227 			for (ptarget = target; *ptarget; ptarget++);
1228 			if (removefile(&stb, 0) < 0) {
1229 				ptarget = optarget;
1230 				(void) unlink(new);
1231 				(void) sendcmd(C_END, NULL);
1232 				(void) response();
1233 				return;
1234 			}
1235 			ptarget = optarget;
1236 		}
1237 	}
1238 
1239 	/*
1240 	 * Install link as the target
1241 	 */
1242 	if (rename(new, target) < 0) {
1243 		error("%s -> %s: symlink rename failed: %s",
1244 		      new, target, SYSERR);
1245 		(void) unlink(new);
1246 		(void) sendcmd(C_END, NULL);
1247 		(void) response();
1248 		return;
1249 	}
1250 
1251 	message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1252 
1253 	/*
1254 	 * Indicate end of receive operation
1255 	 */
1256 	(void) sendcmd(C_END, NULL);
1257 	(void) response();
1258 }
1259 
1260 /*
1261  * Creat a hard link to existing file.
1262  */
1263 static void
1264 hardlink(char *cmd)
1265 {
1266 	struct stat stb;
1267 	int exists = 0;
1268 	char *xoldname, *xnewname;
1269 	char *cp = cmd;
1270 	static char expbuf[BUFSIZ];
1271 	char oldname[BUFSIZ], newname[BUFSIZ];
1272 
1273 	/* Skip over opts */
1274 	(void) strtol(cp, &cp, 8);
1275 	if (*cp++ != ' ') {
1276 		error("hardlink: options not delimited");
1277 		return;
1278 	}
1279 
1280 	xoldname = strtok(cp, " ");
1281 	if (xoldname == NULL) {
1282 		error("hardlink: oldname name not delimited");
1283 		return;
1284 	}
1285 
1286 	if (DECODE(oldname, xoldname) == -1) {
1287 		error("hardlink: Cannot decode oldname");
1288 		return;
1289 	}
1290 
1291 	xnewname = strtok(NULL, " ");
1292 	if (xnewname == NULL) {
1293 		error("hardlink: new name not specified");
1294 		return;
1295 	}
1296 
1297 	if (DECODE(newname, xnewname) == -1) {
1298 		error("hardlink: Cannot decode newname");
1299 		return;
1300 	}
1301 
1302 	if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL) {
1303 		error("hardlink: tilde expansion failed");
1304 		return;
1305 	}
1306 
1307 	if (catname && cattarget(newname) < 0) {
1308 		error("Cannot set newname target.");
1309 		return;
1310 	}
1311 
1312 	if (lstat(target, &stb) == 0) {
1313 		int mode = stb.st_mode & S_IFMT;
1314 
1315 		if (mode != S_IFREG && mode != S_IFLNK) {
1316 			error("%s: not a regular file", target);
1317 			return;
1318 		}
1319 		exists = 1;
1320 	}
1321 
1322 	if (chkparent(target, options) < 0 ) {
1323 		error("%s: no parent: %s ", target, SYSERR);
1324 		return;
1325 	}
1326 	if (exists && (unlink(target) < 0)) {
1327 		error("%s: unlink failed: %s", target, SYSERR);
1328 		return;
1329 	}
1330 	if (link(expbuf, target) < 0) {
1331 		error("%s: cannot link to %s: %s", target, oldname, SYSERR);
1332 		return;
1333 	}
1334 	ack();
1335 }
1336 
1337 /*
1338  * Set configuration information.
1339  *
1340  * A key letter is followed immediately by the value
1341  * to set.  The keys are:
1342  *	SC_FREESPACE	- Set minimium free space of filesystem
1343  *	SC_FREEFILES	- Set minimium free number of files of filesystem
1344  */
1345 static void
1346 setconfig(char *cmd)
1347 {
1348 	char *cp = cmd;
1349 	char *estr;
1350 
1351 	switch (*cp++) {
1352 	case SC_HOSTNAME:	/* Set hostname */
1353 		/*
1354 		 * Only use info if we don't know who this is.
1355 		 */
1356 		if (!fromhost) {
1357 			fromhost = xstrdup(cp);
1358 			message(MT_SYSLOG, "startup for %s", fromhost);
1359 #if defined(SETARGS) || defined(HAVE_SETPROCTITLE)
1360 			setproctitle("serving %s", cp);
1361 #endif /* SETARGS || HAVE_SETPROCTITLE */
1362 		}
1363 		break;
1364 
1365 	case SC_FREESPACE: 	/* Minimium free space */
1366 		if (!isdigit((unsigned char)*cp)) {
1367 			fatalerr("Expected digit, got '%s'.", cp);
1368 			return;
1369 		}
1370 		min_freespace = (unsigned long) atoi(cp);
1371 		break;
1372 
1373 	case SC_FREEFILES: 	/* Minimium free files */
1374 		if (!isdigit((unsigned char)*cp)) {
1375 			fatalerr("Expected digit, got '%s'.", cp);
1376 			return;
1377 		}
1378 		min_freefiles = (unsigned long) atoi(cp);
1379 		break;
1380 
1381 	case SC_LOGGING:	/* Logging options */
1382 		if ((estr = msgparseopts(cp, TRUE)) != NULL) {
1383 			fatalerr("Bad message option string (%s): %s",
1384 				 cp, estr);
1385 			return;
1386 		}
1387 		break;
1388 
1389 	case SC_DEFOWNER:
1390 		(void) strlcpy(defowner, cp, sizeof(defowner));
1391 		break;
1392 
1393 	case SC_DEFGROUP:
1394 		(void) strlcpy(defgroup, cp, sizeof(defgroup));
1395 		break;
1396 
1397 	default:
1398 		message(MT_NOTICE, "Unknown config command \"%s\".", cp-1);
1399 		return;
1400 	}
1401 }
1402 
1403 /*
1404  * Receive something
1405  */
1406 static void
1407 recvit(char *cmd, int type)
1408 {
1409 	int mode;
1410 	opt_t opts;
1411 	off_t size;
1412 	time_t mtime, atime;
1413 	char *owner, *group, *file;
1414 	char new[MAXPATHLEN];
1415 	char fileb[MAXPATHLEN];
1416 	long freespace = -1, freefiles = -1;
1417 	char *cp = cmd;
1418 
1419 	/*
1420 	 * Get rdist option flags
1421 	 */
1422 	opts = strtol(cp, &cp, 8);
1423 	if (*cp++ != ' ') {
1424 		error("recvit: options not delimited");
1425 		return;
1426 	}
1427 
1428 	/*
1429 	 * Get file mode
1430 	 */
1431 	mode = strtol(cp, &cp, 8);
1432 	if (*cp++ != ' ') {
1433 		error("recvit: mode not delimited");
1434 		return;
1435 	}
1436 
1437 	/*
1438 	 * Get file size
1439 	 */
1440 	size = strtol(cp, &cp, 10);
1441 	if (*cp++ != ' ') {
1442 		error("recvit: size not delimited");
1443 		return;
1444 	}
1445 
1446 	/*
1447 	 * Get modification time
1448 	 */
1449 	mtime = strtol(cp, &cp, 10);
1450 	if (*cp++ != ' ') {
1451 		error("recvit: mtime not delimited");
1452 		return;
1453 	}
1454 
1455 	/*
1456 	 * Get access time
1457 	 */
1458 	atime = strtol(cp, &cp, 10);
1459 	if (*cp++ != ' ') {
1460 		error("recvit: atime not delimited");
1461 		return;
1462 	}
1463 
1464 	/*
1465 	 * Get file owner name
1466 	 */
1467 	owner = strtok(cp, " ");
1468 	if (owner == NULL) {
1469 		error("recvit: owner name not delimited");
1470 		return;
1471 	}
1472 
1473 	/*
1474 	 * Get file group name
1475 	 */
1476 	group = strtok(NULL, " ");
1477 	if (group == NULL) {
1478 		error("recvit: group name not delimited");
1479 		return;
1480 	}
1481 
1482 	/*
1483 	 * Get file name. Can't use strtok() since there could
1484 	 * be white space in the file name.
1485 	 */
1486 	if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1487 		error("recvit: Cannot decode file name");
1488 		return;
1489 	}
1490 
1491 	if (fileb[0] == '\0') {
1492 		error("recvit: no file name");
1493 		return;
1494 	}
1495 	file = fileb;
1496 
1497 	debugmsg(DM_MISC,
1498 		 "recvit: opts = %04o mode = %04o size = %d mtime = %d",
1499 		 opts, mode, size, mtime);
1500 	debugmsg(DM_MISC,
1501        "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
1502 		 owner, group, file, catname, (type == S_IFDIR) ? 1 : 0);
1503 
1504 	if (type == S_IFDIR) {
1505 		if ((size_t) catname >= sizeof(sptarget)) {
1506 			error("%s: too many directory levels", target);
1507 			return;
1508 		}
1509 		sptarget[catname] = ptarget;
1510 		if (catname++) {
1511 			*ptarget++ = '/';
1512 			while ((*ptarget++ = *file++) != '\0')
1513 			    continue;
1514 			ptarget--;
1515 		}
1516 	} else {
1517 		/*
1518 		 * Create name of temporary file
1519 		 */
1520 		if (catname && cattarget(file) < 0) {
1521 			error("Cannot set file name.");
1522 			return;
1523 		}
1524 		file = strrchr(target, '/');
1525 		if (file == NULL)
1526 			(void) strlcpy(new, tempname, sizeof(new));
1527 		else if (file == target)
1528 			(void) snprintf(new, sizeof(new), "/%s", tempname);
1529 		else {
1530 			*file = CNULL;
1531 			(void) snprintf(new, sizeof(new), "%s/%s", target,
1532 					tempname);
1533 			*file = '/';
1534 		}
1535 	}
1536 
1537 	/*
1538 	 * Check to see if there is enough free space and inodes
1539 	 * to install this file.
1540 	 */
1541 	if (min_freespace || min_freefiles) {
1542 		/* Convert file size to kilobytes */
1543 		long fsize = (long) (size / 1024);
1544 
1545 		if (getfilesysinfo(target, &freespace, &freefiles) != 0)
1546 			return;
1547 
1548 		/*
1549 		 * filesystem values < 0 indicate unsupported or unavailable
1550 		 * information.
1551 		 */
1552 		if (min_freespace && (freespace >= 0) &&
1553 		    (freespace - fsize < min_freespace)) {
1554 			error(
1555 		     "%s: Not enough free space on filesystem: min %d free %d",
1556 			      target, min_freespace, freespace);
1557 			return;
1558 		}
1559 		if (min_freefiles && (freefiles >= 0) &&
1560 		    (freefiles - 1 < min_freefiles)) {
1561 			error(
1562 		     "%s: Not enough free files on filesystem: min %d free %d",
1563 			      target, min_freefiles, freefiles);
1564 			return;
1565 		}
1566 	}
1567 
1568 	/*
1569 	 * Call appropriate receive function to receive file
1570 	 */
1571 	switch (type) {
1572 	case S_IFDIR:
1573 		recvdir(opts, mode, owner, group);
1574 		break;
1575 
1576 	case S_IFLNK:
1577 		recvlink(new, opts, mode, size);
1578 		break;
1579 
1580 	case S_IFREG:
1581 		recvfile(new, opts, mode, owner, group, mtime, atime, size);
1582 		break;
1583 
1584 	default:
1585 		error("%d: unknown file type", type);
1586 		break;
1587 	}
1588 }
1589 
1590 /*
1591  * Chmog something
1592  */
1593 static void
1594 dochmog(char *cmd)
1595 {
1596 	int mode;
1597 	opt_t opts;
1598 	char *owner, *group, *file;
1599 	char *cp = cmd;
1600 	char fileb[MAXPATHLEN];
1601 
1602 	/*
1603 	 * Get rdist option flags
1604 	 */
1605 	opts = strtol(cp, &cp, 8);
1606 	if (*cp++ != ' ') {
1607 		error("dochmog: options not delimited");
1608 		return;
1609 	}
1610 
1611 	/*
1612 	 * Get file mode
1613 	 */
1614 	mode = strtol(cp, &cp, 8);
1615 	if (*cp++ != ' ') {
1616 		error("dochmog: mode not delimited");
1617 		return;
1618 	}
1619 
1620 	/*
1621 	 * Get file owner name
1622 	 */
1623 	owner = strtok(cp, " ");
1624 	if (owner == NULL) {
1625 		error("dochmog: owner name not delimited");
1626 		return;
1627 	}
1628 
1629 	/*
1630 	 * Get file group name
1631 	 */
1632 	group = strtok(NULL, " ");
1633 	if (group == NULL) {
1634 		error("dochmog: group name not delimited");
1635 		return;
1636 	}
1637 
1638 	/*
1639 	 * Get file name. Can't use strtok() since there could
1640 	 * be white space in the file name.
1641 	 */
1642 	if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1643 		error("dochmog: Cannot decode file name");
1644 		return;
1645 	}
1646 
1647 	if (fileb[0] == '\0') {
1648 		error("dochmog: no file name");
1649 		return;
1650 	}
1651 	file = fileb;
1652 
1653 	debugmsg(DM_MISC,
1654 		 "dochmog: opts = %04o mode = %04o", opts, mode);
1655 	debugmsg(DM_MISC,
1656 	         "dochmog: owner = '%s' group = '%s' file = '%s' catname = %d",
1657 		 owner, group, file, catname);
1658 
1659 	if (catname && cattarget(file) < 0) {
1660 		error("Cannot set newname target.");
1661 		return;
1662 	}
1663 
1664 	(void) fchog(-1, target, owner, group, mode);
1665 
1666 	ack();
1667 }
1668 
1669 /*
1670  * Set target information
1671  */
1672 static void
1673 settarget(char *cmd, int isdir)
1674 {
1675 	char *cp = cmd;
1676 	opt_t opts;
1677 	char file[BUFSIZ];
1678 
1679 	catname = isdir;
1680 
1681 	/*
1682 	 * Parse options for this target
1683 	 */
1684 	opts = strtol(cp, &cp, 8);
1685 	if (*cp++ != ' ') {
1686 		error("settarget: options not delimited");
1687 		return;
1688 	}
1689 	options = opts;
1690 
1691 	if (DECODE(file, cp) == -1) {
1692 		error("settarget: Cannot decode target name");
1693 		return;
1694 	}
1695 
1696 	/*
1697 	 * Handle target
1698 	 */
1699 	if (exptilde(target, cp, sizeof(target)) == NULL)
1700 		return;
1701 	ptarget = target;
1702 	while (*ptarget)
1703 		ptarget++;
1704 
1705 	ack();
1706 }
1707 
1708 /*
1709  * Cleanup in preparation for exiting.
1710  */
1711 void
1712 cleanup(int dummy)
1713 {
1714 	/* We don't need to do anything */
1715 }
1716 
1717 /*
1718  * Server routine to read requests and process them.
1719  */
1720 void
1721 server(void)
1722 {
1723 	static char cmdbuf[BUFSIZ];
1724 	char *cp;
1725 	int n;
1726 	extern jmp_buf finish_jmpbuf;
1727 
1728 	if (setjmp(finish_jmpbuf))
1729 		return;
1730 	(void) signal(SIGHUP, sighandler);
1731 	(void) signal(SIGINT, sighandler);
1732 	(void) signal(SIGQUIT, sighandler);
1733 	(void) signal(SIGTERM, sighandler);
1734 	(void) signal(SIGPIPE, sighandler);
1735 	(void) umask(oumask = umask(0));
1736 	(void) strlcpy(tempname, _RDIST_TMP, sizeof(tempname));
1737 	if (fromhost) {
1738 		message(MT_SYSLOG, "Startup for %s", fromhost);
1739 #if 	defined(SETARGS)
1740 		setproctitle("Serving %s", fromhost);
1741 #endif	/* SETARGS */
1742 	}
1743 
1744 	/*
1745 	 * Let client know we want it to send it's version number
1746 	 */
1747 	(void) sendcmd(S_VERSION, NULL);
1748 
1749 	if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) {
1750 		error("server: expected control record");
1751 		return;
1752 	}
1753 
1754 	if (cmdbuf[0] != S_VERSION || !isdigit((unsigned char)cmdbuf[1])) {
1755 		error("Expected version command, received: \"%s\".", cmdbuf);
1756 		return;
1757 	}
1758 
1759 	proto_version = atoi(&cmdbuf[1]);
1760 	if (proto_version != VERSION) {
1761 		error("Protocol version %d is not supported.", proto_version);
1762 		return;
1763 	}
1764 
1765 	/* Version number is okay */
1766 	ack();
1767 
1768 	/*
1769 	 * Main command loop
1770 	 */
1771 	for ( ; ; ) {
1772 		n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE);
1773 		if (n == -1)		/* EOF */
1774 			return;
1775 		if (n == 0) {
1776 			error("server: expected control record");
1777 			continue;
1778 		}
1779 
1780 		switch (*cp++) {
1781 		case C_SETCONFIG:  	/* Configuration info */
1782 		        setconfig(cp);
1783 			ack();
1784 			continue;
1785 
1786 		case C_DIRTARGET:  	/* init target file/directory name */
1787 			settarget(cp, TRUE);
1788 			continue;
1789 
1790 		case C_TARGET:  	/* init target file/directory name */
1791 			settarget(cp, FALSE);
1792 			continue;
1793 
1794 		case C_RECVREG:  	/* Transfer a regular file. */
1795 			recvit(cp, S_IFREG);
1796 			continue;
1797 
1798 		case C_RECVDIR:  	/* Transfer a directory. */
1799 			recvit(cp, S_IFDIR);
1800 			continue;
1801 
1802 		case C_RECVSYMLINK:  	/* Transfer symbolic link. */
1803 			recvit(cp, S_IFLNK);
1804 			continue;
1805 
1806 		case C_RECVHARDLINK:  	/* Transfer hard link. */
1807 			hardlink(cp);
1808 			continue;
1809 
1810 		case C_END:  		/* End of transfer */
1811 			*ptarget = CNULL;
1812 			if (catname <= 0) {
1813 				error("server: too many '%c's", C_END);
1814 				continue;
1815 			}
1816 			ptarget = sptarget[--catname];
1817 			*ptarget = CNULL;
1818 			ack();
1819 			continue;
1820 
1821 		case C_CLEAN:  		/* Clean. Cleanup a directory */
1822 			clean(cp);
1823 			continue;
1824 
1825 		case C_QUERY:  		/* Query file/directory */
1826 			query(cp);
1827 			continue;
1828 
1829 		case C_SPECIAL:  	/* Special. Execute commands */
1830 			dospecial(cp);
1831 			continue;
1832 
1833 		case C_CMDSPECIAL:  	/* Cmd Special. Execute commands */
1834 			docmdspecial();
1835 			continue;
1836 
1837 	        case C_CHMOG:  		/* Set owner, group, mode */
1838 			dochmog(cp);
1839 			continue;
1840 
1841 		case C_ERRMSG:		/* Normal error message */
1842 			if (cp && *cp)
1843 				message(MT_NERROR|MT_NOREMOTE, "%s", cp);
1844 			continue;
1845 
1846 		case C_FERRMSG:		/* Fatal error message */
1847 			if (cp && *cp)
1848 				message(MT_FERROR|MT_NOREMOTE, "%s", cp);
1849 			return;
1850 
1851 		default:
1852 			error("server: unknown command '%s'", cp - 1);
1853 		case CNULL:
1854 			continue;
1855 		}
1856 	}
1857 }
1858