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