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