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