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