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