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