Annotation of src/usr.bin/oldrdist/server.c, Revision 1.2
1.1 dm 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 1996/02/03 12:12:03 dm Exp $";
1.1 dm 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: {
1.2 ! deraadt 695: register char *cp = cmd;
! 696: int f = -1, mode, opts = 0, wrerr, olderrno;
1.1 dm 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;
707: f = 0; /* Initialize, so for links it remains 0. */
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) {
921: badnew2:
1.2 ! deraadt 922: if (f == -1)
1.1 dm 923: (void) close(f);
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: while (*cp >= '0' && *cp <= '7')
956: opts = (opts << 3) | (*cp++ - '0');
957: if (*cp++ != ' ') {
958: error("hardlink: options not delimited\n");
959: return;
960: }
961: oldname = cp;
962: while (*cp && *cp != ' ')
963: cp++;
964: if (*cp != ' ') {
965: error("hardlink: oldname name not delimited\n");
966: return;
967: }
968: *cp++ = '\0';
969:
970: if (catname) {
971: (void) sprintf(tp, "/%s", cp);
972: }
973: if (lstat(target, &stb) == 0) {
974: int mode = stb.st_mode & S_IFMT;
975: if (mode != S_IFREG && mode != S_IFLNK) {
976: error("%s:%s: not a regular file\n", host, target);
977: return;
978: }
979: exists = 1;
980: }
981: if (chkparent(target) < 0 ) {
982: error("%s:%s: %s (no parent)\n",
983: host, target, strerror(errno));
984: return;
985: }
986: if (exists && (unlink(target) < 0)) {
987: error("%s:%s: %s (unlink)\n",
988: host, target, strerror(errno));
989: return;
990: }
991: if (link(oldname, target) < 0) {
992: error("%s:can't link %s to %s\n",
993: host, target, oldname);
994: return;
995: }
996: ack();
997: }
998:
999: /*
1000: * Check to see if parent directory exists and create one if not.
1001: */
1002: static int
1003: chkparent(name)
1004: char *name;
1005: {
1006: register char *cp;
1007: struct stat stb;
1008:
1009: cp = rindex(name, '/');
1010: if (cp == NULL || cp == name)
1011: return(0);
1012: *cp = '\0';
1013: if (lstat(name, &stb) < 0) {
1014: if (errno == ENOENT && chkparent(name) >= 0 &&
1015: mkdir(name, 0777 & ~oumask) >= 0) {
1016: *cp = '/';
1017: return(0);
1018: }
1019: } else if (ISDIR(stb.st_mode)) {
1020: *cp = '/';
1021: return(0);
1022: }
1023: *cp = '/';
1024: return(-1);
1025: }
1026:
1027: /*
1028: * Change owner, group and mode of file.
1029: */
1030: static int
1031: fchog(fd, file, owner, group, mode)
1032: int fd;
1033: char *file, *owner, *group;
1034: int mode;
1035: {
1036: register int i;
1037: int uid, gid;
1038: extern char user[];
1039: extern int userid;
1040:
1041: uid = userid;
1042: if (userid == 0) {
1043: if (*owner == ':') {
1044: uid = atoi(owner + 1);
1045: } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
1046: if ((pw = getpwnam(owner)) == NULL) {
1047: if (mode & 04000) {
1048: note("%s:%s: unknown login name, clearing setuid",
1049: host, owner);
1050: mode &= ~04000;
1051: uid = 0;
1052: }
1053: } else
1054: uid = pw->pw_uid;
1055: } else
1056: uid = pw->pw_uid;
1057: if (*group == ':') {
1058: gid = atoi(group + 1);
1059: goto ok;
1060: }
1061: } else if ((mode & 04000) && strcmp(user, owner) != 0)
1062: mode &= ~04000;
1063: gid = -1;
1064: if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
1065: if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
1066: || ((gr = getgrnam(group)) == NULL)) {
1067: if (mode & 02000) {
1068: note("%s:%s: unknown group", host, group);
1069: mode &= ~02000;
1070: }
1071: } else
1072: gid = gr->gr_gid;
1073: } else
1074: gid = gr->gr_gid;
1075: if (userid && gid >= 0) {
1076: if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
1077: if (!(strcmp(user, gr->gr_mem[i])))
1078: goto ok;
1079: mode &= ~02000;
1080: gid = -1;
1081: }
1082: ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
1083: note("%s: %s chown: %s", host, file, strerror(errno));
1084: else if (mode & 07000 &&
1085: (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
1086: note("%s: %s chmod: %s", host, file, strerror(errno));
1087: return(0);
1088: }
1089:
1090: /*
1091: * Check for files on the machine being updated that are not on the master
1092: * machine and remove them.
1093: */
1094: static void
1095: rmchk(opts)
1096: int opts;
1097: {
1098: register char *cp, *s;
1099: struct stat stb;
1100:
1101: if (debug)
1102: printf("rmchk()\n");
1103:
1104: /*
1105: * Tell the remote to clean the files from the last directory sent.
1106: */
1107: (void) sprintf(buf, "C%o\n", opts & VERIFY);
1108: if (debug)
1109: printf("buf = %s", buf);
1110: (void) write(rem, buf, strlen(buf));
1111: if (response() < 0)
1112: return;
1113: for (;;) {
1114: cp = s = buf;
1115: do {
1116: if (read(rem, cp, 1) != 1)
1117: lostconn(0);
1118: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1119:
1120: switch (*s++) {
1121: case 'Q': /* Query if file should be removed */
1122: /*
1123: * Return the following codes to remove query.
1124: * N\n -- file exists - DON'T remove.
1125: * Y\n -- file doesn't exist - REMOVE.
1126: */
1127: *--cp = '\0';
1128: (void) sprintf(tp, "/%s", s);
1129: if (debug)
1130: printf("check %s\n", target);
1131: if (except(target))
1132: (void) write(rem, "N\n", 2);
1133: else if (lstat(target, &stb) < 0)
1134: (void) write(rem, "Y\n", 2);
1135: else
1136: (void) write(rem, "N\n", 2);
1137: break;
1138:
1139: case '\0':
1140: *--cp = '\0';
1141: if (*s != '\0')
1142: log(lfp, "%s\n", s);
1143: break;
1144:
1145: case 'E':
1146: *tp = '\0';
1147: ack();
1148: return;
1149:
1150: case '\1':
1151: case '\2':
1152: nerrs++;
1153: if (*s != '\n') {
1154: if (!iamremote) {
1155: fflush(stdout);
1156: (void) write(2, s, cp - s);
1157: }
1158: if (lfp != NULL)
1159: (void) fwrite(s, 1, cp - s, lfp);
1160: }
1161: if (buf[0] == '\2')
1162: lostconn(0);
1163: break;
1164:
1165: default:
1166: error("rmchk: unexpected response '%s'\n", buf);
1167: err();
1168: }
1169: }
1170: }
1171:
1172: /*
1173: * Check the current directory (initialized by the 'T' command to server())
1174: * for extraneous files and remove them.
1175: */
1176: static void
1177: clean(cp)
1178: register char *cp;
1179: {
1180: DIR *d;
1181: register struct direct *dp;
1182: struct stat stb;
1183: char *otp;
1184: int len, opts;
1185:
1186: opts = 0;
1187: while (*cp >= '0' && *cp <= '7')
1188: opts = (opts << 3) | (*cp++ - '0');
1189: if (*cp != '\0') {
1190: error("clean: options not delimited\n");
1191: return;
1192: }
1193: if ((d = opendir(target)) == NULL) {
1194: error("%s:%s: %s\n", host, target, strerror(errno));
1195: return;
1196: }
1197: ack();
1198:
1199: otp = tp;
1200: len = tp - target;
1201: while (dp = readdir(d)) {
1202: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1203: continue;
1204: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1205: error("%s:%s/%s: Name too long\n",
1206: host, target, dp->d_name);
1207: continue;
1208: }
1209: tp = otp;
1210: *tp++ = '/';
1211: cp = dp->d_name;;
1212: while (*tp++ = *cp++)
1213: ;
1214: tp--;
1215: if (lstat(target, &stb) < 0) {
1216: error("%s:%s: %s\n", host, target, strerror(errno));
1217: continue;
1218: }
1219: (void) sprintf(buf, "Q%s\n", dp->d_name);
1220: (void) write(rem, buf, strlen(buf));
1221: cp = buf;
1222: do {
1223: if (read(rem, cp, 1) != 1)
1224: cleanup(0);
1225: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1226: *--cp = '\0';
1227: cp = buf;
1228: if (*cp != 'Y')
1229: continue;
1230: if (opts & VERIFY) {
1231: cp = buf;
1232: *cp++ = '\0';
1233: (void) sprintf(cp, "need to remove: %s\n", target);
1234: (void) write(rem, buf, strlen(cp) + 1);
1235: } else
1236: removeit(&stb);
1237: }
1238: closedir(d);
1239: (void) write(rem, "E\n", 2);
1240: (void) response();
1241: tp = otp;
1242: *tp = '\0';
1243: }
1244:
1245: /*
1246: * Remove a file or directory (recursively) and send back an acknowledge
1247: * or an error message.
1248: */
1249: static void
1250: removeit(stp)
1251: struct stat *stp;
1252: {
1253: DIR *d;
1254: struct direct *dp;
1255: register char *cp;
1256: struct stat stb;
1257: char *otp;
1258: int len;
1259:
1260: switch (stp->st_mode & S_IFMT) {
1261: case S_IFREG:
1262: case S_IFLNK:
1263: if (unlink(target) < 0)
1264: goto bad;
1265: goto removed;
1266:
1267: case S_IFDIR:
1268: break;
1269:
1270: default:
1271: error("%s:%s: not a plain file\n", host, target);
1272: return;
1273: }
1274:
1275: if ((d = opendir(target)) == NULL)
1276: goto bad;
1277:
1278: otp = tp;
1279: len = tp - target;
1280: while (dp = readdir(d)) {
1281: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1282: continue;
1283: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1284: error("%s:%s/%s: Name too long\n",
1285: host, target, dp->d_name);
1286: continue;
1287: }
1288: tp = otp;
1289: *tp++ = '/';
1290: cp = dp->d_name;;
1291: while (*tp++ = *cp++)
1292: ;
1293: tp--;
1294: if (lstat(target, &stb) < 0) {
1295: error("%s:%s: %s\n", host, target, strerror(errno));
1296: continue;
1297: }
1298: removeit(&stb);
1299: }
1300: closedir(d);
1301: tp = otp;
1302: *tp = '\0';
1303: if (rmdir(target) < 0) {
1304: bad:
1305: error("%s:%s: %s\n", host, target, strerror(errno));
1306: return;
1307: }
1308: removed:
1309: cp = buf;
1310: *cp++ = '\0';
1311: (void) sprintf(cp, "removed %s\n", target);
1312: (void) write(rem, buf, strlen(cp) + 1);
1313: }
1314:
1315: /*
1316: * Execute a shell command to handle special cases.
1317: */
1318: static void
1319: dospecial(cmd)
1320: char *cmd;
1321: {
1322: int fd[2], status, pid, i;
1323: register char *cp, *s;
1324: char sbuf[BUFSIZ];
1325: extern int userid, groupid;
1326:
1327: if (pipe(fd) < 0) {
1328: error("%s\n", strerror(errno));
1329: return;
1330: }
1331: if ((pid = fork()) == 0) {
1332: /*
1333: * Return everything the shell commands print.
1334: */
1335: (void) close(0);
1336: (void) close(1);
1337: (void) close(2);
1338: (void) open(_PATH_DEVNULL, O_RDONLY);
1339: (void) dup(fd[1]);
1340: (void) dup(fd[1]);
1341: (void) close(fd[0]);
1342: (void) close(fd[1]);
1343: setgid(groupid);
1344: setuid(userid);
1345: execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
1346: _exit(127);
1347: }
1348: (void) close(fd[1]);
1349: s = sbuf;
1350: *s++ = '\0';
1351: while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
1352: cp = buf;
1353: do {
1354: *s++ = *cp++;
1355: if (cp[-1] != '\n') {
1356: if (s < &sbuf[sizeof(sbuf)-1])
1357: continue;
1358: *s++ = '\n';
1359: }
1360: /*
1361: * Throw away blank lines.
1362: */
1363: if (s == &sbuf[2]) {
1364: s--;
1365: continue;
1366: }
1367: (void) write(rem, sbuf, s - sbuf);
1368: s = &sbuf[1];
1369: } while (--i);
1370: }
1371: if (s > &sbuf[1]) {
1372: *s++ = '\n';
1373: (void) write(rem, sbuf, s - sbuf);
1374: }
1375: while ((i = wait(&status)) != pid && i != -1)
1376: ;
1377: if (i == -1)
1378: status = -1;
1379: (void) close(fd[0]);
1380: if (status)
1381: error("shell returned %d\n", status);
1382: else
1383: ack();
1384: }
1385:
1386: #if __STDC__
1387: #include <stdarg.h>
1388: #else
1389: #include <varargs.h>
1390: #endif
1391:
1392: void
1393: #if __STDC__
1394: log(FILE *fp, const char *fmt, ...)
1395: #else
1396: log(fp, fmt, va_alist)
1397: FILE *fp;
1398: char *fmt;
1399: va_dcl
1400: #endif
1401: {
1402: va_list ap;
1403: #if __STDC__
1404: va_start(ap, fmt);
1405: #else
1406: va_start(ap);
1407: #endif
1408: /* Print changes locally if not quiet mode */
1409: if (!qflag)
1410: (void)vprintf(fmt, ap);
1411:
1412: /* Save changes (for mailing) if really updating files */
1413: if (!(options & VERIFY) && fp != NULL)
1414: (void)vfprintf(fp, fmt, ap);
1415: va_end(ap);
1416: }
1417:
1418: void
1419: #if __STDC__
1420: error(const char *fmt, ...)
1421: #else
1422: error(fmt, va_alist)
1423: char *fmt;
1424: va_dcl
1425: #endif
1426: {
1427: static FILE *fp;
1428: va_list ap;
1429: #if __STDC__
1430: va_start(ap, fmt);
1431: #else
1432: va_start(ap);
1433: #endif
1434:
1435: ++nerrs;
1436: if (!fp && !(fp = fdopen(rem, "w")))
1437: return;
1438: if (iamremote) {
1439: (void)fprintf(fp, "%crdist: ", 0x01);
1440: (void)vfprintf(fp, fmt, ap);
1441: fflush(fp);
1442: }
1443: else {
1444: fflush(stdout);
1445: (void)fprintf(stderr, "rdist: ");
1446: (void)vfprintf(stderr, fmt, ap);
1447: fflush(stderr);
1448: }
1449: if (lfp != NULL) {
1450: (void)fprintf(lfp, "rdist: ");
1451: (void)vfprintf(lfp, fmt, ap);
1452: fflush(lfp);
1453: }
1454: va_end(ap);
1455: }
1456:
1457: void
1458: #if __STDC__
1459: fatal(const char *fmt, ...)
1460: #else
1461: fatal(fmt, va_alist)
1462: char *fmt;
1463: va_dcl
1464: #endif
1465: {
1466: static FILE *fp;
1467: va_list ap;
1468: #if __STDC__
1469: va_start(ap, fmt);
1470: #else
1471: va_start(ap);
1472: #endif
1473:
1474: ++nerrs;
1475: if (!fp && !(fp = fdopen(rem, "w")))
1476: return;
1477: if (iamremote) {
1478: (void)fprintf(fp, "%crdist: ", 0x02);
1479: (void)vfprintf(fp, fmt, ap);
1480: fflush(fp);
1481: }
1482: else {
1483: fflush(stdout);
1484: (void)fprintf(stderr, "rdist: ");
1485: (void)vfprintf(stderr, fmt, ap);
1486: fflush(stderr);
1487: }
1488: if (lfp != NULL) {
1489: (void)fprintf(lfp, "rdist: ");
1490: (void)vfprintf(lfp, fmt, ap);
1491: fflush(lfp);
1492: }
1493: cleanup(0);
1494: }
1495:
1496: static int
1497: response()
1498: {
1499: char *cp, *s;
1500: char resp[BUFSIZ];
1501:
1502: if (debug)
1503: printf("response()\n");
1504:
1505: cp = s = resp;
1506: do {
1507: if (read(rem, cp, 1) != 1)
1508: lostconn(0);
1509: } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
1510:
1511: switch (*s++) {
1512: case '\0':
1513: *--cp = '\0';
1514: if (*s != '\0') {
1515: log(lfp, "%s\n", s);
1516: return(1);
1517: }
1518: return(0);
1519: case '\3':
1520: *--cp = '\0';
1521: log(lfp, "Note: %s\n",s);
1522: return(response());
1523:
1524: default:
1525: s--;
1526: /* fall into... */
1527: case '\1':
1528: case '\2':
1529: nerrs++;
1530: if (*s != '\n') {
1531: if (!iamremote) {
1532: fflush(stdout);
1533: (void) write(2, s, cp - s);
1534: }
1535: if (lfp != NULL)
1536: (void) fwrite(s, 1, cp - s, lfp);
1537: }
1538: if (resp[0] == '\2')
1539: lostconn(0);
1540: return(-1);
1541: }
1542: }
1543:
1544: /*
1545: * Remove temporary files and do any cleanup operations before exiting.
1546: */
1547: void
1548: cleanup(signo)
1549: int signo;
1550: {
1551: (void) unlink(tempfile);
1552: exit(1);
1553: }
1554:
1555: static void
1556: #if __STDC__
1557: note(const char *fmt, ...)
1558: #else
1559: note(fmt, va_alist)
1560: char *fmt;
1561: va_dcl
1562: #endif
1563: {
1564: static char buf[BUFSIZ];
1565: va_list ap;
1566: #if __STDC__
1567: va_start(ap, fmt);
1568: #else
1569: va_start(ap);
1570: #endif
1571: (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1572: va_end(ap);
1573: comment(buf);
1574: }
1575:
1576: static void
1577: comment(s)
1578: char *s;
1579: {
1580: char c;
1581:
1582: c = '\3';
1583: write(rem, &c, 1);
1584: write(rem, s, strlen(s));
1585: c = '\n';
1586: write(rem, &c, 1);
1587: }