Annotation of src/usr.bin/rdist/server.c, Revision 1.2
1.1 deraadt 1: /*
2: * Copyright (c) 1983, 1993
3: * The Regents of the University of California. All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: */
33:
34: #ifndef lint
35: /* from: static char sccsid[] = "@(#)server.c 8.1 (Berkeley) 6/9/93"; */
1.2 ! deraadt 36: static char *rcsid = "$Id: server.c,v 1.1.1.1 1995/10/18 08:45:59 deraadt Exp $";
1.1 deraadt 37: #endif /* not lint */
38:
39: #include <sys/wait.h>
40: #include "defs.h"
41:
42: #define ack() (void) write(rem, "\0\n", 2)
43: #define err() (void) write(rem, "\1\n", 2)
44:
45: struct linkbuf *ihead; /* list of files with more than one link */
46: char buf[BUFSIZ]; /* general purpose buffer */
47: char target[BUFSIZ]; /* target/source directory name */
48: char *tp; /* pointer to end of target name */
49: char *Tdest; /* pointer to last T dest*/
50: int catname; /* cat name to target name */
51: char *stp[32]; /* stack of saved tp's for directories */
52: int oumask; /* old umask for creating files */
53:
54: extern FILE *lfp; /* log file for mailing changes */
55:
56: static int chkparent __P((char *));
57: static void clean __P((char *));
58: static void comment __P((char *));
59: static void dospecial __P((char *));
60: static int fchog __P((int, char *, char *, char *, int));
61: static void hardlink __P((char *));
62: static void note __P((const char *, ...));
63: static void query __P((char *));
64: static void recvf __P((char *, int));
65: static void removeit __P((struct stat *));
66: static int response __P((void));
67: static void rmchk __P((int));
68: static struct linkbuf *
69: savelink __P((struct stat *));
70: static void sendf __P((char *, int));
71: static int update __P((char *, int, struct stat *));
72:
73: /*
74: * Server routine to read requests and process them.
75: * Commands are:
76: * Tname - Transmit file if out of date
77: * Vname - Verify if file out of date or not
78: * Qname - Query if file exists. Return mtime & size if it does.
79: */
80: void
81: server()
82: {
83: char cmdbuf[BUFSIZ];
84: register char *cp;
85:
86: signal(SIGHUP, cleanup);
87: signal(SIGINT, cleanup);
88: signal(SIGQUIT, cleanup);
89: signal(SIGTERM, cleanup);
90: signal(SIGPIPE, cleanup);
91:
92: rem = 0;
93: oumask = umask(0);
94: (void) sprintf(buf, "V%d\n", VERSION);
95: (void) write(rem, buf, strlen(buf));
96:
97: for (;;) {
98: cp = cmdbuf;
99: if (read(rem, cp, 1) <= 0)
100: return;
101: if (*cp++ == '\n') {
102: error("server: expected control record\n");
103: continue;
104: }
105: do {
106: if (read(rem, cp, 1) != 1)
107: cleanup(0);
108: } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
109: *--cp = '\0';
110: cp = cmdbuf;
111: switch (*cp++) {
112: case 'T': /* init target file/directory name */
113: catname = 1; /* target should be directory */
114: goto dotarget;
115:
116: case 't': /* init target file/directory name */
117: catname = 0;
118: dotarget:
119: if (exptilde(target, cp) == NULL)
120: continue;
121: tp = target;
122: while (*tp)
123: tp++;
124: ack();
125: continue;
126:
127: case 'R': /* Transfer a regular file. */
128: recvf(cp, S_IFREG);
129: continue;
130:
131: case 'D': /* Transfer a directory. */
132: recvf(cp, S_IFDIR);
133: continue;
134:
135: case 'K': /* Transfer symbolic link. */
136: recvf(cp, S_IFLNK);
137: continue;
138:
139: case 'k': /* Transfer hard link. */
140: hardlink(cp);
141: continue;
142:
143: case 'E': /* End. (of directory) */
144: *tp = '\0';
145: if (catname <= 0) {
146: error("server: too many 'E's\n");
147: continue;
148: }
149: tp = stp[--catname];
150: *tp = '\0';
151: ack();
152: continue;
153:
154: case 'C': /* Clean. Cleanup a directory */
155: clean(cp);
156: continue;
157:
158: case 'Q': /* Query. Does the file/directory exist? */
159: query(cp);
160: continue;
161:
162: case 'S': /* Special. Execute commands */
163: dospecial(cp);
164: continue;
165:
166: #ifdef notdef
167: /*
168: * These entries are reserved but not currently used.
169: * The intent is to allow remote hosts to have master copies.
170: * Currently, only the host rdist runs on can have masters.
171: */
172: case 'X': /* start a new list of files to exclude */
173: except = bp = NULL;
174: case 'x': /* add name to list of files to exclude */
175: if (*cp == '\0') {
176: ack();
177: continue;
178: }
179: if (*cp == '~') {
180: if (exptilde(buf, cp) == NULL)
181: continue;
182: cp = buf;
183: }
184: if (bp == NULL)
185: except = bp = expand(makeblock(NAME, cp), E_VARS);
186: else
187: bp->b_next = expand(makeblock(NAME, cp), E_VARS);
188: while (bp->b_next != NULL)
189: bp = bp->b_next;
190: ack();
191: continue;
192:
193: case 'I': /* Install. Transfer file if out of date. */
194: opts = 0;
195: while (*cp >= '0' && *cp <= '7')
196: opts = (opts << 3) | (*cp++ - '0');
197: if (*cp++ != ' ') {
198: error("server: options not delimited\n");
199: return;
200: }
201: install(cp, opts);
202: continue;
203:
204: case 'L': /* Log. save message in log file */
205: log(lfp, cp);
206: continue;
207: #endif
208:
209: case '\1':
210: nerrs++;
211: continue;
212:
213: case '\2':
214: return;
215:
216: default:
217: error("server: unknown command '%s'\n", cp);
218: case '\0':
219: continue;
220: }
221: }
222: }
223:
224: /*
225: * Update the file(s) if they are different.
226: * destdir = 1 if destination should be a directory
227: * (i.e., more than one source is being copied to the same destination).
228: */
229: void
230: install(src, dest, destdir, opts)
231: char *src, *dest;
232: int destdir, opts;
233: {
234: char *rname;
235: char destcopy[BUFSIZ];
236:
237: if (dest == NULL) {
238: opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
239: dest = src;
240: }
241:
242: if (nflag || debug) {
243: printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
244: opts & WHOLE ? " -w" : "",
245: opts & YOUNGER ? " -y" : "",
246: opts & COMPARE ? " -b" : "",
247: opts & REMOVE ? " -R" : "", src, dest);
248: if (nflag)
249: return;
250: }
251:
252: rname = exptilde(target, src);
253: if (rname == NULL)
254: return;
255: tp = target;
256: while (*tp)
257: tp++;
258: /*
259: * If we are renaming a directory and we want to preserve
260: * the directory heirarchy (-w), we must strip off the leading
261: * directory name and preserve the rest.
262: */
263: if (opts & WHOLE) {
264: while (*rname == '/')
265: rname++;
266: destdir = 1;
267: } else {
268: rname = rindex(target, '/');
269: if (rname == NULL)
270: rname = target;
271: else
272: rname++;
273: }
274: if (debug)
275: printf("target = %s, rname = %s\n", target, rname);
276: /*
277: * Pass the destination file/directory name to remote.
278: */
279: (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
280: if (debug)
281: printf("buf = %s", buf);
282: (void) write(rem, buf, strlen(buf));
283: if (response() < 0)
284: return;
285:
286: if (destdir) {
287: strcpy(destcopy, dest);
288: Tdest = destcopy;
289: }
290: sendf(rname, opts);
291: Tdest = 0;
292: }
293:
294: #define protoname() (pw ? pw->pw_name : user)
295: #define protogroup() (gr ? gr->gr_name : group)
296: /*
297: * Transfer the file or directory in target[].
298: * rname is the name of the file on the remote host.
299: */
300: static void
301: sendf(rname, opts)
302: char *rname;
303: int opts;
304: {
305: register struct subcmd *sc;
306: struct stat stb;
307: int sizerr, f, u, len;
308: off_t i;
309: DIR *d;
310: struct direct *dp;
311: char *otp, *cp;
312: extern struct subcmd *subcmds;
313: static char user[15], group[15];
314:
315: if (debug)
316: printf("sendf(%s, %x)\n", rname, opts);
317:
318: if (except(target))
319: return;
320: if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
321: error("%s: %s\n", target, strerror(errno));
322: return;
323: }
324: if ((u = update(rname, opts, &stb)) == 0) {
325: if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
326: (void) savelink(&stb);
327: return;
328: }
329:
330: if (pw == NULL || pw->pw_uid != stb.st_uid)
331: if ((pw = getpwuid(stb.st_uid)) == NULL) {
332: log(lfp, "%s: no password entry for uid %d \n",
333: target, stb.st_uid);
334: pw = NULL;
335: (void)sprintf(user, ":%lu", stb.st_uid);
336: }
337: if (gr == NULL || gr->gr_gid != stb.st_gid)
338: if ((gr = getgrgid(stb.st_gid)) == NULL) {
339: log(lfp, "%s: no name for group %d\n",
340: target, stb.st_gid);
341: gr = NULL;
342: (void)sprintf(group, ":%lu", stb.st_gid);
343: }
344: if (u == 1) {
345: if (opts & VERIFY) {
346: log(lfp, "need to install: %s\n", target);
347: goto dospecial;
348: }
349: log(lfp, "installing: %s\n", target);
350: opts &= ~(COMPARE|REMOVE);
351: }
352:
353: switch (stb.st_mode & S_IFMT) {
354: case S_IFDIR:
355: if ((d = opendir(target)) == NULL) {
356: error("%s: %s\n", target, strerror(errno));
357: return;
358: }
359: (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
360: stb.st_mode & 07777, protoname(), protogroup(), rname);
361: if (debug)
362: printf("buf = %s", buf);
363: (void) write(rem, buf, strlen(buf));
364: if (response() < 0) {
365: closedir(d);
366: return;
367: }
368:
369: if (opts & REMOVE)
370: rmchk(opts);
371:
372: otp = tp;
373: len = tp - target;
374: while (dp = readdir(d)) {
375: if (!strcmp(dp->d_name, ".") ||
376: !strcmp(dp->d_name, ".."))
377: continue;
378: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
379: error("%s/%s: Name too long\n", target,
380: dp->d_name);
381: continue;
382: }
383: tp = otp;
384: *tp++ = '/';
385: cp = dp->d_name;
386: while (*tp++ = *cp++)
387: ;
388: tp--;
389: sendf(dp->d_name, opts);
390: }
391: closedir(d);
392: (void) write(rem, "E\n", 2);
393: (void) response();
394: tp = otp;
395: *tp = '\0';
396: return;
397:
398: case S_IFLNK:
399: if (u != 1)
400: opts |= COMPARE;
401: if (stb.st_nlink > 1) {
402: struct linkbuf *lp;
403:
404: if ((lp = savelink(&stb)) != NULL) {
405: /* install link */
406: if (*lp->target == 0)
407: (void) sprintf(buf, "k%o %s %s\n", opts,
408: lp->pathname, rname);
409: else
410: (void) sprintf(buf, "k%o %s/%s %s\n", opts,
411: lp->target, lp->pathname, rname);
412: if (debug)
413: printf("buf = %s", buf);
414: (void) write(rem, buf, strlen(buf));
415: (void) response();
416: return;
417: }
418: }
419: (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts,
420: stb.st_mode & 07777, stb.st_size, stb.st_mtime,
421: protoname(), protogroup(), rname);
422: if (debug)
423: printf("buf = %s", buf);
424: (void) write(rem, buf, strlen(buf));
425: if (response() < 0)
426: return;
427: sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
428: (void) write(rem, buf, stb.st_size);
429: if (debug)
430: printf("readlink = %.*s\n", (int)stb.st_size, buf);
431: goto done;
432:
433: case S_IFREG:
434: break;
435:
436: default:
437: error("%s: not a file or directory\n", target);
438: return;
439: }
440:
441: if (u == 2) {
442: if (opts & VERIFY) {
443: log(lfp, "need to update: %s\n", target);
444: goto dospecial;
445: }
446: log(lfp, "updating: %s\n", target);
447: }
448:
449: if (stb.st_nlink > 1) {
450: struct linkbuf *lp;
451:
452: if ((lp = savelink(&stb)) != NULL) {
453: /* install link */
454: if (*lp->target == 0)
455: (void) sprintf(buf, "k%o %s %s\n", opts,
456: lp->pathname, rname);
457: else
458: (void) sprintf(buf, "k%o %s/%s %s\n", opts,
459: lp->target, lp->pathname, rname);
460: if (debug)
461: printf("buf = %s", buf);
462: (void) write(rem, buf, strlen(buf));
463: (void) response();
464: return;
465: }
466: }
467:
468: if ((f = open(target, O_RDONLY, 0)) < 0) {
469: error("%s: %s\n", target, strerror(errno));
470: return;
471: }
472: (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts,
473: stb.st_mode & 07777, stb.st_size, stb.st_mtime,
474: protoname(), protogroup(), rname);
475: if (debug)
476: printf("buf = %s", buf);
477: (void) write(rem, buf, strlen(buf));
478: if (response() < 0) {
479: (void) close(f);
480: return;
481: }
482: sizerr = 0;
483: for (i = 0; i < stb.st_size; i += BUFSIZ) {
484: int amt = BUFSIZ;
485: if (i + amt > stb.st_size)
486: amt = stb.st_size - i;
487: if (sizerr == 0 && read(f, buf, amt) != amt)
488: sizerr = 1;
489: (void) write(rem, buf, amt);
490: }
491: (void) close(f);
492: done:
493: if (sizerr) {
494: error("%s: file changed size\n", target);
495: err();
496: } else
497: ack();
498: f = response();
499: if (f < 0 || f == 0 && (opts & COMPARE))
500: return;
501: dospecial:
502: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
503: if (sc->sc_type != SPECIAL)
504: continue;
505: if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
506: continue;
507: log(lfp, "special \"%s\"\n", sc->sc_name);
508: if (opts & VERIFY)
509: continue;
510: (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
511: if (debug)
512: printf("buf = %s", buf);
513: (void) write(rem, buf, strlen(buf));
514: while (response() > 0)
515: ;
516: }
517: }
518:
519: static struct linkbuf *
520: savelink(stp)
521: struct stat *stp;
522: {
523: struct linkbuf *lp;
524:
525: for (lp = ihead; lp != NULL; lp = lp->nextp)
526: if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
527: lp->count--;
528: return(lp);
529: }
530: lp = (struct linkbuf *) malloc(sizeof(*lp));
531: if (lp == NULL)
532: log(lfp, "out of memory, link information lost\n");
533: else {
534: lp->nextp = ihead;
535: ihead = lp;
536: lp->inum = stp->st_ino;
537: lp->devnum = stp->st_dev;
538: lp->count = stp->st_nlink - 1;
539: strcpy(lp->pathname, target);
540: if (Tdest)
541: strcpy(lp->target, Tdest);
542: else
543: *lp->target = 0;
544: }
545: return(NULL);
546: }
547:
548: /*
549: * Check to see if file needs to be updated on the remote machine.
550: * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
551: * and 3 if comparing binaries to determine if out of date.
552: */
553: static int
554: update(rname, opts, stp)
555: char *rname;
556: int opts;
557: struct stat *stp;
558: {
559: register char *cp, *s;
560: register off_t size;
561: register time_t mtime;
562:
563: if (debug)
564: printf("update(%s, %x, %x)\n", rname, opts, stp);
565:
566: /*
567: * Check to see if the file exists on the remote machine.
568: */
569: (void) sprintf(buf, "Q%s\n", rname);
570: if (debug)
571: printf("buf = %s", buf);
572: (void) write(rem, buf, strlen(buf));
573: again:
574: cp = s = buf;
575: do {
576: if (read(rem, cp, 1) != 1)
577: lostconn(0);
578: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
579:
580: switch (*s++) {
581: case 'Y':
582: break;
583:
584: case 'N': /* file doesn't exist so install it */
585: return(1);
586:
587: case '\1':
588: nerrs++;
589: if (*s != '\n') {
590: if (!iamremote) {
591: fflush(stdout);
592: (void) write(2, s, cp - s);
593: }
594: if (lfp != NULL)
595: (void) fwrite(s, 1, cp - s, lfp);
596: }
597: return(0);
598:
599: case '\3':
600: *--cp = '\0';
601: if (lfp != NULL)
602: log(lfp, "update: note: %s\n", s);
603: goto again;
604:
605: default:
606: *--cp = '\0';
607: error("update: unexpected response '%s'\n", s);
608: return(0);
609: }
610:
611: if (*s == '\n')
612: return(2);
613:
614: if (opts & COMPARE)
615: return(3);
616:
617: size = 0;
618: while (isdigit(*s))
619: size = size * 10 + (*s++ - '0');
620: if (*s++ != ' ') {
621: error("update: size not delimited\n");
622: return(0);
623: }
624: mtime = 0;
625: while (isdigit(*s))
626: mtime = mtime * 10 + (*s++ - '0');
627: if (*s != '\n') {
628: error("update: mtime not delimited\n");
629: return(0);
630: }
631: /*
632: * File needs to be updated?
633: */
634: if (opts & YOUNGER) {
635: if (stp->st_mtime == mtime)
636: return(0);
637: if (stp->st_mtime < mtime) {
638: log(lfp, "Warning: %s: remote copy is newer\n", target);
639: return(0);
640: }
641: } else if (stp->st_mtime == mtime && stp->st_size == size)
642: return(0);
643: return(2);
644: }
645:
646: /*
647: * Query. Check to see if file exists. Return one of the following:
648: * N\n - doesn't exist
649: * Ysize mtime\n - exists and its a regular file (size & mtime of file)
650: * Y\n - exists and its a directory or symbolic link
651: * ^Aerror message\n
652: */
653: static void
654: query(name)
655: char *name;
656: {
657: struct stat stb;
658:
659: if (catname)
660: (void) sprintf(tp, "/%s", name);
661:
662: if (lstat(target, &stb) < 0) {
663: if (errno == ENOENT)
664: (void) write(rem, "N\n", 2);
665: else
666: error("%s:%s: %s\n", host, target, strerror(errno));
667: *tp = '\0';
668: return;
669: }
670:
671: switch (stb.st_mode & S_IFMT) {
672: case S_IFREG:
673: (void) sprintf(buf, "Y%qd %ld\n", stb.st_size,
674: stb.st_mtime);
675: (void) write(rem, buf, strlen(buf));
676: break;
677:
678: case S_IFLNK:
679: case S_IFDIR:
680: (void) write(rem, "Y\n", 2);
681: break;
682:
683: default:
684: error("%s: not a file or directory\n", name);
685: break;
686: }
687: *tp = '\0';
688: }
689:
690: static void
691: recvf(cmd, type)
692: char *cmd;
693: int type;
694: {
695: register char *cp;
696: int f, mode, opts, wrerr, olderrno;
697: off_t i, size;
698: time_t mtime;
699: struct stat stb;
700: struct timeval tvp[2];
701: char *owner, *group;
702: char new[BUFSIZ];
703: extern char *tempname;
704:
705: cp = cmd;
706: opts = 0;
1.2 ! deraadt 707: f = 0; /* Initialize, so for links it remains 0. */
1.1 deraadt 708: while (*cp >= '0' && *cp <= '7')
709: opts = (opts << 3) | (*cp++ - '0');
710: if (*cp++ != ' ') {
711: error("recvf: options not delimited\n");
712: return;
713: }
714: mode = 0;
715: while (*cp >= '0' && *cp <= '7')
716: mode = (mode << 3) | (*cp++ - '0');
717: if (*cp++ != ' ') {
718: error("recvf: mode not delimited\n");
719: return;
720: }
721: size = 0;
722: while (isdigit(*cp))
723: size = size * 10 + (*cp++ - '0');
724: if (*cp++ != ' ') {
725: error("recvf: size not delimited\n");
726: return;
727: }
728: mtime = 0;
729: while (isdigit(*cp))
730: mtime = mtime * 10 + (*cp++ - '0');
731: if (*cp++ != ' ') {
732: error("recvf: mtime not delimited\n");
733: return;
734: }
735: owner = cp;
736: while (*cp && *cp != ' ')
737: cp++;
738: if (*cp != ' ') {
739: error("recvf: owner name not delimited\n");
740: return;
741: }
742: *cp++ = '\0';
743: group = cp;
744: while (*cp && *cp != ' ')
745: cp++;
746: if (*cp != ' ') {
747: error("recvf: group name not delimited\n");
748: return;
749: }
750: *cp++ = '\0';
751:
752: if (type == S_IFDIR) {
753: if (catname >= sizeof(stp)) {
754: error("%s:%s: too many directory levels\n",
755: host, target);
756: return;
757: }
758: stp[catname] = tp;
759: if (catname++) {
760: *tp++ = '/';
761: while (*tp++ = *cp++)
762: ;
763: tp--;
764: }
765: if (opts & VERIFY) {
766: ack();
767: return;
768: }
769: if (lstat(target, &stb) == 0) {
770: if (ISDIR(stb.st_mode)) {
771: if ((stb.st_mode & 07777) == mode) {
772: ack();
773: return;
774: }
775: buf[0] = '\0';
776: (void) sprintf(buf + 1,
777: "%s: Warning: remote mode %o != local mode %o\n",
778: target, stb.st_mode & 07777, mode);
779: (void) write(rem, buf, strlen(buf + 1) + 1);
780: return;
781: }
782: errno = ENOTDIR;
783: } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
784: chkparent(target) == 0 && mkdir(target, mode) == 0)) {
785: if (fchog(-1, target, owner, group, mode) == 0)
786: ack();
787: return;
788: }
789: error("%s:%s: %s\n", host, target, strerror(errno));
790: tp = stp[--catname];
791: *tp = '\0';
792: return;
793: }
794:
795: if (catname)
796: (void) sprintf(tp, "/%s", cp);
797: cp = rindex(target, '/');
798: if (cp == NULL)
799: strcpy(new, tempname);
800: else if (cp == target)
801: (void) sprintf(new, "/%s", tempname);
802: else {
803: *cp = '\0';
804: (void) sprintf(new, "%s/%s", target, tempname);
805: *cp = '/';
806: }
807:
808: if (type == S_IFLNK) {
809: int j;
810:
811: ack();
812: cp = buf;
813: for (i = 0; i < size; i += j) {
814: if ((j = read(rem, cp, size - i)) <= 0)
815: cleanup(0);
816: cp += j;
817: }
818: *cp = '\0';
819: if (response() < 0) {
820: err();
821: return;
822: }
823: if (symlink(buf, new) < 0) {
824: if (errno != ENOENT || chkparent(new) < 0 ||
825: symlink(buf, new) < 0)
826: goto badnew1;
827: }
828: mode &= 0777;
829: if (opts & COMPARE) {
830: char tbuf[BUFSIZ];
831:
832: if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
833: i == size && strncmp(buf, tbuf, size) == 0) {
834: (void) unlink(new);
835: ack();
836: return;
837: }
838: if (opts & VERIFY)
839: goto differ;
840: }
841: goto fixup;
842: }
843:
844: if ((f = creat(new, mode)) < 0) {
845: if (errno != ENOENT || chkparent(new) < 0 ||
846: (f = creat(new, mode)) < 0)
847: goto badnew1;
848: }
849:
850: ack();
851: wrerr = 0;
852: for (i = 0; i < size; i += BUFSIZ) {
853: int amt = BUFSIZ;
854:
855: cp = buf;
856: if (i + amt > size)
857: amt = size - i;
858: do {
859: int j = read(rem, cp, amt);
860:
861: if (j <= 0) {
862: (void) close(f);
863: (void) unlink(new);
864: cleanup(0);
865: }
866: amt -= j;
867: cp += j;
868: } while (amt > 0);
869: amt = BUFSIZ;
870: if (i + amt > size)
871: amt = size - i;
872: if (wrerr == 0 && write(f, buf, amt) != amt) {
873: olderrno = errno;
874: wrerr++;
875: }
876: }
877: if (response() < 0) {
878: err();
879: goto badnew2;
880: }
881: if (wrerr)
882: goto badnew1;
883: if (opts & COMPARE) {
884: FILE *f1, *f2;
885: int c;
886:
887: if ((f1 = fopen(target, "r")) == NULL)
888: goto badtarget;
889: if ((f2 = fopen(new, "r")) == NULL) {
890: badnew1: error("%s:%s: %s\n", host, new, strerror(errno));
891: goto badnew2;
892: }
893: while ((c = getc(f1)) == getc(f2))
894: if (c == EOF) {
895: (void) fclose(f1);
896: (void) fclose(f2);
897: ack();
898: goto badnew2;
899: }
900: (void) fclose(f1);
901: (void) fclose(f2);
902: if (opts & VERIFY) {
903: differ: buf[0] = '\0';
904: (void) sprintf(buf + 1, "need to update: %s\n",target);
905: (void) write(rem, buf, strlen(buf + 1) + 1);
906: goto badnew2;
907: }
908: }
909:
910: /*
911: * Set last modified time
912: */
913: tvp[0].tv_sec = time(0);
914: tvp[0].tv_usec = 0;
915: tvp[1].tv_sec = mtime;
916: tvp[1].tv_usec = 0;
917: if (utimes(new, tvp) < 0)
918: note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
919:
920: if (fchog(f, new, owner, group, mode) < 0) {
1.2 ! deraadt 921: badnew2:
! 922: if (f) /* Don't close if f hasn't been opened. */
! 923: (void) close(f);
1.1 deraadt 924: (void) unlink(new);
925: return;
926: }
927: (void) close(f);
928:
929: fixup: if (rename(new, target) < 0) {
930: badtarget: error("%s:%s: %s\n", host, target, strerror(errno));
931: (void) unlink(new);
932: return;
933: }
934:
935: if (opts & COMPARE) {
936: buf[0] = '\0';
937: (void) sprintf(buf + 1, "updated %s\n", target);
938: (void) write(rem, buf, strlen(buf + 1) + 1);
939: } else
940: ack();
941: }
942:
943: /*
944: * Creat a hard link to existing file.
945: */
946: static void
947: hardlink(cmd)
948: char *cmd;
949: {
950: register char *cp;
951: struct stat stb;
952: char *oldname;
953: int opts, exists = 0;
954:
955: cp = cmd;
956: opts = 0;
957: while (*cp >= '0' && *cp <= '7')
958: opts = (opts << 3) | (*cp++ - '0');
959: if (*cp++ != ' ') {
960: error("hardlink: options not delimited\n");
961: return;
962: }
963: oldname = cp;
964: while (*cp && *cp != ' ')
965: cp++;
966: if (*cp != ' ') {
967: error("hardlink: oldname name not delimited\n");
968: return;
969: }
970: *cp++ = '\0';
971:
972: if (catname) {
973: (void) sprintf(tp, "/%s", cp);
974: }
975: if (lstat(target, &stb) == 0) {
976: int mode = stb.st_mode & S_IFMT;
977: if (mode != S_IFREG && mode != S_IFLNK) {
978: error("%s:%s: not a regular file\n", host, target);
979: return;
980: }
981: exists = 1;
982: }
983: if (chkparent(target) < 0 ) {
984: error("%s:%s: %s (no parent)\n",
985: host, target, strerror(errno));
986: return;
987: }
988: if (exists && (unlink(target) < 0)) {
989: error("%s:%s: %s (unlink)\n",
990: host, target, strerror(errno));
991: return;
992: }
993: if (link(oldname, target) < 0) {
994: error("%s:can't link %s to %s\n",
995: host, target, oldname);
996: return;
997: }
998: ack();
999: }
1000:
1001: /*
1002: * Check to see if parent directory exists and create one if not.
1003: */
1004: static int
1005: chkparent(name)
1006: char *name;
1007: {
1008: register char *cp;
1009: struct stat stb;
1010:
1011: cp = rindex(name, '/');
1012: if (cp == NULL || cp == name)
1013: return(0);
1014: *cp = '\0';
1015: if (lstat(name, &stb) < 0) {
1016: if (errno == ENOENT && chkparent(name) >= 0 &&
1017: mkdir(name, 0777 & ~oumask) >= 0) {
1018: *cp = '/';
1019: return(0);
1020: }
1021: } else if (ISDIR(stb.st_mode)) {
1022: *cp = '/';
1023: return(0);
1024: }
1025: *cp = '/';
1026: return(-1);
1027: }
1028:
1029: /*
1030: * Change owner, group and mode of file.
1031: */
1032: static int
1033: fchog(fd, file, owner, group, mode)
1034: int fd;
1035: char *file, *owner, *group;
1036: int mode;
1037: {
1038: register int i;
1039: int uid, gid;
1040: extern char user[];
1041: extern int userid;
1042:
1043: uid = userid;
1044: if (userid == 0) {
1045: if (*owner == ':') {
1046: uid = atoi(owner + 1);
1047: } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
1048: if ((pw = getpwnam(owner)) == NULL) {
1049: if (mode & 04000) {
1050: note("%s:%s: unknown login name, clearing setuid",
1051: host, owner);
1052: mode &= ~04000;
1053: uid = 0;
1054: }
1055: } else
1056: uid = pw->pw_uid;
1057: } else
1058: uid = pw->pw_uid;
1059: if (*group == ':') {
1060: gid = atoi(group + 1);
1061: goto ok;
1062: }
1063: } else if ((mode & 04000) && strcmp(user, owner) != 0)
1064: mode &= ~04000;
1065: gid = -1;
1066: if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
1067: if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
1068: || ((gr = getgrnam(group)) == NULL)) {
1069: if (mode & 02000) {
1070: note("%s:%s: unknown group", host, group);
1071: mode &= ~02000;
1072: }
1073: } else
1074: gid = gr->gr_gid;
1075: } else
1076: gid = gr->gr_gid;
1077: if (userid && gid >= 0) {
1078: if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
1079: if (!(strcmp(user, gr->gr_mem[i])))
1080: goto ok;
1081: mode &= ~02000;
1082: gid = -1;
1083: }
1084: ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
1085: note("%s: %s chown: %s", host, file, strerror(errno));
1086: else if (mode & 07000 &&
1087: (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
1088: note("%s: %s chmod: %s", host, file, strerror(errno));
1089: return(0);
1090: }
1091:
1092: /*
1093: * Check for files on the machine being updated that are not on the master
1094: * machine and remove them.
1095: */
1096: static void
1097: rmchk(opts)
1098: int opts;
1099: {
1100: register char *cp, *s;
1101: struct stat stb;
1102:
1103: if (debug)
1104: printf("rmchk()\n");
1105:
1106: /*
1107: * Tell the remote to clean the files from the last directory sent.
1108: */
1109: (void) sprintf(buf, "C%o\n", opts & VERIFY);
1110: if (debug)
1111: printf("buf = %s", buf);
1112: (void) write(rem, buf, strlen(buf));
1113: if (response() < 0)
1114: return;
1115: for (;;) {
1116: cp = s = buf;
1117: do {
1118: if (read(rem, cp, 1) != 1)
1119: lostconn(0);
1120: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1121:
1122: switch (*s++) {
1123: case 'Q': /* Query if file should be removed */
1124: /*
1125: * Return the following codes to remove query.
1126: * N\n -- file exists - DON'T remove.
1127: * Y\n -- file doesn't exist - REMOVE.
1128: */
1129: *--cp = '\0';
1130: (void) sprintf(tp, "/%s", s);
1131: if (debug)
1132: printf("check %s\n", target);
1133: if (except(target))
1134: (void) write(rem, "N\n", 2);
1135: else if (lstat(target, &stb) < 0)
1136: (void) write(rem, "Y\n", 2);
1137: else
1138: (void) write(rem, "N\n", 2);
1139: break;
1140:
1141: case '\0':
1142: *--cp = '\0';
1143: if (*s != '\0')
1144: log(lfp, "%s\n", s);
1145: break;
1146:
1147: case 'E':
1148: *tp = '\0';
1149: ack();
1150: return;
1151:
1152: case '\1':
1153: case '\2':
1154: nerrs++;
1155: if (*s != '\n') {
1156: if (!iamremote) {
1157: fflush(stdout);
1158: (void) write(2, s, cp - s);
1159: }
1160: if (lfp != NULL)
1161: (void) fwrite(s, 1, cp - s, lfp);
1162: }
1163: if (buf[0] == '\2')
1164: lostconn(0);
1165: break;
1166:
1167: default:
1168: error("rmchk: unexpected response '%s'\n", buf);
1169: err();
1170: }
1171: }
1172: }
1173:
1174: /*
1175: * Check the current directory (initialized by the 'T' command to server())
1176: * for extraneous files and remove them.
1177: */
1178: static void
1179: clean(cp)
1180: register char *cp;
1181: {
1182: DIR *d;
1183: register struct direct *dp;
1184: struct stat stb;
1185: char *otp;
1186: int len, opts;
1187:
1188: opts = 0;
1189: while (*cp >= '0' && *cp <= '7')
1190: opts = (opts << 3) | (*cp++ - '0');
1191: if (*cp != '\0') {
1192: error("clean: options not delimited\n");
1193: return;
1194: }
1195: if ((d = opendir(target)) == NULL) {
1196: error("%s:%s: %s\n", host, target, strerror(errno));
1197: return;
1198: }
1199: ack();
1200:
1201: otp = tp;
1202: len = tp - target;
1203: while (dp = readdir(d)) {
1204: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1205: continue;
1206: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1207: error("%s:%s/%s: Name too long\n",
1208: host, target, dp->d_name);
1209: continue;
1210: }
1211: tp = otp;
1212: *tp++ = '/';
1213: cp = dp->d_name;;
1214: while (*tp++ = *cp++)
1215: ;
1216: tp--;
1217: if (lstat(target, &stb) < 0) {
1218: error("%s:%s: %s\n", host, target, strerror(errno));
1219: continue;
1220: }
1221: (void) sprintf(buf, "Q%s\n", dp->d_name);
1222: (void) write(rem, buf, strlen(buf));
1223: cp = buf;
1224: do {
1225: if (read(rem, cp, 1) != 1)
1226: cleanup(0);
1227: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1228: *--cp = '\0';
1229: cp = buf;
1230: if (*cp != 'Y')
1231: continue;
1232: if (opts & VERIFY) {
1233: cp = buf;
1234: *cp++ = '\0';
1235: (void) sprintf(cp, "need to remove: %s\n", target);
1236: (void) write(rem, buf, strlen(cp) + 1);
1237: } else
1238: removeit(&stb);
1239: }
1240: closedir(d);
1241: (void) write(rem, "E\n", 2);
1242: (void) response();
1243: tp = otp;
1244: *tp = '\0';
1245: }
1246:
1247: /*
1248: * Remove a file or directory (recursively) and send back an acknowledge
1249: * or an error message.
1250: */
1251: static void
1252: removeit(stp)
1253: struct stat *stp;
1254: {
1255: DIR *d;
1256: struct direct *dp;
1257: register char *cp;
1258: struct stat stb;
1259: char *otp;
1260: int len;
1261:
1262: switch (stp->st_mode & S_IFMT) {
1263: case S_IFREG:
1264: case S_IFLNK:
1265: if (unlink(target) < 0)
1266: goto bad;
1267: goto removed;
1268:
1269: case S_IFDIR:
1270: break;
1271:
1272: default:
1273: error("%s:%s: not a plain file\n", host, target);
1274: return;
1275: }
1276:
1277: if ((d = opendir(target)) == NULL)
1278: goto bad;
1279:
1280: otp = tp;
1281: len = tp - target;
1282: while (dp = readdir(d)) {
1283: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1284: continue;
1285: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1286: error("%s:%s/%s: Name too long\n",
1287: host, target, dp->d_name);
1288: continue;
1289: }
1290: tp = otp;
1291: *tp++ = '/';
1292: cp = dp->d_name;;
1293: while (*tp++ = *cp++)
1294: ;
1295: tp--;
1296: if (lstat(target, &stb) < 0) {
1297: error("%s:%s: %s\n", host, target, strerror(errno));
1298: continue;
1299: }
1300: removeit(&stb);
1301: }
1302: closedir(d);
1303: tp = otp;
1304: *tp = '\0';
1305: if (rmdir(target) < 0) {
1306: bad:
1307: error("%s:%s: %s\n", host, target, strerror(errno));
1308: return;
1309: }
1310: removed:
1311: cp = buf;
1312: *cp++ = '\0';
1313: (void) sprintf(cp, "removed %s\n", target);
1314: (void) write(rem, buf, strlen(cp) + 1);
1315: }
1316:
1317: /*
1318: * Execute a shell command to handle special cases.
1319: */
1320: static void
1321: dospecial(cmd)
1322: char *cmd;
1323: {
1324: int fd[2], status, pid, i;
1325: register char *cp, *s;
1326: char sbuf[BUFSIZ];
1327: extern int userid, groupid;
1328:
1329: if (pipe(fd) < 0) {
1330: error("%s\n", strerror(errno));
1331: return;
1332: }
1333: if ((pid = fork()) == 0) {
1334: /*
1335: * Return everything the shell commands print.
1336: */
1337: (void) close(0);
1338: (void) close(1);
1339: (void) close(2);
1340: (void) open(_PATH_DEVNULL, O_RDONLY);
1341: (void) dup(fd[1]);
1342: (void) dup(fd[1]);
1343: (void) close(fd[0]);
1344: (void) close(fd[1]);
1345: setgid(groupid);
1346: setuid(userid);
1347: execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
1348: _exit(127);
1349: }
1350: (void) close(fd[1]);
1351: s = sbuf;
1352: *s++ = '\0';
1353: while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
1354: cp = buf;
1355: do {
1356: *s++ = *cp++;
1357: if (cp[-1] != '\n') {
1358: if (s < &sbuf[sizeof(sbuf)-1])
1359: continue;
1360: *s++ = '\n';
1361: }
1362: /*
1363: * Throw away blank lines.
1364: */
1365: if (s == &sbuf[2]) {
1366: s--;
1367: continue;
1368: }
1369: (void) write(rem, sbuf, s - sbuf);
1370: s = &sbuf[1];
1371: } while (--i);
1372: }
1373: if (s > &sbuf[1]) {
1374: *s++ = '\n';
1375: (void) write(rem, sbuf, s - sbuf);
1376: }
1377: while ((i = wait(&status)) != pid && i != -1)
1378: ;
1379: if (i == -1)
1380: status = -1;
1381: (void) close(fd[0]);
1382: if (status)
1383: error("shell returned %d\n", status);
1384: else
1385: ack();
1386: }
1387:
1388: #if __STDC__
1389: #include <stdarg.h>
1390: #else
1391: #include <varargs.h>
1392: #endif
1393:
1394: void
1395: #if __STDC__
1396: log(FILE *fp, const char *fmt, ...)
1397: #else
1398: log(fp, fmt, va_alist)
1399: FILE *fp;
1400: char *fmt;
1401: va_dcl
1402: #endif
1403: {
1404: va_list ap;
1405: #if __STDC__
1406: va_start(ap, fmt);
1407: #else
1408: va_start(ap);
1409: #endif
1410: /* Print changes locally if not quiet mode */
1411: if (!qflag)
1412: (void)vprintf(fmt, ap);
1413:
1414: /* Save changes (for mailing) if really updating files */
1415: if (!(options & VERIFY) && fp != NULL)
1416: (void)vfprintf(fp, fmt, ap);
1417: va_end(ap);
1418: }
1419:
1420: void
1421: #if __STDC__
1422: error(const char *fmt, ...)
1423: #else
1424: error(fmt, va_alist)
1425: char *fmt;
1426: va_dcl
1427: #endif
1428: {
1429: static FILE *fp;
1430: va_list ap;
1431: #if __STDC__
1432: va_start(ap, fmt);
1433: #else
1434: va_start(ap);
1435: #endif
1436:
1437: ++nerrs;
1438: if (!fp && !(fp = fdopen(rem, "w")))
1439: return;
1440: if (iamremote) {
1441: (void)fprintf(fp, "%crdist: ", 0x01);
1442: (void)vfprintf(fp, fmt, ap);
1443: fflush(fp);
1444: }
1445: else {
1446: fflush(stdout);
1447: (void)fprintf(stderr, "rdist: ");
1448: (void)vfprintf(stderr, fmt, ap);
1449: fflush(stderr);
1450: }
1451: if (lfp != NULL) {
1452: (void)fprintf(lfp, "rdist: ");
1453: (void)vfprintf(lfp, fmt, ap);
1454: fflush(lfp);
1455: }
1456: va_end(ap);
1457: }
1458:
1459: void
1460: #if __STDC__
1461: fatal(const char *fmt, ...)
1462: #else
1463: fatal(fmt, va_alist)
1464: char *fmt;
1465: va_dcl
1466: #endif
1467: {
1468: static FILE *fp;
1469: va_list ap;
1470: #if __STDC__
1471: va_start(ap, fmt);
1472: #else
1473: va_start(ap);
1474: #endif
1475:
1476: ++nerrs;
1477: if (!fp && !(fp = fdopen(rem, "w")))
1478: return;
1479: if (iamremote) {
1480: (void)fprintf(fp, "%crdist: ", 0x02);
1481: (void)vfprintf(fp, fmt, ap);
1482: fflush(fp);
1483: }
1484: else {
1485: fflush(stdout);
1486: (void)fprintf(stderr, "rdist: ");
1487: (void)vfprintf(stderr, fmt, ap);
1488: fflush(stderr);
1489: }
1490: if (lfp != NULL) {
1491: (void)fprintf(lfp, "rdist: ");
1492: (void)vfprintf(lfp, fmt, ap);
1493: fflush(lfp);
1494: }
1495: cleanup(0);
1496: }
1497:
1498: static int
1499: response()
1500: {
1501: char *cp, *s;
1502: char resp[BUFSIZ];
1503:
1504: if (debug)
1505: printf("response()\n");
1506:
1507: cp = s = resp;
1508: do {
1509: if (read(rem, cp, 1) != 1)
1510: lostconn(0);
1511: } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
1512:
1513: switch (*s++) {
1514: case '\0':
1515: *--cp = '\0';
1516: if (*s != '\0') {
1517: log(lfp, "%s\n", s);
1518: return(1);
1519: }
1520: return(0);
1521: case '\3':
1522: *--cp = '\0';
1523: log(lfp, "Note: %s\n",s);
1524: return(response());
1525:
1526: default:
1527: s--;
1528: /* fall into... */
1529: case '\1':
1530: case '\2':
1531: nerrs++;
1532: if (*s != '\n') {
1533: if (!iamremote) {
1534: fflush(stdout);
1535: (void) write(2, s, cp - s);
1536: }
1537: if (lfp != NULL)
1538: (void) fwrite(s, 1, cp - s, lfp);
1539: }
1540: if (resp[0] == '\2')
1541: lostconn(0);
1542: return(-1);
1543: }
1544: }
1545:
1546: /*
1547: * Remove temporary files and do any cleanup operations before exiting.
1548: */
1549: void
1550: cleanup(signo)
1551: int signo;
1552: {
1553: (void) unlink(tempfile);
1554: exit(1);
1555: }
1556:
1557: static void
1558: #if __STDC__
1559: note(const char *fmt, ...)
1560: #else
1561: note(fmt, va_alist)
1562: char *fmt;
1563: va_dcl
1564: #endif
1565: {
1566: static char buf[BUFSIZ];
1567: va_list ap;
1568: #if __STDC__
1569: va_start(ap, fmt);
1570: #else
1571: va_start(ap);
1572: #endif
1573: (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1574: va_end(ap);
1575: comment(buf);
1576: }
1577:
1578: static void
1579: comment(s)
1580: char *s;
1581: {
1582: char c;
1583:
1584: c = '\3';
1585: write(rem, &c, 1);
1586: write(rem, s, strlen(s));
1587: c = '\n';
1588: write(rem, &c, 1);
1589: }