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