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