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