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