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