xref: /openbsd-src/usr.bin/rdist/client.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: client.c,v 1.21 2009/02/15 22:20:54 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 
32 #include "defs.h"
33 #include "y.tab.h"
34 
35 #ifndef lint
36 #if 0
37 static char RCSid[] __attribute__((__unused__)) =
38 "$From: client.c,v 1.13 1999/11/01 00:22:14 christos Exp $";
39 #else
40 static char RCSid[] __attribute__((__unused__)) =
41 "$OpenBSD: client.c,v 1.21 2009/02/15 22:20:54 deraadt Exp $";
42 #endif
43 
44 static char sccsid[] __attribute__((__unused__)) =
45 "@(#)client.c";
46 
47 static char copyright[] __attribute__((__unused__)) =
48 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
49  All rights reserved.\n";
50 #endif /* not lint */
51 
52 /*
53  * Routines used in client mode to communicate with remove server.
54  */
55 
56 
57 /*
58  * Update status
59  */
60 #define US_NOTHING 	0	/* No update needed */
61 #define US_NOENT	1	/* Entry does not exist */
62 #define US_OUTDATE	2	/* Entry is out of date */
63 #define US_DOCOMP	3	/* Do a binary comparison */
64 #define US_CHMOG	4	/* Modes or ownership of file differ */
65 
66 struct	linkbuf *ihead = NULL;	/* list of files with more than one link */
67 char	buf[BUFSIZ];		/* general purpose buffer */
68 u_char	respbuff[BUFSIZ];	/* Response buffer */
69 char	target[BUFSIZ];		/* target/source directory name */
70 char	source[BUFSIZ];		/* source directory name */
71 char	*ptarget;		/* pointer to end of target name */
72 char	*Tdest;			/* pointer to last T dest*/
73 struct namelist	*updfilelist = NULL; /* List of updated files */
74 
75 static void runspecial(char *, opt_t, char *, int);
76 static void addcmdspecialfile(char *, char *, int);
77 static void freecmdspecialfiles(void);
78 static struct linkbuf *linkinfo(struct stat *);
79 static int sendhardlink(opt_t, struct linkbuf *, char *, int);
80 static int sendfile(char *, opt_t, struct stat *, char *, char *, int);
81 static int rmchk(opt_t);
82 static int senddir(char *, opt_t, struct stat *, char *, char *, int);
83 static int sendlink(char *, opt_t, struct stat *, char *, char *, int);
84 static int update(char *, opt_t, struct stat *);
85 static int dostat(char *, struct stat *, opt_t);
86 static int statupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *);
87 static int fullupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *);
88 static int sendit(char *, opt_t, int);
89 
90 /*
91  * return remote file pathname (relative from target)
92  */
93 char *
94 remfilename(char *src, char *dest, char *path, char *rname, int destdir)
95 {
96 	extern struct namelist *filelist;
97 	char *lname, *cp;
98 	static char buff[BUFSIZ];
99 	int srclen, pathlen;
100 	char *p;
101 
102 
103 	debugmsg(DM_MISC,
104 		 "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n",
105 		A(src), A(dest), A(path), A(rname), destdir);
106 
107 	if (!dest) {
108 		debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path);
109 		return(path);
110 	}
111 
112 	if (!destdir) {
113 		debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest);
114 		return(dest);
115 	}
116 
117 	buff[0] = CNULL;
118 	lname = buff;
119 	if (path && *path) {
120 		cp = strrchr(path, '/');
121  		if (cp == NULL)
122 			(void) snprintf(buff, sizeof(buff), "%s/%s", dest, path);
123 		else {
124 			srclen = strlen(src);
125 			pathlen = strlen(path);
126 			if (srclen >= pathlen)
127 				cp++; /* xbasename(path) */
128 			else {
129 				if (filelist && filelist->n_next == NULL)
130 					/* path relative to src */
131 					cp = path + srclen;
132 				else {
133 					if ((p = strrchr(src, '/')))
134 						cp = path + srclen - strlen(p);
135 					else
136 						cp = path;
137 				}
138 			}
139 			if ((*cp != '/') && *cp)
140 				(void) snprintf(buff, sizeof(buff), "%s/%s",
141 						dest, cp);
142 			else
143 				(void) snprintf(buff, sizeof(buff), "%s%s",
144 						dest, cp);
145 		}
146 	} else
147 		(void) strlcpy(lname, dest, buf + sizeof buff - lname);
148 
149 	debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname);
150 
151 	return(lname);
152 }
153 
154 /*
155  * Return true if name is in the list.
156  */
157 int
158 inlist(struct namelist *list, char *file)
159 {
160 	struct namelist *nl;
161 
162 	for (nl = list; nl != NULL; nl = nl->n_next)
163 		if (strcmp(file, nl->n_name) == 0)
164 			return(1);
165 	return(0);
166 }
167 
168 /*
169  * Run any special commands for this file
170  */
171 static void
172 runspecial(char *starget, opt_t opts, char *rname, int destdir)
173 {
174 	struct subcmd *sc;
175 	extern struct subcmd *subcmds;
176 	char *rfile;
177 
178  	rfile = remfilename(source, Tdest, target, rname, destdir);
179 
180 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
181 		if (sc->sc_type != SPECIAL)
182 			continue;
183 		if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
184 			continue;
185 		message(MT_CHANGE, "special \"%s\"", sc->sc_name);
186 		if (IS_ON(opts, DO_VERIFY))
187 			continue;
188 		(void) sendcmd(C_SPECIAL,
189 			"%s=%s;%s=%s;%s=%s;export %s %s %s;%s",
190 			E_LOCFILE, starget,
191 			E_REMFILE, rfile,
192 			E_BASEFILE, xbasename(rfile),
193 			E_LOCFILE, E_REMFILE, E_BASEFILE,
194 			sc->sc_name);
195 		while (response() > 0)
196 			;
197 	}
198 }
199 
200 /*
201  * If we're doing a target with a "cmdspecial" in it, then
202  * save the name of the file being updated for use with "cmdspecial".
203  */
204 static void
205 addcmdspecialfile(char *starget, char *rname, int destdir)
206 {
207 	char *rfile;
208 	struct namelist *new;
209 	struct subcmd *sc;
210 	extern struct subcmd *subcmds;
211 	int isokay = 0;
212 
213  	rfile = remfilename(source, Tdest, target, rname, destdir);
214 
215 	for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) {
216 		if (sc->sc_type != CMDSPECIAL)
217 			continue;
218 		if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
219 			continue;
220 		isokay = TRUE;
221 	}
222 
223 	if (isokay) {
224 		new = (struct namelist *) xmalloc(sizeof(struct namelist));
225 		new->n_name = xstrdup(rfile);
226 		new->n_regex = NULL;
227 		new->n_next = updfilelist;
228 		updfilelist = new;
229 	}
230 }
231 
232 /*
233  * Free the file list
234  */
235 static void
236 freecmdspecialfiles(void)
237 {
238 	struct namelist *ptr, *save;
239 
240 	for (ptr = updfilelist; ptr; ) {
241 		if (ptr->n_name) (void) free(ptr->n_name);
242 		save = ptr->n_next;
243 		(void) free(ptr);
244 		if (save)
245 			ptr = save->n_next;
246 		else
247 			ptr = NULL;
248 	}
249 	updfilelist = NULL;
250 }
251 
252 /*
253  * Run commands for an entire cmd
254  */
255 void
256 runcmdspecial(struct cmd *cmd, opt_t opts)
257 {
258 	struct subcmd *sc;
259 	struct namelist *f;
260 	int first = TRUE;
261 
262 	for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) {
263 		if (sc->sc_type != CMDSPECIAL)
264 			continue;
265 		message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name);
266 		if (IS_ON(opts, DO_VERIFY))
267 			continue;
268 		/* Send all the file names */
269 		for (f = updfilelist; f != NULL; f = f->n_next) {
270 			if (first) {
271 				(void) sendcmd(C_CMDSPECIAL, NULL);
272 				if (response() < 0)
273 					return;
274 				first = FALSE;
275 			}
276 			(void) sendcmd(RC_FILE, f->n_name);
277 			if (response() < 0)
278 				return;
279 		}
280 		if (first) {
281 			(void) sendcmd(C_CMDSPECIAL, NULL);
282 			if (response() < 0)
283 				return;
284 			first = FALSE;
285 		}
286 		/* Send command to run and wait for it to complete */
287 		(void) sendcmd(RC_COMMAND, sc->sc_name);
288 		while (response() > 0)
289 			;
290 		first = TRUE;	/* Reset in case there are more CMDSPECIAL's */
291 	}
292 	freecmdspecialfiles();
293 }
294 
295 /*
296  * For security, reject filenames that contains a newline
297  */
298 int
299 checkfilename(char *name)
300 {
301 	char *cp;
302 
303 	if (strchr(name, '\n')) {
304 		for (cp = name; *cp; cp++)
305 			if (*cp == '\n')
306 				*cp = '?';
307 		message(MT_NERROR,
308 			"Refuse to handle filename containing newline: %s",
309 			name);
310 		return(-1);
311 	}
312 
313 	return(0);
314 }
315 
316 void
317 freelinkinfo(struct linkbuf *lp)
318 {
319 	if (lp->pathname)
320 		free(lp->pathname);
321 	if (lp->src)
322 		free(lp->src);
323 	if (lp->target)
324 		free(lp->target);
325 	free(lp);
326 }
327 
328 /*
329  * Save and retrieve hard link info
330  */
331 static struct linkbuf *
332 linkinfo(struct stat *statp)
333 {
334 	struct linkbuf *lp;
335 
336 	/* XXX - linear search doesn't scale with many links */
337 	for (lp = ihead; lp != NULL; lp = lp->nextp)
338 		if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) {
339 			lp->count--;
340 			return(lp);
341 		}
342 
343 	lp = (struct linkbuf *) xmalloc(sizeof(*lp));
344 	lp->nextp = ihead;
345 	ihead = lp;
346 	lp->inum = statp->st_ino;
347 	lp->devnum = statp->st_dev;
348 	lp->count = statp->st_nlink - 1;
349 	lp->pathname = xstrdup(target);
350 	lp->src = xstrdup(source);
351 	if (Tdest)
352 		lp->target = xstrdup(Tdest);
353 	else
354 		lp->target = NULL;
355 
356 	return(NULL);
357 }
358 
359 /*
360  * Send a hardlink
361  */
362 static int
363 sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir)
364 {
365 	static char buff[MAXPATHLEN];
366 	char *lname;	/* name of file to link to */
367 	char ername[MAXPATHLEN*4], elname[MAXPATHLEN*4];
368 
369 	debugmsg(DM_MISC,
370 	       "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
371 		rname, lp->pathname ? lp->pathname : "",
372 		lp->src ? lp->src : "", lp->target ? lp->target : "");
373 
374 	if (lp->target == NULL)
375 		lname = lp->pathname;
376 	else {
377 		lname = buff;
378 		strlcpy(lname, remfilename(lp->src, lp->target,
379 					  lp->pathname, rname,
380 					  destdir), sizeof(buff));
381 		debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname);
382 	}
383 	ENCODE(elname, lname);
384 	ENCODE(ername, rname);
385 	(void) sendcmd(C_RECVHARDLINK, "%o %s %s",
386 		       opts, elname, ername);
387 
388 	return(response());
389 }
390 
391 /*
392  * Send a file
393  */
394 static int
395 sendfile(char *rname, opt_t opts, struct stat *stb, char *user,
396 	 char *group, int destdir)
397 {
398 	int goterr, f;
399 	off_t i;
400 	char ername[MAXPATHLEN*4];
401 
402 	if (stb->st_nlink > 1) {
403 		struct linkbuf *lp;
404 
405 		if ((lp = linkinfo(stb)) != NULL)
406 			return(sendhardlink(opts, lp, rname, destdir));
407 	}
408 
409 	if ((f = open(target, O_RDONLY)) < 0) {
410 		error("%s: open for read failed: %s", target, SYSERR);
411 		return(-1);
412 	}
413 
414 	/*
415 	 * Send file info
416 	 */
417 	ENCODE(ername, rname);
418 
419 	(void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s",
420 		       opts, stb->st_mode & 07777, (long) stb->st_size,
421 		       stb->st_mtime, stb->st_atime,
422 		       user, group, ername);
423 	if (response() < 0) {
424 		(void) close(f);
425 		return(-1);
426 	}
427 
428 
429 	debugmsg(DM_MISC, "Send file '%s' %ld bytes\n", rname,
430 		 (long) stb->st_size);
431 
432 	/*
433 	 * Set remote time out alarm handler.
434 	 */
435 	(void) signal(SIGALRM, sighandler);
436 
437 	/*
438 	 * Actually transfer the file
439 	 */
440 	goterr = 0;
441 	for (i = 0; i < stb->st_size; i += BUFSIZ) {
442 		off_t amt = BUFSIZ;
443 
444 		(void) alarm(rtimeout);
445 		if (i + amt > stb->st_size)
446 			amt = stb->st_size - i;
447 		if (read(f, buf, (size_t) amt) != (ssize_t) amt) {
448 			error("%s: File changed size", target);
449 			err();
450 			++goterr;
451 			/*
452 			 * XXX - We have to keep going because the
453 			 * server expects to receive a fixed number
454 			 * of bytes that we specified as the file size.
455 			 * We need Out Of Band communication to handle
456 			 * this situation gracefully.
457 			 */
458 		}
459 		if (xwrite(rem_w, buf, (size_t) amt) < 0) {
460 			error("%s: Error writing to client: %s",
461 			      target, SYSERR);
462 			err();
463 			++goterr;
464 			break;
465 		}
466 		(void) alarm(0);
467 	}
468 
469 	(void) alarm(0);	/* Insure alarm is off */
470 	(void) close(f);
471 
472 	debugmsg(DM_MISC, "Send file '%s' %s.\n",
473 		 (goterr) ? "failed" : "complete", rname);
474 
475 	/*
476 	 * Check for errors and end send
477 	 */
478 	if (goterr)
479 		return(-1);
480 	else {
481 		ack();
482 		f = response();
483 		if (f < 0)
484 			return(-1);
485 		else if (f == 0 && IS_ON(opts, DO_COMPARE))
486 			return(0);
487 
488 		runspecial(target, opts, rname, destdir);
489 		addcmdspecialfile(target, rname, destdir);
490 
491 		return(0);
492 	}
493 }
494 
495 /*
496  * Check for files on the machine being updated that are not on the master
497  * machine and remove them.
498  *
499  * Return < 0 on error.
500  * Return 0 if nothing happened.
501  * Return > 0 if anything is updated.
502  */
503 static int
504 rmchk(opt_t opts)
505 {
506 	u_char *s;
507 	struct stat stb;
508 	int didupdate = 0;
509 	int n;
510 	char targ[MAXPATHLEN*4];
511 
512 	debugmsg(DM_CALL, "rmchk()\n");
513 
514 	/*
515 	 * Tell the remote to clean the files from the last directory sent.
516 	 */
517 	(void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY));
518 	if (response() < 0)
519 		return(-1);
520 
521 	for ( ; ; ) {
522 		n = remline(s = respbuff, sizeof(respbuff), TRUE);
523 		if (n <= 0) {
524 			error("rmchk: unexpected control record");
525 			return(didupdate);
526 		}
527 
528 		switch (*s++) {
529 		case CC_QUERY: /* Query if file should be removed */
530 			/*
531 			 * Return the following codes to remove query.
532 			 * CC_NO -- file exists - DON'T remove.
533 			 * CC_YES -- file doesn't exist - REMOVE.
534 			 */
535 			if (DECODE(targ, (char *) s) == -1) {
536 				error("rmchk: cannot decode file");
537 				return(-1);
538 			}
539 			(void) snprintf(ptarget,
540 					sizeof(target) - (ptarget - target),
541 					"%s%s",
542 				        (ptarget[-1] == '/' ? "" : "/"),
543 				        targ);
544 			debugmsg(DM_MISC, "check %s\n", target);
545 			if (except(target))
546 				(void) sendcmd(CC_NO, NULL);
547 			else if (lstat(target, &stb) < 0) {
548 				if (sendcmd(CC_YES, NULL) == 0)
549 					didupdate = 1;
550 			} else
551 				(void) sendcmd(CC_NO, NULL);
552 			break;
553 
554 		case CC_END:
555 			*ptarget = CNULL;
556 			ack();
557 			return(didupdate);
558 
559 		case C_LOGMSG:
560 			if (n > 0)
561 				message(MT_INFO, "%s", s);
562 			break;
563 
564 		case C_NOTEMSG:
565 			if (n > 0)
566 				message(MT_NOTICE, "%s", s);
567 			break;
568 			/* Goto top of loop */
569 
570 		case C_ERRMSG:
571 			message(MT_NERROR, "%s", s);
572 			return(didupdate);
573 
574 		case C_FERRMSG:
575 			message(MT_FERROR, "%s", s);
576 			finish();
577 
578 		default:
579 			error("rmchk: unexpected response '%s'", respbuff);
580 			err();
581 		}
582 	}
583 	/*NOTREACHED*/
584 }
585 
586 /*
587  * Send a directory
588  *
589  * Return < 0 on error.
590  * Return 0 if nothing happened.
591  * Return > 0 if anything is updated.
592  */
593 static int
594 senddir(char *rname, opt_t opts, struct stat *stb, char *user,
595 	char *group, int destdir)
596 {
597 	DIRENTRY *dp;
598 	DIR *d;
599 	char *optarget, *cp;
600 	int len;
601 	int didupdate = 0;
602 	char ername[MAXPATHLEN*4];
603 
604 	/*
605 	 * Send recvdir command in recvit() format.
606 	 */
607 	ENCODE(ername, rname);
608 	(void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s",
609 		       opts, stb->st_mode & 07777, user, group, ername);
610 	if (response() < 0)
611 		return(-1);
612 
613 	/*
614 	 * Don't descend into directory
615 	 */
616 	if (IS_ON(opts, DO_NODESCEND))
617 		return(0);
618 
619 	if (IS_ON(opts, DO_REMOVE))
620 		if (rmchk(opts) > 0)
621 			++didupdate;
622 
623 	if ((d = opendir(target)) == NULL) {
624 		error("%s: opendir failed: %s", target, SYSERR);
625 		return(-1);
626 	}
627 
628 	optarget = ptarget;
629 	len = ptarget - target;
630 	while ((dp = readdir(d)) != NULL) {
631 		if (!strcmp(dp->d_name, ".") ||
632 		    !strcmp(dp->d_name, ".."))
633 			continue;
634 		if (len + 1 + (int) strlen(dp->d_name) >= MAXPATHLEN - 1) {
635 			error("%s/%s: Name too long", target,
636 			      dp->d_name);
637 			continue;
638 		}
639 		ptarget = optarget;
640 		if (ptarget[-1] != '/')
641 			*ptarget++ = '/';
642 		cp = dp->d_name;
643 		while ((*ptarget++ = *cp++) != '\0')
644 			continue;
645 		ptarget--;
646 		if (sendit(dp->d_name, opts, destdir) > 0)
647 			didupdate = 1;
648 	}
649 	(void) closedir(d);
650 
651 	(void) sendcmd(C_END, NULL);
652 	(void) response();
653 
654 	ptarget = optarget;
655 	*ptarget = CNULL;
656 
657 	return(didupdate);
658 }
659 
660 /*
661  * Send a link
662  */
663 static int
664 sendlink(char *rname, opt_t opts, struct stat *stb, char *user,
665 	 char *group, int destdir)
666 {
667 	int f, n;
668 	static char tbuf[BUFSIZ];
669 	char lbuf[MAXPATHLEN];
670 	u_char *s;
671 	char ername[MAXPATHLEN*4];
672 
673 	debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir);
674 
675 	if (stb->st_nlink > 1) {
676 		struct linkbuf *lp;
677 
678 		if ((lp = linkinfo(stb)) != NULL)
679 			return(sendhardlink(opts, lp, rname, destdir));
680 	}
681 
682 	/*
683 	 * Gather and send basic link info
684 	 */
685 	ENCODE(ername, rname);
686 	(void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s",
687 		       opts, stb->st_mode & 07777, (long) stb->st_size,
688 		       stb->st_mtime, stb->st_atime,
689 		       user, group, ername);
690 	if (response() < 0)
691 		return(-1);
692 
693 	/*
694 	 * Gather and send additional link info
695 	 */
696 	if ((n = readlink(target, lbuf, sizeof(lbuf)-1)) != -1)
697 		lbuf[n] = '\0';
698 	else {
699 		error("%s: readlink failed", target);
700 		err();
701 	}
702 	(void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf);
703 	ENCODE(ername, tbuf);
704 	(void) sendcmd(C_NONE, "%s\n", ername);
705 
706 	if (n != stb->st_size) {
707 		error("%s: file changed size", target);
708 		err();
709 	} else
710 		ack();
711 
712 	/*
713 	 * Check response
714 	 */
715 	f = response();
716 	if (f < 0)
717 		return(-1);
718 	else if (f == 0 && IS_ON(opts, DO_COMPARE))
719 		return(0);
720 
721 	/*
722 	 * Read and process responses from server.
723 	 * The server may send multiple messages regarding
724 	 * file deletes if the remote target is a directory.
725 	 */
726 	for (;;) {
727 		n = remline(s = respbuff, sizeof(respbuff), TRUE);
728 		if (n == -1)	/* normal EOF */
729 			return(0);
730 		if (n == 0) {
731 			error("expected control record");
732 			continue;
733 		}
734 
735 		switch (*s++) {
736 		case C_END:	/* End of send operation */
737 			*ptarget = CNULL;
738 			ack();
739 			runspecial(target, opts, rname, destdir);
740 			addcmdspecialfile(target, rname, destdir);
741 			return(0);
742 
743 		case C_LOGMSG:
744 			if (n > 0)
745 				message(MT_INFO, "%s", s);
746 			break;
747 
748 		case C_NOTEMSG:
749 			if (n > 0)
750 				message(MT_NOTICE, "%s", s);
751 			break;
752 			/* Goto top of loop */
753 
754 		case C_ERRMSG:
755 			message(MT_NERROR, "%s", s);
756 			return(-1);
757 
758 		case C_FERRMSG:
759 			message(MT_FERROR, "%s", s);
760 			finish();
761 
762 		default:
763 			error("install link: unexpected response '%s'",
764 			      respbuff);
765 			err();
766 		}
767 	}
768 	/*NOTREACHED*/
769 }
770 
771 /*
772  * Check to see if file needs to be updated on the remote machine.
773  * Returns:
774  * 	US_NOTHING	- no update
775  *	US_NOENT	- remote doesn't exist
776  *	US_OUTDATE	- out of date
777  *	US_DOCOMP	- comparing binaries to determine if out of date
778  *	US_CHMOG	- File modes or ownership do not match
779  */
780 static int
781 update(char *rname, opt_t opts, struct stat *statp)
782 {
783 	off_t size;
784 	time_t mtime;
785 	unsigned short lmode;
786 	unsigned short rmode;
787 	char *owner = NULL, *group = NULL;
788 	int done, n;
789 	u_char *cp;
790 	char ername[MAXPATHLEN*4];
791 
792 	debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp);
793 
794 	switch (statp->st_mode & S_IFMT) {
795 	case S_IFBLK:
796 		debugmsg(DM_MISC, "%s is a block special; skipping\n", target);
797 		return(US_NOTHING);
798 	case S_IFCHR:
799 		debugmsg(DM_MISC, "%s is a character special; skipping\n",
800 		    target);
801 		return(US_NOTHING);
802 	case S_IFIFO:
803 		debugmsg(DM_MISC, "%s is a fifo; skipping\n", target);
804 		return(US_NOTHING);
805 	case S_IFSOCK:
806 		debugmsg(DM_MISC, "%s is a socket; skipping\n", target);
807 		return(US_NOTHING);
808 	}
809 
810 	if (IS_ON(opts, DO_NOEXEC))
811 		if (isexec(target, statp)) {
812 			debugmsg(DM_MISC, "%s is an executable\n", target);
813 			return(US_NOTHING);
814 		}
815 
816 	/*
817 	 * Check to see if the file exists on the remote machine.
818 	 */
819 	ENCODE(ername, rname);
820 	(void) sendcmd(C_QUERY, "%s", ername);
821 
822 	for (done = 0; !done;) {
823 		n = remline(cp = respbuff, sizeof(respbuff), TRUE);
824 		if (n <= 0) {
825 			error("update: unexpected control record in response to query");
826 			return(US_NOTHING);
827 		}
828 
829 		switch (*cp++) {
830 		case QC_ONNFS:  /* Resides on a NFS */
831 			debugmsg(DM_MISC,
832 				 "update: %s is on a NFS.  Skipping...\n",
833 				 rname);
834 			return(US_NOTHING);
835 
836 		case QC_SYM:  /* Is a symbolic link */
837 			debugmsg(DM_MISC,
838 				 "update: %s is a symlink.  Skipping...\n",
839 				 rname);
840 			return(US_NOTHING);
841 
842 		case QC_ONRO:  /* Resides on a Read-Only fs */
843 			debugmsg(DM_MISC,
844 				 "update: %s is on a RO fs.  Skipping...\n",
845 				 rname);
846 			return(US_NOTHING);
847 
848 		case QC_YES:
849 			done = 1;
850 			break;
851 
852 		case QC_NO:  /* file doesn't exist so install it */
853 			return(US_NOENT);
854 
855 		case C_ERRMSG:
856 			if (cp)
857 				message(MT_NERROR, "%s", cp);
858 			return(US_NOTHING);
859 
860 		case C_FERRMSG:
861 			if (cp)
862 				message(MT_FERROR, "%s", cp);
863 			finish();
864 
865 		case C_NOTEMSG:
866 			if (cp)
867 				message(MT_NOTICE, "%s", cp);
868 			break;
869 			/* Goto top of loop */
870 
871 		default:
872 			error("update: unexpected response to query '%s'", respbuff);
873 			return(US_NOTHING);
874 		}
875 	}
876 
877 	/*
878 	 * Target exists, but no other info passed
879 	 */
880 	if (n <= 1 || !S_ISREG(statp->st_mode))
881 		return(US_OUTDATE);
882 
883 	if (IS_ON(opts, DO_COMPARE))
884 		return(US_DOCOMP);
885 
886 	/*
887 	 * Parse size
888 	 */
889 	size = (off_t) strtol(cp, (char **)&cp, 10);
890 	if (*cp++ != ' ') {
891 		error("update: size not delimited");
892 		return(US_NOTHING);
893 	}
894 
895 	/*
896 	 * Parse mtime
897 	 */
898 	mtime = strtol(cp, (char **)&cp, 10);
899 	if (*cp++ != ' ') {
900 		error("update: mtime not delimited");
901 		return(US_NOTHING);
902 	}
903 
904 	/*
905 	 * Parse remote file mode
906 	 */
907 	rmode = strtol(cp, (char **)&cp, 8);
908 	if (cp && *cp)
909 		++cp;
910 
911 	/*
912 	 * Be backwards compatible
913 	 */
914 	if (cp && *cp != CNULL) {
915 		/*
916 		 * Parse remote file owner
917 		 */
918 		owner = strtok((char *)cp, " ");
919 		if (owner == NULL) {
920 			error("update: owner not delimited");
921 			return(US_NOTHING);
922 		}
923 
924 		/*
925 		 * Parse remote file group
926 		 */
927 		group = strtok(NULL, " ");
928 		if (group == NULL) {
929 			error("update: group not delimited");
930 			return(US_NOTHING);
931 		}
932 	}
933 
934 	/*
935 	 * File needs to be updated?
936 	 */
937 	lmode = statp->st_mode & 07777;
938 
939 	debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n",
940 		 rname, lmode, rmode);
941 	debugmsg(DM_MISC, "update(%s,) size %ld mtime %d owner '%s' grp '%s'\n",
942 		 rname, (long) size, mtime, owner, group);
943 
944 	if (statp->st_mtime != mtime) {
945 		if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
946 			message(MT_WARNING,
947 				"%s: Warning: remote copy is newer",
948 				target);
949 			return(US_NOTHING);
950 		}
951 		return(US_OUTDATE);
952 	}
953 
954 	if (statp->st_size != size) {
955 		debugmsg(DM_MISC, "size does not match (%ld != %ld).\n",
956 			 (long) statp->st_size, (long) size);
957 		return(US_OUTDATE);
958 	}
959 
960 	if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
961 		debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n",
962 			 lmode, rmode);
963 		return(US_CHMOG);
964 	}
965 
966 
967 	/*
968 	 * Check ownership
969 	 */
970 	if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
971 		if (!IS_ON(opts, DO_NUMCHKOWNER)) {
972 			/* Check by string compare */
973 			if (strcmp(owner, getusername(statp->st_uid,
974 						      target, opts)) != 0) {
975 				debugmsg(DM_MISC,
976 					 "owner does not match (%s != %s).\n",
977 					 getusername(statp->st_uid,
978 						     target, opts), owner);
979 				return(US_CHMOG);
980 			}
981 		} else {
982 			/*
983 			 * Check numerically.
984 			 * Allow negative numbers.
985 			 */
986 			while (*owner && !isdigit((unsigned char)*owner) &&
987 			    (*owner != '-'))
988 				++owner;
989 			if (owner && (UID_T) atoi(owner) != statp->st_uid) {
990 				debugmsg(DM_MISC,
991 					 "owner does not match (%d != %s).\n",
992 					 statp->st_uid, owner);
993 				return(US_CHMOG);
994 			}
995 		}
996 	}
997 
998 	if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
999 		if (!IS_ON(opts, DO_NUMCHKGROUP)) {
1000 			/* Check by string compare */
1001 			if (strcmp(group, getgroupname(statp->st_gid,
1002 						       target, opts)) != 0) {
1003 				debugmsg(DM_MISC,
1004 					 "group does not match (%s != %s).\n",
1005 					 getgroupname(statp->st_gid,
1006 						      target, opts), group);
1007 				return(US_CHMOG);
1008 			}
1009 		} else {
1010 			/* Check numerically */
1011 			/* Allow negative gid */
1012 			while (*group && !isdigit((unsigned char) *group) &&
1013 			    (*group != '-'))
1014 				++group;
1015 			if (group && (UID_T) atoi(group) != statp->st_gid) {
1016 				debugmsg(DM_MISC,
1017 					 "group does not match (%d != %s).\n",
1018 					 statp->st_gid, group);
1019 				return(US_CHMOG);
1020 			}
1021 		}
1022 	}
1023 
1024 	return(US_NOTHING);
1025 }
1026 
1027 /*
1028  * Stat a file
1029  */
1030 static int
1031 dostat(char *file, struct stat *statbuf, opt_t opts)
1032 {
1033 	int s;
1034 
1035 	if (IS_ON(opts, DO_FOLLOW))
1036 		s = stat(file, statbuf);
1037 	else
1038 		s = lstat(file, statbuf);
1039 
1040 	if (s < 0)
1041 		error("%s: %s failed: %s", file,
1042 		      IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
1043 	return(s);
1044 }
1045 
1046 /*
1047  * We need to just change file info.
1048  */
1049 static int
1050 statupdate(int u, char *target, opt_t opts, char *rname, int destdir,
1051 	   struct stat *st, char *user, char *group)
1052 {
1053 	int rv = 0;
1054 	char ername[MAXPATHLEN*4];
1055 	int lmode = st->st_mode & 07777;
1056 
1057 	if (u == US_CHMOG) {
1058 		if (IS_ON(opts, DO_VERIFY)) {
1059 			message(MT_INFO,
1060 				"%s: need to change to perm %04o, owner %s, group %s",
1061 				target, lmode, user, group);
1062 			runspecial(target, opts, rname, destdir);
1063 		}
1064 		else {
1065 			message(MT_CHANGE, "%s: change to perm %04o, owner %s, group %s",
1066 				target, lmode, user, group);
1067 			ENCODE(ername, rname);
1068 			(void) sendcmd(C_CHMOG, "%o %04o %s %s %s",
1069 				       opts, lmode, user, group, ername);
1070 			(void) response();
1071 		}
1072 		rv = 1;
1073 	}
1074 	return(rv);
1075 }
1076 
1077 
1078 /*
1079  * We need to install/update:
1080  */
1081 static int
1082 fullupdate(int u, char *target, opt_t opts, char *rname, int destdir,
1083 	   struct stat *st, char *user, char *group)
1084 {
1085 	/*
1086 	 * No entry - need to install
1087 	 */
1088 	if (u == US_NOENT) {
1089 		if (IS_ON(opts, DO_VERIFY)) {
1090 			message(MT_INFO, "%s: need to install", target);
1091 			runspecial(target, opts, rname, destdir);
1092 			return(1);
1093 		}
1094 		if (!IS_ON(opts, DO_QUIET))
1095 			message(MT_CHANGE, "%s: installing", target);
1096 		FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
1097 	}
1098 
1099 	/*
1100 	 * Handle special file types, including directories and symlinks
1101 	 */
1102 	if (S_ISDIR(st->st_mode)) {
1103 		if (senddir(rname, opts, st, user, group, destdir) > 0)
1104 			return(1);
1105 		return(0);
1106 	} else if (S_ISLNK(st->st_mode)) {
1107 		if (u == US_NOENT)
1108 			FLAG_ON(opts, DO_COMPARE);
1109 		/*
1110 		 * Since we always send link info to the server
1111 		 * so the server can determine if the remote link
1112 		 * is correct, we never get any acknowledgement
1113 		 * from the server whether the link was really
1114 		 * updated or not.
1115 		 */
1116 		(void) sendlink(rname, opts, st, user, group, destdir);
1117 		return(0);
1118 	} else if (S_ISREG(st->st_mode)) {
1119 		if (u == US_OUTDATE) {
1120 			if (IS_ON(opts, DO_VERIFY)) {
1121 				message(MT_INFO, "%s: need to update", target);
1122 				runspecial(target, opts, rname, destdir);
1123 				return(1);
1124 			}
1125 			if (!IS_ON(opts, DO_QUIET))
1126 				message(MT_CHANGE, "%s: updating", target);
1127 		}
1128 		return (sendfile(rname, opts, st, user, group, destdir) == 0);
1129 	} else {
1130 		message(MT_INFO, "%s: unknown file type 0%o", target,
1131 			st->st_mode);
1132 		return(0);
1133 	}
1134 }
1135 
1136 /*
1137  * Transfer the file or directory in target[].
1138  * rname is the name of the file on the remote host.
1139  *
1140  * Return < 0 on error.
1141  * Return 0 if nothing happened.
1142  * Return > 0 if anything is updated.
1143  */
1144 static int
1145 sendit(char *rname, opt_t opts, int destdir)
1146 {
1147 	static struct stat stb;
1148 	char *user, *group;
1149 	int u, len;
1150 
1151 	/*
1152 	 * Remove possible accidental newline
1153 	 */
1154 	len = strlen(rname);
1155 	if (len > 0 && rname[len-1] == '\n')
1156 		rname[len-1] = CNULL;
1157 
1158 	if (checkfilename(rname) != 0)
1159 		return(-1);
1160 
1161 	debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts);
1162 
1163 	if (except(target))
1164 		return(0);
1165 
1166 	if (dostat(target, &stb, opts) < 0)
1167 		return(-1);
1168 
1169 	/*
1170 	 * Does rname need updating?
1171 	 */
1172 	u = update(rname, opts, &stb);
1173 	debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n",
1174 		 rname, opts, target, u);
1175 
1176 	/*
1177 	 * Don't need to update the file, but we may need to save hardlink
1178 	 * info.
1179 	 */
1180 	if (u == US_NOTHING) {
1181 		if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
1182 			(void) linkinfo(&stb);
1183 		return(0);
1184 	}
1185 
1186 	user = getusername(stb.st_uid, target, opts);
1187 	group = getgroupname(stb.st_gid, target, opts);
1188 
1189 	if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM))
1190 		u = US_OUTDATE;
1191 
1192 	if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP)
1193 		return(fullupdate(u, target, opts, rname, destdir, &stb,
1194 				  user, group));
1195 
1196 	if (u == US_CHMOG)
1197 		return(statupdate(u, target, opts, rname, destdir, &stb,
1198 				  user, group));
1199 
1200 	return(0);
1201 }
1202 
1203 /*
1204  * Remove temporary files and do any cleanup operations before exiting.
1205  */
1206 void
1207 cleanup(int dummy)
1208 {
1209 	char *file;
1210 #ifdef USE_STATDB
1211 	extern char statfile[];
1212 
1213 	(void) unlink(statfile);
1214 #endif
1215 
1216 	if ((file = getnotifyfile()) != NULL)
1217 		(void) unlink(file);
1218 }
1219 
1220 /*
1221  * Update the file(s) if they are different.
1222  * destdir = 1 if destination should be a directory
1223  * (i.e., more than one source is being copied to the same destination).
1224  *
1225  * Return < 0 on error.
1226  * Return 0 if nothing updated.
1227  * Return > 0 if something was updated.
1228  */
1229 int
1230 install(char *src, char *dest, int ddir, int destdir, opt_t opts)
1231 {
1232 	static char destcopy[MAXPATHLEN];
1233 	char *rname;
1234 	int didupdate = 0;
1235 	char ername[MAXPATHLEN*4];
1236 
1237 	debugmsg(DM_CALL,
1238 		"install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n",
1239 		(src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
1240 	/*
1241 	 * Save source name
1242 	 */
1243 	if (IS_ON(opts, DO_WHOLE))
1244 		source[0] = CNULL;
1245 	else
1246 		(void) strlcpy(source, src, sizeof(source));
1247 
1248 	if (dest == NULL) {
1249 		FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
1250 		dest = src;
1251 	}
1252 
1253 	if (checkfilename(dest) != 0)
1254 		return(-1);
1255 
1256 	if (nflag || debug) {
1257 		static char buff[BUFSIZ];
1258 		char *cp;
1259 
1260 		cp = getondistoptlist(opts);
1261 		(void) snprintf(buff, sizeof(buff), "%s%s%s %s %s",
1262 			       IS_ON(opts, DO_VERIFY) ? "verify" : "install",
1263 			       (cp) ? " -o" : "", (cp) ? cp : "",
1264 			       src, dest);
1265 		if (nflag) {
1266 			printf("%s\n", buff);
1267 			return(0);
1268 		} else
1269 			debugmsg(DM_MISC, "%s\n", buff);
1270 	}
1271 
1272 	rname = exptilde(target, src, sizeof(target));
1273 	if (rname == NULL)
1274 		return(-1);
1275 	ptarget = target;
1276 	while (*ptarget)
1277 		ptarget++;
1278 	/*
1279 	 * If we are renaming a directory and we want to preserve
1280 	 * the directory hierarchy (-w), we must strip off the leading
1281 	 * directory name and preserve the rest.
1282 	 */
1283 	if (IS_ON(opts, DO_WHOLE)) {
1284 		while (*rname == '/')
1285 			rname++;
1286 		ddir = 1;
1287 		destdir = 1;
1288 	} else {
1289 		rname = strrchr(target, '/');
1290 		/* Check if no '/' or target ends in '/' */
1291 		if (rname == NULL ||
1292 		    rname+1 == NULL ||
1293 		    *(rname+1) == CNULL)
1294 			rname = target;
1295 		else
1296 			rname++;
1297 	}
1298 
1299 	debugmsg(DM_MISC,
1300  	"install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n",
1301  		 target, source, rname, dest, destdir, ddir);
1302 
1303 	/*
1304 	 * Pass the destination file/directory name to remote.
1305 	 */
1306 	ENCODE(ername, dest);
1307  	if (ddir)
1308 		(void) sendcmd(C_DIRTARGET, "%o %s", opts, ername);
1309 	else
1310 		(void) sendcmd(C_TARGET, "%o %s", opts, ername);
1311 	if (response() < 0)
1312 		return(-1);
1313 
1314 	/*
1315 	 * Save the name of the remote target destination if we are
1316 	 * in WHOLE mode (destdir > 0) or if the source and destination
1317 	 * are not the same.  This info will be used later for maintaining
1318 	 * hardlink info.
1319 	 */
1320 	if (destdir || (src && dest && strcmp(src, dest))) {
1321 		(void) strlcpy(destcopy, dest, sizeof(destcopy));
1322 		Tdest = destcopy;
1323 	}
1324 
1325 	didupdate = sendit(rname, opts, destdir);
1326 	Tdest = 0;
1327 
1328 	return(didupdate);
1329 }
1330