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