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