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