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