xref: /openbsd-src/usr.bin/rdist/client.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: client.c,v 1.19 2003/06/03 02:56:14 millert 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.19 2003/06/03 02:56:14 millert 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 	if (IS_ON(opts, DO_NOEXEC))
795 		if (isexec(target, statp)) {
796 			debugmsg(DM_MISC, "%s is an executable\n", target);
797 			return(US_NOTHING);
798 		}
799 
800 	/*
801 	 * Check to see if the file exists on the remote machine.
802 	 */
803 	ENCODE(ername, rname);
804 	(void) sendcmd(C_QUERY, "%s", ername);
805 
806 	for (done = 0; !done;) {
807 		n = remline(cp = respbuff, sizeof(respbuff), TRUE);
808 		if (n <= 0) {
809 			error("update: unexpected control record in response to query");
810 			return(US_NOTHING);
811 		}
812 
813 		switch (*cp++) {
814 		case QC_ONNFS:  /* Resides on a NFS */
815 			debugmsg(DM_MISC,
816 				 "update: %s is on a NFS.  Skipping...\n",
817 				 rname);
818 			return(US_NOTHING);
819 
820 		case QC_SYM:  /* Is a symbolic link */
821 			debugmsg(DM_MISC,
822 				 "update: %s is a symlink.  Skipping...\n",
823 				 rname);
824 			return(US_NOTHING);
825 
826 		case QC_ONRO:  /* Resides on a Read-Only fs */
827 			debugmsg(DM_MISC,
828 				 "update: %s is on a RO fs.  Skipping...\n",
829 				 rname);
830 			return(US_NOTHING);
831 
832 		case QC_YES:
833 			done = 1;
834 			break;
835 
836 		case QC_NO:  /* file doesn't exist so install it */
837 			return(US_NOENT);
838 
839 		case C_ERRMSG:
840 			if (cp)
841 				message(MT_NERROR, "%s", cp);
842 			return(US_NOTHING);
843 
844 		case C_FERRMSG:
845 			if (cp)
846 				message(MT_FERROR, "%s", cp);
847 			finish();
848 
849 		case C_NOTEMSG:
850 			if (cp)
851 				message(MT_NOTICE, "%s", cp);
852 			break;
853 			/* Goto top of loop */
854 
855 		default:
856 			error("update: unexpected response to query '%s'", respbuff);
857 			return(US_NOTHING);
858 		}
859 	}
860 
861 	/*
862 	 * Target exists, but no other info passed
863 	 */
864 	if (n <= 1 || !S_ISREG(statp->st_mode))
865 		return(US_OUTDATE);
866 
867 	if (IS_ON(opts, DO_COMPARE))
868 		return(US_DOCOMP);
869 
870 	/*
871 	 * Parse size
872 	 */
873 	size = (off_t) strtol(cp, (char **)&cp, 10);
874 	if (*cp++ != ' ') {
875 		error("update: size not delimited");
876 		return(US_NOTHING);
877 	}
878 
879 	/*
880 	 * Parse mtime
881 	 */
882 	mtime = strtol(cp, (char **)&cp, 10);
883 	if (*cp++ != ' ') {
884 		error("update: mtime not delimited");
885 		return(US_NOTHING);
886 	}
887 
888 	/*
889 	 * Parse remote file mode
890 	 */
891 	rmode = strtol(cp, (char **)&cp, 8);
892 	if (cp && *cp)
893 		++cp;
894 
895 	/*
896 	 * Be backwards compatible
897 	 */
898 	if (cp && *cp != CNULL) {
899 		/*
900 		 * Parse remote file owner
901 		 */
902 		owner = strtok((char *)cp, " ");
903 		if (owner == NULL) {
904 			error("update: owner not delimited");
905 			return(US_NOTHING);
906 		}
907 
908 		/*
909 		 * Parse remote file group
910 		 */
911 		group = strtok(NULL, " ");
912 		if (group == NULL) {
913 			error("update: group not delimited");
914 			return(US_NOTHING);
915 		}
916 	}
917 
918 	/*
919 	 * File needs to be updated?
920 	 */
921 	lmode = statp->st_mode & 07777;
922 
923 	debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n",
924 		 rname, lmode, rmode);
925 	debugmsg(DM_MISC, "update(%s,) size %ld mtime %d owner '%s' grp '%s'\n",
926 		 rname, (long) size, mtime, owner, group);
927 
928 	if (statp->st_mtime != mtime) {
929 		if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
930 			message(MT_WARNING,
931 				"%s: Warning: remote copy is newer",
932 				target);
933 			return(US_NOTHING);
934 		}
935 		return(US_OUTDATE);
936 	}
937 
938 	if (statp->st_size != size) {
939 		debugmsg(DM_MISC, "size does not match (%ld != %ld).\n",
940 			 (long) statp->st_size, (long) size);
941 		return(US_OUTDATE);
942 	}
943 
944 	if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
945 		debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n",
946 			 lmode, rmode);
947 		return(US_CHMOG);
948 	}
949 
950 
951 	/*
952 	 * Check ownership
953 	 */
954 	if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
955 		if (!IS_ON(opts, DO_NUMCHKOWNER)) {
956 			/* Check by string compare */
957 			if (strcmp(owner, getusername(statp->st_uid,
958 						      target, opts)) != 0) {
959 				debugmsg(DM_MISC,
960 					 "owner does not match (%s != %s).\n",
961 					 getusername(statp->st_uid,
962 						     target, opts), owner);
963 				return(US_CHMOG);
964 			}
965 		} else {
966 			/*
967 			 * Check numerically.
968 			 * Allow negative numbers.
969 			 */
970 			while (*owner && !isdigit((unsigned char)*owner) &&
971 			    (*owner != '-'))
972 				++owner;
973 			if (owner && (UID_T) atoi(owner) != statp->st_uid) {
974 				debugmsg(DM_MISC,
975 					 "owner does not match (%d != %s).\n",
976 					 statp->st_uid, owner);
977 				return(US_CHMOG);
978 			}
979 		}
980 	}
981 
982 	if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
983 		if (!IS_ON(opts, DO_NUMCHKGROUP)) {
984 			/* Check by string compare */
985 			if (strcmp(group, getgroupname(statp->st_gid,
986 						       target, opts)) != 0) {
987 				debugmsg(DM_MISC,
988 					 "group does not match (%s != %s).\n",
989 					 getgroupname(statp->st_gid,
990 						      target, opts), group);
991 				return(US_CHMOG);
992 			}
993 		} else {
994 			/* Check numerically */
995 			/* Allow negative gid */
996 			while (*group && !isdigit((unsigned char) *group) &&
997 			    (*group != '-'))
998 				++group;
999 			if (group && (UID_T) atoi(group) != statp->st_gid) {
1000 				debugmsg(DM_MISC,
1001 					 "group does not match (%d != %s).\n",
1002 					 statp->st_gid, group);
1003 				return(US_CHMOG);
1004 			}
1005 		}
1006 	}
1007 
1008 	return(US_NOTHING);
1009 }
1010 
1011 /*
1012  * Stat a file
1013  */
1014 static int
1015 dostat(char *file, struct stat *statbuf, opt_t opts)
1016 {
1017 	int s;
1018 
1019 	if (IS_ON(opts, DO_FOLLOW))
1020 		s = stat(file, statbuf);
1021 	else
1022 		s = lstat(file, statbuf);
1023 
1024 	if (s < 0)
1025 		error("%s: %s failed: %s", file,
1026 		      IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
1027 	return(s);
1028 }
1029 
1030 /*
1031  * We need to just change file info.
1032  */
1033 static int
1034 statupdate(int u, char *target, opt_t opts, char *rname, int destdir,
1035 	   struct stat *st, char *user, char *group)
1036 {
1037 	int rv = 0;
1038 	char ername[MAXPATHLEN*4];
1039 	int lmode = st->st_mode & 07777;
1040 
1041 	if (u == US_CHMOG) {
1042 		if (IS_ON(opts, DO_VERIFY)) {
1043 			message(MT_INFO,
1044 				"%s: need to change to perm %04o, owner %s, group %s",
1045 				target, lmode, user, group);
1046 			runspecial(target, opts, rname, destdir);
1047 		}
1048 		else {
1049 			message(MT_CHANGE, "%s: change to perm %04o, owner %s, group %s",
1050 				target, lmode, user, group);
1051 			ENCODE(ername, rname);
1052 			(void) sendcmd(C_CHMOG, "%o %04o %s %s %s",
1053 				       opts, lmode, user, group, ername);
1054 			(void) response();
1055 		}
1056 		rv = 1;
1057 	}
1058 	return(rv);
1059 }
1060 
1061 
1062 /*
1063  * We need to install/update:
1064  */
1065 static int
1066 fullupdate(int u, char *target, opt_t opts, char *rname, int destdir,
1067 	   struct stat *st, char *user, char *group)
1068 {
1069 	/*
1070 	 * No entry - need to install
1071 	 */
1072 	if (u == US_NOENT) {
1073 		if (IS_ON(opts, DO_VERIFY)) {
1074 			message(MT_INFO, "%s: need to install", target);
1075 			runspecial(target, opts, rname, destdir);
1076 			return(1);
1077 		}
1078 		if (!IS_ON(opts, DO_QUIET))
1079 			message(MT_CHANGE, "%s: installing", target);
1080 		FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
1081 	}
1082 
1083 	/*
1084 	 * Handle special file types, including directories and symlinks
1085 	 */
1086 	if (S_ISDIR(st->st_mode)) {
1087 		if (senddir(rname, opts, st, user, group, destdir) > 0)
1088 			return(1);
1089 		return(0);
1090 	} else if (S_ISLNK(st->st_mode)) {
1091 		if (u == US_NOENT)
1092 			FLAG_ON(opts, DO_COMPARE);
1093 		/*
1094 		 * Since we always send link info to the server
1095 		 * so the server can determine if the remote link
1096 		 * is correct, we never get any acknowledge meant
1097 		 * from the server whether the link was really
1098 		 * updated or not.
1099 		 */
1100 		(void) sendlink(rname, opts, st, user, group, destdir);
1101 		return(0);
1102 	} else if (S_ISREG(st->st_mode)) {
1103 		if (u == US_OUTDATE) {
1104 			if (IS_ON(opts, DO_VERIFY)) {
1105 				message(MT_INFO, "%s: need to update", target);
1106 				runspecial(target, opts, rname, destdir);
1107 				return(1);
1108 			}
1109 			if (!IS_ON(opts, DO_QUIET))
1110 				message(MT_CHANGE, "%s: updating", target);
1111 		}
1112 		return (sendfile(rname, opts, st, user, group, destdir) == 0);
1113 	} else {
1114 		message(MT_INFO, "%s: unknown file type 0%o", target,
1115 			st->st_mode);
1116 		return(0);
1117 	}
1118 }
1119 
1120 /*
1121  * Transfer the file or directory in target[].
1122  * rname is the name of the file on the remote host.
1123  *
1124  * Return < 0 on error.
1125  * Return 0 if nothing happened.
1126  * Return > 0 if anything is updated.
1127  */
1128 static int
1129 sendit(char *rname, opt_t opts, int destdir)
1130 {
1131 	static struct stat stb;
1132 	char *user, *group;
1133 	int u, len;
1134 
1135 	/*
1136 	 * Remove possible accidental newline
1137 	 */
1138 	len = strlen(rname);
1139 	if (len > 0 && rname[len-1] == '\n')
1140 		rname[len-1] = CNULL;
1141 
1142 	if (checkfilename(rname) != 0)
1143 		return(-1);
1144 
1145 	debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts);
1146 
1147 	if (except(target))
1148 		return(0);
1149 
1150 	if (dostat(target, &stb, opts) < 0)
1151 		return(-1);
1152 
1153 	/*
1154 	 * Does rname need updating?
1155 	 */
1156 	u = update(rname, opts, &stb);
1157 	debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n",
1158 		 rname, opts, target, u);
1159 
1160 	/*
1161 	 * Don't need to update the file, but we may need to save hardlink
1162 	 * info.
1163 	 */
1164 	if (u == US_NOTHING) {
1165 		if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
1166 			(void) linkinfo(&stb);
1167 		return(0);
1168 	}
1169 
1170 	user = getusername(stb.st_uid, target, opts);
1171 	group = getgroupname(stb.st_gid, target, opts);
1172 
1173 	if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM))
1174 		u = US_OUTDATE;
1175 
1176 	if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP)
1177 		return(fullupdate(u, target, opts, rname, destdir, &stb,
1178 				  user, group));
1179 
1180 	if (u == US_CHMOG)
1181 		return(statupdate(u, target, opts, rname, destdir, &stb,
1182 				  user, group));
1183 
1184 	return(0);
1185 }
1186 
1187 /*
1188  * Remove temporary files and do any cleanup operations before exiting.
1189  */
1190 void
1191 cleanup(int dummy)
1192 {
1193 	char *file;
1194 #ifdef USE_STATDB
1195 	extern char statfile[];
1196 
1197 	(void) unlink(statfile);
1198 #endif
1199 
1200 	if ((file = getnotifyfile()) != NULL)
1201 		(void) unlink(file);
1202 }
1203 
1204 /*
1205  * Update the file(s) if they are different.
1206  * destdir = 1 if destination should be a directory
1207  * (i.e., more than one source is being copied to the same destination).
1208  *
1209  * Return < 0 on error.
1210  * Return 0 if nothing updated.
1211  * Return > 0 if something was updated.
1212  */
1213 int
1214 install(char *src, char *dest, int ddir, int destdir, opt_t opts)
1215 {
1216 	static char destcopy[MAXPATHLEN];
1217 	char *rname;
1218 	int didupdate = 0;
1219 	char ername[MAXPATHLEN*4];
1220 
1221 	debugmsg(DM_CALL,
1222 		"install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n",
1223 		(src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
1224 	/*
1225 	 * Save source name
1226 	 */
1227 	if (IS_ON(opts, DO_WHOLE))
1228 		source[0] = CNULL;
1229 	else
1230 		(void) strlcpy(source, src, sizeof(source));
1231 
1232 	if (dest == NULL) {
1233 		FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
1234 		dest = src;
1235 	}
1236 
1237 	if (checkfilename(dest) != 0)
1238 		return(-1);
1239 
1240 	if (nflag || debug) {
1241 		static char buff[BUFSIZ];
1242 		char *cp;
1243 
1244 		cp = getondistoptlist(opts);
1245 		(void) snprintf(buff, sizeof(buff), "%s%s%s %s %s",
1246 			       IS_ON(opts, DO_VERIFY) ? "verify" : "install",
1247 			       (cp) ? " -o" : "", (cp) ? cp : "",
1248 			       src, dest);
1249 		if (nflag) {
1250 			printf("%s\n", buff);
1251 			return(0);
1252 		} else
1253 			debugmsg(DM_MISC, "%s\n", buff);
1254 	}
1255 
1256 	rname = exptilde(target, src, sizeof(target));
1257 	if (rname == NULL)
1258 		return(-1);
1259 	ptarget = target;
1260 	while (*ptarget)
1261 		ptarget++;
1262 	/*
1263 	 * If we are renaming a directory and we want to preserve
1264 	 * the directory heirarchy (-w), we must strip off the leading
1265 	 * directory name and preserve the rest.
1266 	 */
1267 	if (IS_ON(opts, DO_WHOLE)) {
1268 		while (*rname == '/')
1269 			rname++;
1270 		ddir = 1;
1271 		destdir = 1;
1272 	} else {
1273 		rname = strrchr(target, '/');
1274 		/* Check if no '/' or target ends in '/' */
1275 		if (rname == NULL ||
1276 		    rname+1 == NULL ||
1277 		    *(rname+1) == CNULL)
1278 			rname = target;
1279 		else
1280 			rname++;
1281 	}
1282 
1283 	debugmsg(DM_MISC,
1284  	"install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n",
1285  		 target, source, rname, dest, destdir, ddir);
1286 
1287 	/*
1288 	 * Pass the destination file/directory name to remote.
1289 	 */
1290 	ENCODE(ername, dest);
1291  	if (ddir)
1292 		(void) sendcmd(C_DIRTARGET, "%o %s", opts, ername);
1293 	else
1294 		(void) sendcmd(C_TARGET, "%o %s", opts, ername);
1295 	if (response() < 0)
1296 		return(-1);
1297 
1298 	/*
1299 	 * Save the name of the remote target destination if we are
1300 	 * in WHOLE mode (destdir > 0) or if the source and destination
1301 	 * are not the same.  This info will be used later for maintaining
1302 	 * hardlink info.
1303 	 */
1304 	if (destdir || (src && dest && strcmp(src, dest))) {
1305 		(void) strlcpy(destcopy, dest, sizeof(destcopy));
1306 		Tdest = destcopy;
1307 	}
1308 
1309 	didupdate = sendit(rname, opts, destdir);
1310 	Tdest = 0;
1311 
1312 	return(didupdate);
1313 }
1314