1 /*
2  * Copyright (c) 1992-1998 Michael A. Cooper.
3  * This software may be freely used and distributed provided it is not
4  * sold for profit or used in part or in whole for commercial gain
5  * without prior written agreement, and the author is credited
6  * appropriately.
7  */
8 /*
9  * Copyright (c) 1983 Regents of the University of California.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #ifndef lint
42 static char RCSid[] =
43 "$Id: client.c,v 6.82 1998/11/10 04:08:47 mcooper Exp $";
44 
45 static char sccsid[] = "@(#)client.c";
46 
47 static char copyright[] =
48 "Copyright (c) 1992-1998 Michael A. Cooper.\n\
49 Copyright (c) 1983 Regents of the University of California.\n\
50  All rights reserved.\n";
51 #endif /* not lint */
52 
53 /*
54  * Routines used in client mode to communicate with remove server.
55  */
56 
57 #include "defs.h"
58 
59 /*
60  * Update status
61  */
62 #define US_NOTHING 	0	/* No update needed */
63 #define US_NOENT	1	/* Entry does not exist */
64 #define US_OUTDATE	2	/* Entry is out of date */
65 #define US_DOCOMP	3	/* Do a binary comparison */
66 #define US_MODE		4	/* Modes of file differ */
67 
68 struct	linkbuf *ihead = NULL;	/* list of files with more than one link */
69 char	buf[BUFSIZ];		/* general purpose buffer */
70 u_char	respbuff[BUFSIZ];	/* Response buffer */
71 char	target[BUFSIZ];		/* target/source directory name */
72 char	source[BUFSIZ];		/* source directory name */
73 char	*ptarget;		/* pointer to end of target name */
74 char	*Tdest;			/* pointer to last T dest*/
75 struct namelist	*updfilelist = NULL; /* List of updated files */
76 
77 static int sendit();
78 
79 /*
80  * return remote file pathname (relative from target)
81  */
remfilename(src,dest,path,rname,destdir)82 char *remfilename(src, dest, path, rname, destdir)
83 	char *src, *dest, *path, *rname;
84 	int destdir;
85 {
86 	extern struct namelist *filelist;
87 	register char *lname, *cp;
88 	static char buff[BUFSIZ];
89 	int srclen, pathlen;
90 	char *p;
91 
92 
93 	debugmsg(DM_MISC,
94 		 "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n",
95 		A(src), A(dest), A(path), A(rname), destdir);
96 
97 	if (!dest) {
98 		debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path);
99 		return(path);
100 	}
101 
102 	if (!destdir) {
103 		debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest);
104 		return(dest);
105 	}
106 
107 	buff[0] = CNULL;
108 	lname = buff;
109 	if (path && *path) {
110 		cp = strrchr(path, '/');
111  		if (cp == NULL)
112 			(void) sprintf(buff, "%s/%s", dest, path);
113 		else {
114 			srclen = strlen(src);
115 			pathlen = strlen(path);
116 			if (srclen >= pathlen)
117 				cp++; /* xbasename(path) */
118 			else {
119 				if (filelist && filelist->n_next == NULL)
120 					/* path relative to src */
121 					cp = path + srclen;
122 				else {
123 					if ((p = strrchr(src, '/')))
124 						cp = path + srclen - strlen(p);
125 					else
126 						cp = path;
127 				}
128 			}
129 			if ((*cp != '/') && *cp)
130 				(void) sprintf(buff, "%s/%s", dest, cp);
131 			else
132 				(void) sprintf(buff, "%s%s", dest, cp);
133 		}
134 	} else
135 		strcpy(lname, dest);
136 
137 	debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname);
138 
139 	return(lname);
140 }
141 
142 /*
143  * Return true if name is in the list.
144  */
inlist(list,file)145 int inlist(list, file)
146 	struct namelist *list;
147 	char *file;
148 {
149 	register struct namelist *nl;
150 
151 	for (nl = list; nl != NULL; nl = nl->n_next)
152 		if (strcmp(file, nl->n_name) == 0)
153 			return(1);
154 	return(0);
155 }
156 
157 /*
158  * Run any special commands for this file
159  */
runspecial(starget,opts,rname,destdir)160 static void runspecial(starget, opts, rname, destdir)
161 	char *starget;
162 	opt_t opts;
163 	char *rname;
164 	int destdir;
165 {
166 	register struct subcmd *sc;
167 	extern struct subcmd *subcmds;
168 	char *rfile;
169 
170  	rfile = remfilename(source, Tdest, target, rname, destdir);
171 
172 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
173 		if (sc->sc_type != SPECIAL)
174 			continue;
175 		if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
176 			continue;
177 		message(MT_CHANGE, "special \"%s\"", sc->sc_name);
178 		if (IS_ON(opts, DO_VERIFY))
179 			continue;
180 		(void) sendcmd(C_SPECIAL,
181 			"%s=%s;%s=%s;%s=%s;export %s %s %s;%s",
182 			E_LOCFILE, starget,
183 			E_REMFILE, rfile,
184 			E_BASEFILE, xbasename(rfile),
185 			E_LOCFILE, E_REMFILE, E_BASEFILE,
186 			sc->sc_name);
187 		while (response() > 0)
188 			;
189 	}
190 }
191 
192 /*
193  * If we're doing a target with a "cmdspecial" in it, then
194  * save the name of the file being updated for use with "cmdspecial".
195  */
addcmdspecialfile(starget,rname,destdir)196 static void addcmdspecialfile(starget, rname, destdir)
197 	char *starget;
198 	char *rname;
199 	int destdir;
200 {
201 	char *rfile;
202 	struct namelist *new;
203 	register struct subcmd *sc;
204 	extern struct subcmd *subcmds;
205 	int isokay = 0;
206 
207  	rfile = remfilename(source, Tdest, target, rname, destdir);
208 
209 	for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) {
210 		if (sc->sc_type != CMDSPECIAL)
211 			continue;
212 		if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
213 			continue;
214 		isokay = TRUE;
215 	}
216 
217 	if (isokay) {
218 		new = (struct namelist *) xmalloc(sizeof(struct namelist));
219 		new->n_name = strdup(rfile);
220 		new->n_next = updfilelist;
221 		updfilelist = new;
222 	}
223 }
224 
225 /*
226  * Free the file list
227  */
freecmdspecialfiles()228 static void freecmdspecialfiles()
229 {
230 	register struct namelist *ptr, *save;
231 
232 	for (ptr = updfilelist; ptr; ) {
233 		if (ptr->n_name) (void) free(ptr->n_name);
234 		save = ptr->n_next;
235 		(void) free(ptr);
236 		if (save)
237 			ptr = save->n_next;
238 		else
239 			ptr = NULL;
240 	}
241 	updfilelist = NULL;
242 }
243 
244 /*
245  * Run commands for an entire cmd
246  */
runcmdspecial(cmd,filev,opts)247 extern void runcmdspecial(cmd, filev, opts)
248 	struct cmd *cmd;
249 	char **filev;
250 	opt_t opts;
251 {
252 	register struct subcmd *sc;
253 	register struct namelist *f;
254 	register char **cpp;
255 	char *file;
256 	int first = TRUE;
257 
258 	for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) {
259 		if (sc->sc_type != CMDSPECIAL)
260 			continue;
261 		message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name);
262 		if (IS_ON(opts, DO_VERIFY))
263 			continue;
264 		/* Send all the file names */
265 		for (f = updfilelist; f != NULL; f = f->n_next) {
266 			if (first) {
267 				(void) sendcmd(C_CMDSPECIAL, NULL);
268 				if (response() < 0)
269 					return;
270 				first = FALSE;
271 			}
272 			(void) sendcmd(RC_FILE, f->n_name);
273 			if (response() < 0)
274 				return;
275 		}
276 		if (first) {
277 			(void) sendcmd(C_CMDSPECIAL, NULL);
278 			if (response() < 0)
279 				return;
280 			first = FALSE;
281 		}
282 		/* Send command to run and wait for it to complete */
283 		(void) sendcmd(RC_COMMAND, sc->sc_name);
284 		while (response() > 0)
285 			;
286 		first = TRUE;	/* Reset in case there are more CMDSPECIAL's */
287 	}
288 	freecmdspecialfiles();
289 }
290 
291 /*
292  * For security, reject filenames that contains a newline
293  */
checkfilename(name)294 int checkfilename(name)
295 	char *name;
296 {
297 	register char *cp;
298 
299 	if (strchr(name, '\n')) {
300 		for (cp = name; *cp; cp++)
301 			if (*cp == '\n')
302 				*cp = '?';
303 		message(MT_NERROR,
304 			"Refuse to handle filename containing newline: %s",
305 			name);
306 		return(-1);
307 	}
308 
309 	return(0);
310 }
311 
312 /*
313  * Save and retrieve hard link info
314  */
linkinfo(statp)315 static struct linkbuf *linkinfo(statp)
316 	struct stat *statp;
317 {
318 	struct linkbuf *lp;
319 
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 	(void) strcpy(lp->pathname, target);
333 	(void) strcpy(lp->src, source);
334 	if (Tdest)
335 		(void) strcpy(lp->target, Tdest);
336 	else
337 		*lp->target = CNULL;
338 
339 	return((struct linkbuf *) NULL);
340 }
341 
342 /*
343  * Send a hardlink
344  */
sendhardlink(opts,lp,rname,destdir)345 static int sendhardlink(opts, lp, rname, destdir)
346 	opt_t opts;
347 	struct linkbuf *lp;
348 	char *rname;
349 	int destdir;
350 {
351 	static char buff[MAXPATHLEN];
352 	char *lname;	/* name of file to link to */
353 
354 	debugmsg(DM_MISC,
355 	       "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
356 		 rname, lp->pathname, lp->src, lp->target);
357 
358 	if (*lp->target == CNULL)
359 		(void) sendcmd(C_RECVHARDLINK, "%o %d %s %s",
360 			       opts, strlen(lp->pathname), lp->pathname, rname);
361 	else {
362 		lname = buff;
363 		strcpy(lname, remfilename(lp->src, lp->target,
364 					  lp->pathname, rname,
365 					  destdir));
366 		debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname);
367 		(void) sendcmd(C_RECVHARDLINK, "%o %d %s %s",
368 			       opts, strlen(lname), lname, rname);
369 	}
370 
371 	return(response());
372 }
373 
374 /*
375  * Send a file
376  */
sendfile(rname,opts,stb,user,group,destdir)377 static int sendfile(rname, opts, stb, user, group, destdir)
378 	char *rname;
379 	opt_t opts;
380 	struct stat *stb;
381 	char *user, *group;
382 	int destdir;
383 {
384 	int goterr, f;
385 	off_t i;
386 
387 	if (stb->st_nlink > 1) {
388 		struct linkbuf *lp;
389 
390 		if ((lp = linkinfo(stb)) != NULL)
391 			return(sendhardlink(opts, lp, rname, destdir));
392 	}
393 
394 	if ((f = open(target, O_RDONLY)) < 0) {
395 		error("%s: open for read failed: %s", target, SYSERR);
396 		return(-1);
397 	}
398 
399 	/*
400 	 * Send file info
401 	 */
402 	(void) sendcmd(C_RECVREG, "%o %04o %lld %ld %ld %s %s %s",
403 		       opts, stb->st_mode & 07777,
404 		       (long long) stb->st_size,
405 		       stb->st_mtime, stb->st_atime,
406 		       user, group, rname);
407 	if (response() < 0) {
408 		(void) close(f);
409 		return(-1);
410 	}
411 
412 	debugmsg(DM_MISC, "Send file '%s' %ld bytes\n",
413 		 rname, (long 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 		int 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, amt) != 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, 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  */
rmchk(opts)486 static int rmchk(opts)
487 	opt_t opts;
488 {
489 	register u_char *s;
490 	struct stat stb;
491 	int didupdate = 0;
492 	int n;
493 
494 	debugmsg(DM_CALL, "rmchk()\n");
495 
496 	/*
497 	 * Tell the remote to clean the files from the last directory sent.
498 	 */
499 	(void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY));
500 	if (response() < 0)
501 		return(-1);
502 
503 	for ( ; ; ) {
504 		n = remline(s = respbuff, sizeof(respbuff), TRUE);
505 		if (n <= 0) {
506 			error("rmchk: unexpected control record");
507 			return(didupdate);
508 		}
509 
510 		switch (*s++) {
511 		case CC_QUERY: /* Query if file should be removed */
512 			/*
513 			 * Return the following codes to remove query.
514 			 * CC_NO -- file exists - DON'T remove.
515 			 * CC_YES -- file doesn't exist - REMOVE.
516 			 */
517 			(void) sprintf(ptarget, "%s%s",
518 				       (ptarget[-1] == '/' ? "" : "/"), s);
519 			debugmsg(DM_MISC, "check %s\n", target);
520 			if (except(target))
521 				(void) sendcmd(CC_NO, NULL);
522 			else if (lstat(target, &stb) < 0) {
523 				if (sendcmd(CC_YES, NULL) == 0)
524 					didupdate = 1;
525 			} else
526 				(void) sendcmd(CC_NO, NULL);
527 			break;
528 
529 		case CC_END:
530 			*ptarget = CNULL;
531 			ack();
532 			return(didupdate);
533 
534 		case C_LOGMSG:
535 			if (n > 0)
536 				message(MT_INFO, "%s", s);
537 			break;
538 
539 		case C_NOTEMSG:
540 			if (n > 0)
541 				message(MT_NOTICE, "%s", s);
542 			break;
543 			/* Goto top of loop */
544 
545 		case C_ERRMSG:
546 			message(MT_NERROR, "%s", s);
547 			return(didupdate);
548 
549 		case C_FERRMSG:
550 			message(MT_FERROR, "%s", s);
551 			finish();
552 
553 		default:
554 			error("rmchk: unexpected response '%s'", respbuff);
555 			err();
556 		}
557 	}
558 	/*NOTREACHED*/
559 }
560 
561 /*
562  * Send a directory
563  *
564  * Return < 0 on error.
565  * Return 0 if nothing happened.
566  * Return > 0 if anything is updated.
567  */
senddir(rname,opts,stb,user,group,destdir)568 static int senddir(rname, opts, stb, user, group, destdir)
569 	char *rname;
570 	opt_t opts;
571 	struct stat *stb;
572 	char *user, *group;
573 	int destdir;
574 {
575 	DIRENTRY *dp;
576 	DIR *d;
577 	char *optarget, *cp;
578 	int len;
579 	int didupdate = 0;
580 
581 	/*
582 	 * Don't descend into directory
583 	 */
584 	if (IS_ON(opts, DO_NODESCEND))
585 		return(0);
586 
587 	if ((d = opendir(target)) == NULL) {
588 		error("%s: opendir failed: %s", target, SYSERR);
589 		return(-1);
590 	}
591 
592 	/*
593 	 * Send recvdir command in recvit() format.
594 	 */
595 	(void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s",
596 		       opts, stb->st_mode & 07777, user, group, rname);
597 	if (response() < 0)
598 		return(-1);
599 
600 	if (IS_ON(opts, DO_REMOVE))
601 		if (rmchk(opts) > 0)
602 			++didupdate;
603 
604 	optarget = ptarget;
605 	len = ptarget - target;
606 	while (dp = readdir(d)) {
607 		if (!strcmp(dp->d_name, ".") ||
608 		    !strcmp(dp->d_name, ".."))
609 			continue;
610 		if (len + 1 + (int) strlen(dp->d_name) >= MAXPATHLEN - 1) {
611 			error("%s/%s: Name too long", target,
612 			      dp->d_name);
613 			continue;
614 		}
615 		ptarget = optarget;
616 		if (ptarget[-1] != '/')
617 			*ptarget++ = '/';
618 		cp = dp->d_name;
619 		while (*ptarget++ = *cp++)
620 			;
621 		ptarget--;
622 		if (sendit(dp->d_name, opts, destdir) > 0)
623 			didupdate = 1;
624 	}
625 	(void) closedir(d);
626 
627 	(void) sendcmd(C_END, NULL);
628 	(void) response();
629 
630 	ptarget = optarget;
631 	*ptarget = CNULL;
632 
633 	return(didupdate);
634 }
635 
636 /*
637  * Send a link
638  */
sendlink(rname,opts,stb,user,group,destdir)639 static int sendlink(rname, opts, stb, user, group, destdir)
640 	char *rname;
641 	opt_t opts;
642 	struct stat *stb;
643 	char *user;
644 	char *group;
645 	int destdir;
646 {
647 	int sizerr, f, n;
648 	static char tbuf[BUFSIZ];
649 	char lbuf[MAXPATHLEN];
650 	u_char *s;
651 
652 	debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir);
653 
654 	if (stb->st_nlink > 1) {
655 		struct linkbuf *lp;
656 
657 		if ((lp = linkinfo(stb)) != NULL)
658 			return(sendhardlink(opts, lp, rname, destdir));
659 	}
660 
661 	/*
662 	 * Gather and send basic link info
663 	 */
664 	(void) sendcmd(C_RECVSYMLINK, "%o %04o %lld %ld %ld %s %s %s",
665 		       opts, stb->st_mode & 07777,
666 		       (long long) stb->st_size,
667 		       stb->st_mtime, stb->st_atime,
668 		       user, group, rname);
669 	if (response() < 0)
670 		return(-1);
671 
672 	/*
673 	 * Gather and send additional link info
674 	 */
675 	sizerr = (readlink(target, lbuf, sizeof(lbuf)) != stb->st_size);
676 	(void) sprintf(tbuf, "%.*s", (int) stb->st_size, lbuf);
677 	(void) sendcmd(C_NONE, "%s\n", tbuf);
678 
679 	if (sizerr) {
680 		error("%s: file changed size", target);
681 		err();
682 	} else
683 		ack();
684 
685 	/*
686 	 * Check response
687 	 */
688 	f = response();
689 	if (f < 0)
690 		return(-1);
691 	else if (f == 0 && IS_ON(opts, DO_COMPARE))
692 		return(0);
693 
694 	/*
695 	 * Read and process responses from server.
696 	 * The server may send multiple messages regarding
697 	 * file deletes if the remote target is a directory.
698 	 */
699 	for (;;) {
700 		n = remline(s = respbuff, sizeof(respbuff), TRUE);
701 		if (n == -1)	/* normal EOF */
702 			return(0);
703 		if (n == 0) {
704 			error("expected control record");
705 			continue;
706 		}
707 
708 		switch (*s++) {
709 		case C_END:	/* End of send operation */
710 			*ptarget = CNULL;
711 			ack();
712 			runspecial(target, opts, rname, destdir);
713 			addcmdspecialfile(target, rname, destdir);
714 			return(0);
715 
716 		case C_LOGMSG:
717 			if (n > 0)
718 				message(MT_INFO, "%s", s);
719 			break;
720 
721 		case C_NOTEMSG:
722 			if (n > 0)
723 				message(MT_NOTICE, "%s", s);
724 			break;
725 			/* Goto top of loop */
726 
727 		case C_ERRMSG:
728 			message(MT_NERROR, "%s", s);
729 			return(-1);
730 
731 		case C_FERRMSG:
732 			message(MT_FERROR, "%s", s);
733 			finish();
734 
735 		default:
736 			error("install link: unexpected response '%s'",
737 			      respbuff);
738 			err();
739 		}
740 	}
741 	/*NOTREACHED*/
742 }
743 
744 /*
745  * Check to see if file needs to be updated on the remote machine.
746  * Returns:
747  * 	US_NOTHING	- no update
748  *	US_NOENT	- remote doesn't exist
749  *	US_OUTDATE	- out of date
750  *	US_DOCOMP	- comparing binaries to determine if out of date
751  *	US_MODE		- File modes do not match
752  */
update(rname,opts,statp)753 static int update(rname, opts, statp)
754 	char *rname;
755 	opt_t opts;
756 	struct stat *statp;
757 {
758 	register off_t size;
759 	register time_t mtime;
760 	unsigned short lmode;
761 	unsigned short rmode;
762 	char *owner = NULL, *group = NULL;
763 	int done, n;
764 	char *cp;
765 
766 	debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp);
767 
768 	if (IS_ON(opts, DO_NOEXEC))
769 		if (isexec(target, statp)) {
770 			debugmsg(DM_MISC, "%s is an executable\n", target);
771 			return(US_NOTHING);
772 		}
773 
774 	/*
775 	 * Check to see if the file exists on the remote machine.
776 	 */
777 	(void) sendcmd(C_QUERY, "%s", rname);
778 
779 	for (done = 0; !done;) {
780 		n = remline(cp = respbuff, sizeof(respbuff), TRUE);
781 		if (n <= 0) {
782 			error("update: unexpected control record in response to query");
783 			return(US_NOTHING);
784 		}
785 
786 		switch (*cp++) {
787 		case QC_ONNFS:  /* Resides on a NFS */
788 			debugmsg(DM_MISC,
789 				 "update: %s is on a NFS.  Skipping...\n",
790 				 rname);
791 			return(US_NOTHING);
792 
793 		case QC_SYM:  /* Is a symbolic link */
794 			debugmsg(DM_MISC,
795 				 "update: %s is a symlink.  Skipping...\n",
796 				 rname);
797 			return(US_NOTHING);
798 
799 		case QC_ONRO:  /* Resides on a Read-Only fs */
800 			debugmsg(DM_MISC,
801 				 "update: %s is on a RO fs.  Skipping...\n",
802 				 rname);
803 			return(US_NOTHING);
804 
805 		case QC_YES:
806 			done = 1;
807 			break;
808 
809 		case QC_NO:  /* file doesn't exist so install it */
810 			return(US_NOENT);
811 
812 		case C_ERRMSG:
813 			if (cp)
814 				message(MT_NERROR, "%s", cp);
815 			return(US_NOTHING);
816 
817 		case C_FERRMSG:
818 			if (cp)
819 				message(MT_FERROR, "%s", cp);
820 			finish();
821 
822 		case C_NOTEMSG:
823 			if (cp)
824 				message(MT_NOTICE, "%s", cp);
825 			break;
826 			/* Goto top of loop */
827 
828 		default:
829 			error("update: unexpected response to query '%s'", cp);
830 			return(US_NOTHING);
831 		}
832 	}
833 
834 	/*
835 	 * Target exists, but no other info passed
836 	 */
837 	if (n <= 1 || !S_ISREG(statp->st_mode))
838 		return(US_OUTDATE);
839 
840 	if (IS_ON(opts, DO_COMPARE))
841 		return(US_DOCOMP);
842 
843 	/*
844 	 * Parse size
845 	 */
846 	size = strtoll(cp, &cp, 10);
847 	if (*cp++ != ' ') {
848 		error("update: size not delimited");
849 		return(US_NOTHING);
850 	}
851 
852 	/*
853 	 * Parse mtime
854 	 */
855 	mtime = strtol(cp, &cp, 10);
856 	if (*cp++ != ' ') {
857 		error("update: mtime not delimited");
858 		return(US_NOTHING);
859 	}
860 
861 	/*
862 	 * Parse remote file mode
863 	 */
864 	rmode = strtol(cp, &cp, 8);
865 	if (cp && *cp)
866 		++cp;
867 
868 	/*
869 	 * Be backwards compatible
870 	 */
871 	if (cp && *cp != CNULL) {
872 		/*
873 		 * Parse remote file owner
874 		 */
875 		owner = strtok((char *)cp, " ");
876 		if (owner == NULL) {
877 			error("update: owner not delimited");
878 			return(US_NOTHING);
879 		}
880 
881 		/*
882 		 * Parse remote file group
883 		 */
884 		group = strtok((char *) NULL, " ");
885 		if (group == NULL) {
886 			error("update: group not delimited");
887 			return(US_NOTHING);
888 		}
889 	}
890 
891 	/*
892 	 * File needs to be updated?
893 	 */
894 	lmode = statp->st_mode & 07777;
895 
896 	debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n",
897 		 rname, lmode, rmode);
898 	debugmsg(DM_MISC, "update(%s,) size %lld mtime %d owner '%s' grp '%s'\n",
899 		 rname, (long long) size, mtime, owner, group);
900 
901 	if (statp->st_mtime != mtime) {
902 		if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
903 			message(MT_WARNING,
904 				"%s: Warning: remote copy is newer",
905 				target);
906 			return(US_NOTHING);
907 		}
908 		return(US_OUTDATE);
909 	}
910 
911 	/*
912 	 * If the mode of a file does not match the local mode, the
913 	 * whole file is updated.  This is done both to insure that
914 	 * a bogus version of the file has not been installed and to
915 	 * avoid having to handle weird cases of chmod'ing symlinks
916 	 * and such.
917 	 */
918 	if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
919 		debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n",
920 			 lmode, rmode);
921 		return(US_OUTDATE);
922 	}
923 
924 	if (statp->st_size != size) {
925 		debugmsg(DM_MISC, "size does not match (%lld != %lld).\n",
926 			 (long long) statp->st_size, (long long)size);
927 		return(US_OUTDATE);
928 	}
929 
930 	/*
931 	 * Check ownership
932 	 */
933 	if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
934 		if (!IS_ON(opts, DO_NUMCHKOWNER)) {
935 			/* Check by string compare */
936 			if (strcmp(owner, getusername(statp->st_uid,
937 						      target, opts)) != 0) {
938 				debugmsg(DM_MISC,
939 					 "owner does not match (%s != %s).\n",
940 					 getusername(statp->st_uid,
941 						     target, opts), owner);
942 				return(US_OUTDATE);
943 			}
944 		} else {
945 			/*
946 			 * Check numerically.
947 			 * Allow negative numbers.
948 			 */
949 			while (*owner && !isdigit(*owner) && (*owner != '-'))
950 				++owner;
951 			if (owner && atoi(owner) != statp->st_uid) {
952 				debugmsg(DM_MISC,
953 					 "owner does not match (%d != %s).\n",
954 					 statp->st_uid, owner);
955 				return(US_OUTDATE);
956 			}
957 		}
958 	}
959 
960 	if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
961 		if (!IS_ON(opts, DO_NUMCHKGROUP)) {
962 			/* Check by string compare */
963 			if (strcmp(group, getgroupname(statp->st_gid,
964 						       target, opts)) != 0) {
965 				debugmsg(DM_MISC,
966 					 "group does not match (%s != %s).\n",
967 					 getgroupname(statp->st_gid,
968 						      target, opts), group);
969 				return(US_OUTDATE);
970 			}
971 		} else {
972 			/* Check numerically */
973 			/* Allow negative gid */
974 			while (*group && !isdigit(*group) && (*group != '-'))
975 				++group;
976 			if (group && atoi(group) != statp->st_gid) {
977 				debugmsg(DM_MISC,
978 					 "group does not match (%d != %s).\n",
979 					 statp->st_gid, group);
980 				return(US_OUTDATE);
981 			}
982 		}
983 	}
984 
985 	return(US_NOTHING);
986 }
987 
988 /*
989  * Stat a file
990  */
dostat(file,statbuf,opts)991 static int dostat(file, statbuf, opts)
992 	char *file;
993 	struct stat *statbuf;
994 	opt_t opts;
995 {
996 	int s;
997 
998 	if (IS_ON(opts, DO_FOLLOW))
999 		s = stat(file, statbuf);
1000 	else
1001 		s = lstat(file, statbuf);
1002 
1003 	if (s < 0)
1004 		error("%s: %s failed: %s", file,
1005 		      IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
1006 	return(s);
1007 }
1008 
1009 /*
1010  * Transfer the file or directory in target[].
1011  * rname is the name of the file on the remote host.
1012  *
1013  * Return < 0 on error.
1014  * Return 0 if nothing happened.
1015  * Return > 0 if anything is updated.
1016  */
sendit(rname,opts,destdir)1017 static int sendit(rname, opts, destdir)
1018 	char *rname;
1019 	opt_t opts;
1020 	int destdir;
1021 {
1022 	static struct stat stb;
1023 	extern struct subcmd *subcmds;
1024 	char *user, *group;
1025 	int u, len;
1026 	int didupdate = 0;
1027 
1028 	/*
1029 	 * Remove possible accidental newline
1030 	 */
1031 	len = strlen(rname);
1032 	if (len > 0 && rname[len-1] == '\n')
1033 		rname[len-1] = CNULL;
1034 
1035 	if (checkfilename(rname) != 0)
1036 		return(-1);
1037 
1038 	debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts);
1039 
1040 	if (except(target))
1041 		return(0);
1042 
1043 	if (dostat(target, &stb, opts) < 0)
1044 		return(-1);
1045 
1046 	/*
1047 	 * Does rname need updating?
1048 	 */
1049 	u = update(rname, opts, &stb);
1050 	debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n",
1051 		 rname, opts, target, u);
1052 
1053 	/*
1054 	 * Don't need to update the file, but we may need to save hardlink
1055 	 * info.
1056 	 */
1057 	if (u == US_NOTHING) {
1058 		if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
1059 			(void) linkinfo(&stb);
1060 		return(0);
1061 	}
1062 
1063 	/*
1064 	 * File mode needs changing
1065 	 */
1066 	if (u == US_MODE) {
1067 		if (IS_ON(opts, DO_VERIFY)) {
1068 			message(MT_INFO, "%s: need to chmod to %04o",
1069 				target, stb.st_mode & 07777);
1070 			runspecial(target, opts, rname, destdir);
1071 			return(1);
1072 		}
1073 		message(MT_CHANGE, "%s: chmod to %04o",
1074 			target, stb.st_mode & 07777);
1075 		(void) sendcmd(C_CHMOD, "%o %04o %s",
1076 			       opts, stb.st_mode & 07777, rname);
1077 		(void) response();
1078 		return(1);
1079 	}
1080 
1081 	user = getusername(stb.st_uid, target, opts);
1082 	group = getgroupname(stb.st_gid, target, opts);
1083 
1084 	/*
1085 	 * No entry - need to install
1086 	 */
1087 	if (u == US_NOENT) {
1088 		if (IS_ON(opts, DO_VERIFY)) {
1089 			message(MT_INFO, "%s: need to install", target);
1090 			runspecial(target, opts, rname, destdir);
1091 			return(1);
1092 		}
1093 		if (!IS_ON(opts, DO_QUIET))
1094 			message(MT_CHANGE, "%s: installing", target);
1095 		FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
1096 	}
1097 
1098 	/*
1099 	 * Handle special file types, including directories and symlinks
1100 	 */
1101 	if (S_ISDIR(stb.st_mode)) {
1102 		if (senddir(rname, opts, &stb, user, group, destdir) > 0)
1103 			didupdate = 1;
1104 	} else if (S_ISLNK(stb.st_mode)) {
1105 		if (u != US_NOENT)
1106 			FLAG_ON(opts, DO_COMPARE);
1107 		/*
1108 		 * Since we always send link info to the server
1109 		 * so the server can determine if the remote link
1110 		 * is correct, we never get any acknowledge meant
1111 		 * from the server whether the link was really
1112 		 * updated or not.
1113 		 */
1114 		(void) sendlink(rname, opts, &stb, user, group, destdir);
1115 	} else if (S_ISREG(stb.st_mode)) {
1116 		if (u == US_OUTDATE) {
1117 			if (IS_ON(opts, DO_VERIFY)) {
1118 				message(MT_INFO, "%s: need to update", target);
1119 				runspecial(target, opts, rname, destdir);
1120 				return(1);
1121 			}
1122 			if (!IS_ON(opts, DO_QUIET))
1123 				message(MT_CHANGE, "%s: updating", target);
1124 		}
1125 		if (sendfile(rname, opts, &stb, user, group, destdir) == 0)
1126 			didupdate = 1;
1127 	} else
1128 		error("%s: unknown file type", target);
1129 
1130 	return(didupdate);
1131 }
1132 
1133 /*
1134  * Remove temporary files and do any cleanup operations before exiting.
1135  */
cleanup()1136 extern void cleanup()
1137 {
1138 	char *file;
1139 #ifdef USE_STATDB
1140 	extern char statfile[];
1141 
1142 	(void) unlink(statfile);
1143 #endif
1144 
1145 	if (file = getnotifyfile())
1146 		(void) unlink(file);
1147 }
1148 
1149 /*
1150  * Update the file(s) if they are different.
1151  * destdir = 1 if destination should be a directory
1152  * (i.e., more than one source is being copied to the same destination).
1153  *
1154  * Return < 0 on error.
1155  * Return 0 if nothing updated.
1156  * Return > 0 if something was updated.
1157  */
1158 extern int install(src, dest, ddir, destdir, opts)
1159 	char *src, *dest;
1160  	int ddir, destdir;
1161 	opt_t opts;
1162 {
1163 	static char destcopy[MAXPATHLEN];
1164 	char *rname;
1165 	int didupdate = 0;
1166 
1167 	debugmsg(DM_CALL,
1168 		"install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n",
1169 		(src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
1170 	/*
1171 	 * Save source name
1172 	 */
1173 	if (IS_ON(opts, DO_WHOLE))
1174 		source[0] = CNULL;
1175 	else
1176 		(void) strcpy(source, src);
1177 
1178 	if (dest == NULL) {
1179 		FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
1180 		dest = src;
1181 	}
1182 
1183 	if (checkfilename(dest) != 0)
1184 		return(-1);
1185 
1186 	if (nflag || debug) {
1187 		static char buff[BUFSIZ];
1188 		char *cp;
1189 
1190 		cp = getondistoptlist(opts);
1191 		(void) sprintf(buff, "%s%s%s %s %s",
1192 			       IS_ON(opts, DO_VERIFY) ? "verify" : "install",
1193 			       (cp) ? " -o" : "", (cp) ? cp : "",
1194 			       src, dest);
1195 		if (nflag) {
1196 			printf("%s\n", buff);
1197 			return(0);
1198 		} else
1199 			debugmsg(DM_MISC, "%s\n", buff);
1200 	}
1201 
1202 	rname = exptilde(target, src);
1203 	if (rname == NULL)
1204 		return(-1);
1205 	ptarget = target;
1206 	while (*ptarget)
1207 		ptarget++;
1208 	/*
1209 	 * If we are renaming a directory and we want to preserve
1210 	 * the directory heirarchy (-w), we must strip off the leading
1211 	 * directory name and preserve the rest.
1212 	 */
1213 	if (IS_ON(opts, DO_WHOLE)) {
1214 		while (*rname == '/')
1215 			rname++;
1216 		ddir = 1;
1217 		destdir = 1;
1218 	} else {
1219 		rname = strrchr(target, '/');
1220 		/* Check if no '/' or target ends in '/' */
1221 		if (rname == NULL ||
1222 		    rname+1 == NULL ||
1223 		    *(rname+1) == CNULL)
1224 			rname = target;
1225 		else
1226 			rname++;
1227 	}
1228 
1229 	debugmsg(DM_MISC,
1230  	"install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n",
1231  		 target, source, rname, dest, destdir, ddir);
1232 
1233 	/*
1234 	 * Pass the destination file/directory name to remote.
1235 	 */
1236  	if (ddir)
1237 		(void) sendcmd(C_DIRTARGET, "%o %s", opts, dest);
1238 	else
1239 		(void) sendcmd(C_TARGET, "%o %s", opts, dest);
1240 	if (response() < 0)
1241 		return(-1);
1242 
1243 	/*
1244 	 * Save the name of the remote target destination if we are
1245 	 * in WHOLE mode (destdir > 0) or if the source and destination
1246 	 * are not the same.  This info will be used later for maintaining
1247 	 * hardlink info.
1248 	 */
1249 	if (destdir || (src && dest && strcmp(src, dest))) {
1250 		(void) strcpy(destcopy, dest);
1251 		Tdest = destcopy;
1252 	}
1253 
1254 	didupdate = sendit(rname, opts, destdir);
1255 	Tdest = 0;
1256 
1257 	return(didupdate);
1258 }
1259