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