8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / rdist / server.c
blob88fac286eb98b14b8045092479535c457ca22c21
1 /*
2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * Copyright (c) 1983 Regents of the University of California.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by the University of California, Berkeley. The name of the
16 * University may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
20 #include "defs.h"
21 #include <signal.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <ctype.h>
26 #include <krb5defs.h>
29 * If we want to write *to* the client rdist program, *from* the server
30 * side (server-side child `rdist -Server' process exec'ed off of in.rshd),
31 * we write to stdout/stderr, since there is a pipe connecting stdout/stderr
32 * to the outside world (which is why we use `wrem' and not `rem').
34 int wrem = 1;
36 #define ack() (void) write(wrem, "\0\n", 2)
37 #define err() (void) write(wrem, "\1\n", 2)
40 * Set when a desread() is reqd. in response()
43 struct linkbuf *ihead; /* list of files with more than one link */
44 char buf[RDIST_BUFSIZ]; /* general purpose buffer */
45 char source[RDIST_BUFSIZ]; /* base source directory name */
46 char destination[RDIST_BUFSIZ]; /* base destination directory name */
47 char target[RDIST_BUFSIZ]; /* target/source directory name */
48 char *tp; /* pointer to end of target name */
49 char *Tdest; /* pointer to last T dest */
50 int catname; /* cat name to target name */
51 char *stp[32]; /* stack of saved tp's for directories */
52 int oumask; /* old umask for creating files */
54 extern FILE *lfp; /* log file for mailing changes */
56 void cleanup();
57 struct linkbuf *savelink();
58 char *strsub();
60 static void comment(char *s);
61 static void note();
62 static void hardlink(char *cmd);
63 void error();
64 void log();
65 static void recursive_remove(struct stat *stp);
66 static void recvf(char *cmd, int type);
67 static void query(char *name);
68 static void sendf(char *rname, int opts);
69 static void rmchk(int opts);
70 static void dospecial(char *cmd);
71 static void clean(char *cp);
74 * Server routine to read requests and process them.
75 * Commands are:
76 * Tname - Transmit file if out of date
77 * Vname - Verify if file out of date or not
78 * Qname - Query if file exists. Return mtime & size if it does.
80 void
81 server()
83 char cmdbuf[RDIST_BUFSIZ];
84 register char *cp;
86 signal(SIGHUP, cleanup);
87 signal(SIGINT, cleanup);
88 signal(SIGQUIT, cleanup);
89 signal(SIGTERM, cleanup);
90 signal(SIGPIPE, cleanup);
92 rem = 0;
93 oumask = umask(0);
95 (void) sprintf(buf, "V%d\n", VERSION);
96 (void) write(wrem, buf, strlen(buf));
98 for (;;) {
99 cp = cmdbuf;
100 if (read(rem, cp, 1) <= 0)
101 return;
102 if (*cp++ == '\n') {
103 error("server: expected control record\n");
104 continue;
106 do {
107 if (read(rem, cp, 1) != 1)
108 cleanup();
109 } while (*cp++ != '\n' && cp < &cmdbuf[RDIST_BUFSIZ]);
110 *--cp = '\0';
111 cp = cmdbuf;
112 switch (*cp++) {
113 case 'T': /* init target file/directory name */
114 catname = 1; /* target should be directory */
115 goto dotarget;
117 case 't': /* init target file/directory name */
118 catname = 0;
119 dotarget:
120 if (exptilde(target, sizeof (target), cp) == NULL)
121 continue;
122 tp = target;
123 while (*tp)
124 tp++;
125 ack();
126 continue;
128 case 'R': /* Transfer a regular file. */
129 recvf(cp, S_IFREG);
130 continue;
132 case 'D': /* Transfer a directory. */
133 recvf(cp, S_IFDIR);
134 continue;
136 case 'K': /* Transfer symbolic link. */
137 recvf(cp, S_IFLNK);
138 continue;
140 case 'k': /* Transfer hard link. */
141 hardlink(cp);
142 continue;
144 case 'E': /* End. (of directory) */
145 *tp = '\0';
146 if (catname <= 0) {
147 error("server: too many 'E's\n");
148 continue;
150 tp = stp[--catname];
151 *tp = '\0';
152 ack();
153 continue;
155 case 'C': /* Clean. Cleanup a directory */
156 clean(cp);
157 continue;
159 case 'Q': /* Query. Does the file/directory exist? */
160 query(cp);
161 continue;
163 case 'S': /* Special. Execute commands */
164 dospecial(cp);
165 continue;
167 #ifdef notdef
169 * These entries are reserved but not currently used.
170 * The intent is to allow remote hosts to have master copies.
171 * Currently, only the host rdist runs on can have masters.
173 case 'X': /* start a new list of files to exclude */
174 except = bp = NULL;
175 case 'x': /* add name to list of files to exclude */
176 if (*cp == '\0') {
177 ack();
178 continue;
180 if (*cp == '~') {
181 if (exptilde(buf, sizeof (buf), cp) == NULL)
182 continue;
183 cp = buf;
185 if (bp == NULL)
186 except = bp = expand(makeblock(NAME, cp),
187 E_VARS);
188 else
189 bp->b_next = expand(makeblock(NAME, cp),
190 E_VARS);
191 while (bp->b_next != NULL)
192 bp = bp->b_next;
193 ack();
194 continue;
196 case 'I': /* Install. Transfer file if out of date. */
197 opts = 0;
198 while (*cp >= '0' && *cp <= '7')
199 opts = (opts << 3) | (*cp++ - '0');
200 if (*cp++ != ' ') {
201 error("server: options not delimited\n");
202 return;
204 install(cp, opts);
205 continue;
207 case 'L': /* Log. save message in log file */
208 log(lfp, cp);
209 continue;
210 #endif
212 case '\1':
213 nerrs++;
214 continue;
216 case '\2':
217 return;
219 default:
220 error("server: unknown command '%s'\n", cp);
221 case '\0':
222 continue;
228 * Update the file(s) if they are different.
229 * destdir = 1 if destination should be a directory
230 * (i.e., more than one source is being copied to the same destination).
232 void
233 install(src, dest, destdir, opts)
234 char *src, *dest;
235 int destdir, opts;
237 char *rname;
238 char destcopy[RDIST_BUFSIZ];
240 if (dest == NULL) {
241 opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
242 dest = src;
245 if (nflag || debug) {
246 printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
247 opts & WHOLE ? " -w" : "",
248 opts & YOUNGER ? " -y" : "",
249 opts & COMPARE ? " -b" : "",
250 opts & REMOVE ? " -R" : "", src, dest);
251 if (nflag)
252 return;
255 rname = exptilde(target, sizeof (target), src);
256 if (rname == NULL)
257 return;
258 tp = target;
259 while (*tp)
260 tp++;
262 * If we are renaming a directory and we want to preserve
263 * the directory heirarchy (-w), we must strip off the leading
264 * directory name and preserve the rest.
266 if (opts & WHOLE) {
267 while (*rname == '/')
268 rname++;
269 destdir = 1;
270 } else {
271 rname = rindex(target, '/');
272 if (rname == NULL)
273 rname = target;
274 else
275 rname++;
277 if (debug)
278 printf("target = %s, rname = %s\n", target, rname);
280 * Pass the destination file/directory name to remote.
282 if (snprintf(buf, sizeof (buf), "%c%s\n", destdir ? 'T' : 't', dest) >=
283 sizeof (buf)) {
284 error("%s: Name too long\n", dest);
285 return;
287 if (debug)
288 printf("buf = %s", buf);
289 (void) deswrite(rem, buf, strlen(buf), 0);
291 if (response() < 0)
292 return;
294 strcpy(source, src);
295 if (destdir) {
296 strcpy(destcopy, dest);
297 Tdest = destcopy;
298 strcpy(destination, rname);
299 } else {
300 strcpy(destination, dest);
302 sendf(rname, opts);
303 Tdest = 0;
306 #define protoname() (pw ? pw->pw_name : user)
307 #define protogroup() (gr ? gr->gr_name : group)
309 * Transfer the file or directory in target[].
310 * rname is the name of the file on the remote host.
312 void
313 sendf(rname, opts)
314 char *rname;
315 int opts;
317 register struct subcmd *sc;
318 struct stat stb;
319 int sizerr, f, u, len;
320 off_t i;
321 DIR *d;
322 struct dirent *dp;
323 char *otp, *cp;
324 extern struct subcmd *subcmds;
325 static char user[15], group[15];
327 if (debug)
328 printf("sendf(%s, %x%s)\n", rname, opts, printb(opts, OBITS));
330 if (except(target))
331 return;
332 if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
333 error("%s: %s\n", target, strerror(errno));
334 return;
336 if (index(rname, '\n')) {
337 error("file name '%s' contains an embedded newline - "
338 "can't update\n", rname);
339 return;
341 if ((u = update(rname, opts, &stb)) == 0) {
342 if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
343 (void) savelink(&stb, opts);
344 return;
347 if (pw == NULL || pw->pw_uid != stb.st_uid)
348 if ((pw = getpwuid(stb.st_uid)) == NULL) {
349 log(lfp, "%s: no password entry for uid %d \n",
350 target, stb.st_uid);
351 pw = NULL;
352 sprintf(user, ":%d", stb.st_uid);
354 if (gr == NULL || gr->gr_gid != stb.st_gid)
355 if ((gr = getgrgid(stb.st_gid)) == NULL) {
356 log(lfp, "%s: no name for group %d\n",
357 target, stb.st_gid);
358 gr = NULL;
359 sprintf(group, ":%d", stb.st_gid);
361 if (u == 1) {
362 if (opts & VERIFY) {
363 log(lfp, "need to install: %s\n", target);
364 goto dospecial;
366 log(lfp, "installing: %s\n", target);
367 opts &= ~(COMPARE|REMOVE);
370 switch (stb.st_mode & S_IFMT) {
371 case S_IFDIR:
372 if ((d = opendir(target)) == NULL) {
373 error("%s: %s\n", target, strerror(errno));
374 return;
376 if (snprintf(buf, sizeof (buf), "D%o %04o 0 0 %s %s %s\n",
377 opts, stb.st_mode & 07777, protoname(), protogroup(),
378 rname) >= sizeof (buf)) {
379 error("%s: Name too long\n", rname);
380 closedir(d);
381 return;
383 if (debug)
384 printf("buf = %s", buf);
385 (void) deswrite(rem, buf, strlen(buf), 0);
386 if (response() < 0) {
387 closedir(d);
388 return;
391 if (opts & REMOVE)
392 rmchk(opts);
394 otp = tp;
395 len = tp - target;
396 while (dp = readdir(d)) {
397 if ((strcmp(dp->d_name, ".") == 0)||
398 (strcmp(dp->d_name, "..") == 0))
399 continue;
400 if ((int)(len + 1 + strlen(dp->d_name)) >=
401 (int)(RDIST_BUFSIZ - 1)) {
402 error("%.*s/%s: Name too long\n", len, target,
403 dp->d_name);
404 continue;
406 tp = otp;
407 *tp++ = '/';
408 cp = dp->d_name;
409 while (*tp++ = *cp++)
411 tp--;
412 sendf(dp->d_name, opts);
414 closedir(d);
415 (void) deswrite(rem, "E\n", 2, 0);
416 (void) response();
417 tp = otp;
418 *tp = '\0';
419 return;
421 case S_IFLNK:
422 if (u != 1)
423 opts |= COMPARE;
424 if (stb.st_nlink > 1) {
425 struct linkbuf *lp;
427 if ((lp = savelink(&stb, opts)) != NULL) {
428 /* install link */
429 if (*lp->target == 0)
430 len = snprintf(buf, sizeof (buf),
431 "k%o %s %s\n", opts, lp->pathname,
432 rname);
433 else
434 len = snprintf(buf, sizeof (buf),
435 "k%o %s/%s %s\n", opts, lp->target,
436 lp->pathname, rname);
437 if (len >= sizeof (buf)) {
438 error("%s: Name too long\n", rname);
439 return;
441 if (debug)
442 printf("buf = %s", buf);
443 (void) deswrite(rem, buf, strlen(buf), 0);
444 (void) response();
445 return;
448 (void) snprintf(buf, sizeof (buf), "K%o %o %ld %ld %s %s %s\n",
449 opts, stb.st_mode & 07777, stb.st_size, stb.st_mtime,
450 protoname(), protogroup(), rname);
451 if (debug)
452 printf("buf = %s", buf);
453 (void) deswrite(rem, buf, strlen(buf), 0);
454 if (response() < 0)
455 return;
456 sizerr = (readlink(target, buf, RDIST_BUFSIZ) != stb.st_size);
457 (void) deswrite(rem, buf, stb.st_size, 0);
458 if (debug)
459 printf("readlink = %.*s\n", (int)stb.st_size, buf);
460 goto done;
462 case S_IFREG:
463 break;
465 default:
466 error("%s: not a file or directory\n", target);
467 return;
470 if (u == 2) {
471 if (opts & VERIFY) {
472 log(lfp, "need to update: %s\n", target);
473 goto dospecial;
475 log(lfp, "updating: %s\n", target);
478 if (stb.st_nlink > 1) {
479 struct linkbuf *lp;
481 if ((lp = savelink(&stb, opts)) != NULL) {
482 /* install link */
483 if (*lp->target == 0)
484 len = snprintf(buf, sizeof (buf), "k%o %s %s\n",
485 opts, lp->pathname, rname);
486 else
487 len = snprintf(buf, sizeof (buf),
488 "k%o %s/%s %s\n", opts, lp->target,
489 lp->pathname, rname);
490 if (len >= sizeof (buf)) {
491 error("%s: Name too long\n", rname);
492 return;
494 if (debug)
495 printf("buf = %s", buf);
496 (void) deswrite(rem, buf, strlen(buf), 0);
497 (void) response();
498 return;
502 if ((f = open(target, 0)) < 0) {
503 error("%s: %s\n", target, strerror(errno));
504 return;
506 (void) snprintf(buf, sizeof (buf), "R%o %o %ld %ld %s %s %s\n", opts,
507 stb.st_mode & 07777, stb.st_size, stb.st_mtime,
508 protoname(), protogroup(), rname);
509 if (debug)
510 printf("buf = %s", buf);
511 (void) deswrite(rem, buf, strlen(buf), 0);
513 if (response() < 0) {
514 (void) close(f);
515 return;
518 sizerr = 0;
520 for (i = 0; i < stb.st_size; i += RDIST_BUFSIZ) {
521 int amt = RDIST_BUFSIZ;
522 if (i + amt > stb.st_size)
523 amt = stb.st_size - i;
524 if (sizerr == 0 && read(f, buf, amt) != amt)
525 sizerr = 1;
526 (void) deswrite(rem, buf, amt, 0);
528 (void) close(f);
529 done:
530 if (sizerr) {
531 error("%s: file changed size\n", target);
532 (void) deswrite(rem, "\1\n", 2, 0);
533 } else
534 (void) deswrite(rem, "\0\n", 2, 0);
535 f = response();
537 if (f < 0 || f == 0 && (opts & COMPARE))
538 return;
539 dospecial:
540 for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
541 if (sc->sc_type != SPECIAL)
542 continue;
543 if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
544 continue;
545 log(lfp, "special \"%s\"\n", sc->sc_name);
546 if (opts & VERIFY)
547 continue;
548 (void) snprintf(buf, sizeof (buf), "SFILE=%s;%s\n", target,
549 sc->sc_name);
550 if (debug)
551 printf("buf = %s", buf);
552 (void) deswrite(rem, buf, strlen(buf), 0);
553 while (response() > 0)
558 struct linkbuf *
559 savelink(stp, opts)
560 struct stat *stp;
561 int opts;
563 struct linkbuf *lp;
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);
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;
580 if (strlcpy(lp->pathname,
581 opts & WHOLE ? target : strsub(source, destination, target),
582 sizeof (lp->pathname)) >= sizeof (lp->pathname)) {
583 error("%s: target name too long\n", target);
586 if (Tdest) {
587 if (strlcpy(lp->target, Tdest,
588 sizeof (lp->target)) >= sizeof (lp->target))
589 error("%s: target name too long\n", Tdest);
590 } else
591 *lp->target = 0;
593 return (NULL);
597 * Check to see if file needs to be updated on the remote machine.
598 * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
599 * and 3 if comparing binaries to determine if out of date.
602 update(rname, opts, stp)
603 char *rname;
604 int opts;
605 struct stat *stp;
607 register char *cp, *s;
608 register off_t size;
609 register time_t mtime;
611 if (debug)
612 printf("update(%s, %x%s, %x)\n", rname, opts,
613 printb(opts, OBITS), stp);
616 * Check to see if the file exists on the remote machine.
618 if (snprintf(buf, sizeof (buf), "Q%s\n", rname) >= sizeof (buf)) {
619 error("%s: Name too long\n", rname);
620 return (0);
622 if (debug)
623 printf("buf = %s", buf);
624 (void) deswrite(rem, buf, strlen(buf), 0);
625 again:
626 cp = s = buf;
627 more:
628 do {
629 if (desread(rem, cp, 1, 0) != 1)
630 lostconn();
631 } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
633 if (cp < &buf[RDIST_BUFSIZ])
634 *cp = '\0';
635 if (debug) {
636 printf("update reply: ");
637 switch (*s) {
638 case 'Y':
639 case 'N':
640 putchar(*s);
641 break;
642 default:
643 if (iscntrl(*s)) {
644 putchar('^');
645 putchar('A' + *s - 1);
646 } else
647 printf("%#x", *s & 0xff);
648 break;
650 printf("%s", &s[1]);
653 switch (*s++) {
654 case 'Y':
655 break;
657 case 'N': /* file doesn't exist so install it */
658 return (1);
660 case '\1':
661 nerrs++;
662 if (*s != '\n') {
663 if (!iamremote) {
664 fflush(stdout);
665 (void) write(2, s, cp - s);
667 if (lfp != NULL)
668 (void) fwrite(s, 1, cp - s, lfp);
670 if (cp == &buf[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
671 /* preserve status code */
672 cp = s;
673 s = buf;
674 goto more;
676 return (0);
678 case '\3':
679 *--cp = '\0';
680 if (lfp != NULL)
681 log(lfp, "update: note: %s\n", s);
682 goto again;
684 default:
685 *--cp = '\0';
686 error("update: unexpected response '%s'\n", s);
687 return (0);
690 if (*s == '\n')
691 return (2);
693 if (opts & COMPARE)
694 return (3);
696 size = 0;
697 while (isdigit(*s))
698 size = size * 10 + (*s++ - '0');
699 if (*s++ != ' ') {
700 error("update: size not delimited\n");
701 return (0);
703 mtime = 0;
704 while (isdigit(*s))
705 mtime = mtime * 10 + (*s++ - '0');
706 if (*s != '\n') {
707 error("update: mtime not delimited\n");
708 return (0);
711 * File needs to be updated?
713 if (opts & YOUNGER) {
714 if (stp->st_mtime == mtime)
715 return (0);
716 if (stp->st_mtime < mtime) {
717 log(lfp, "Warning: %s: remote copy is newer\n", target);
718 return (0);
720 } else if (stp->st_mtime == mtime && stp->st_size == size)
721 return (0);
722 return (2);
726 * Query. Check to see if file exists. Return one of the following:
727 * N\n - doesn't exist
728 * Ysize mtime\n - exists and its a regular file (size & mtime of file)
729 * Y\n - exists and its a directory or symbolic link
730 * ^Aerror message\n
732 static void
733 query(name)
734 char *name;
736 struct stat stb;
738 if (catname) {
739 if (sizeof (target) - (tp - target) >= strlen(name) + 2) {
740 (void) sprintf(tp, "/%s", name);
741 } else {
742 error("%.*s/%s: Name too long\n", tp - target,
743 target, name);
744 return;
748 if (lstat(target, &stb) < 0) {
749 if (errno == ENOENT)
750 (void) write(wrem, "N\n", 2);
751 else
752 error("%s:%s: %s\n", host, target, strerror(errno));
753 *tp = '\0';
754 return;
757 switch (stb.st_mode & S_IFMT) {
758 case S_IFREG:
759 (void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
760 (void) write(wrem, buf, strlen(buf));
761 break;
763 case S_IFLNK:
764 case S_IFDIR:
765 (void) write(wrem, "Y\n", 2);
766 break;
768 default:
769 error("%s: not a file or directory\n", name);
770 break;
772 *tp = '\0';
775 static void
776 recvf(cmd, type)
777 char *cmd;
778 int type;
780 register char *cp;
781 int f, mode, opts, wrerr, olderrno;
782 off_t i, size;
783 time_t mtime;
784 struct stat stb;
785 struct timeval tvp[2];
786 char *owner, *group;
787 char new[RDIST_BUFSIZ];
788 extern char *tmpname;
790 cp = cmd;
791 opts = 0;
792 while (*cp >= '0' && *cp <= '7')
793 opts = (opts << 3) | (*cp++ - '0');
794 if (*cp++ != ' ') {
795 error("recvf: options not delimited\n");
796 return;
798 mode = 0;
799 while (*cp >= '0' && *cp <= '7')
800 mode = (mode << 3) | (*cp++ - '0');
801 if (*cp++ != ' ') {
802 error("recvf: mode not delimited\n");
803 return;
805 size = 0;
806 while (isdigit(*cp))
807 size = size * 10 + (*cp++ - '0');
808 if (*cp++ != ' ') {
809 error("recvf: size not delimited\n");
810 return;
812 mtime = 0;
813 while (isdigit(*cp))
814 mtime = mtime * 10 + (*cp++ - '0');
815 if (*cp++ != ' ') {
816 error("recvf: mtime not delimited\n");
817 return;
819 owner = cp;
820 while (*cp && *cp != ' ')
821 cp++;
822 if (*cp != ' ') {
823 error("recvf: owner name not delimited\n");
824 return;
826 *cp++ = '\0';
827 group = cp;
828 while (*cp && *cp != ' ')
829 cp++;
830 if (*cp != ' ') {
831 error("recvf: group name not delimited\n");
832 return;
834 *cp++ = '\0';
836 if (type == S_IFDIR) {
837 int isdot;
839 if (strcmp(cp, ".") == 0)
840 isdot = 1;
841 else
842 isdot = 0;
843 if (catname >= sizeof (stp) / sizeof (stp[0])) {
844 error("%s:%s: too many directory levels\n",
845 host, target);
846 return;
848 stp[catname] = tp;
849 if (catname++) {
850 *tp++ = '/';
851 while (*tp++ = *cp++)
853 tp--;
855 if (opts & VERIFY) {
856 ack();
857 return;
859 if (lstat(target, &stb) == 0) {
860 if (ISDIR(stb.st_mode)) {
861 if ((stb.st_mode & 07777) == mode) {
862 ack();
863 return;
865 sendrem("%s: Warning: remote mode %o != "
866 "local mode %o", target,
867 stb.st_mode & 07777, mode);
868 return;
870 errno = ENOTDIR;
871 } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
872 chkparent(target) == 0 &&
873 (isdot == 1 || mkdir(target, mode) == 0))) {
874 if (chog(target, owner, group, mode) == 0)
875 ack();
876 return;
878 error("%s:%s: %s\n", host, target, strerror(errno));
879 tp = stp[--catname];
880 *tp = '\0';
881 return;
884 if (catname) {
885 if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
886 (void) sprintf(tp, "/%s", cp);
887 } else {
888 error("%.*s/%s: Name too long\n", tp - target,
889 target, cp);
890 return;
893 cp = rindex(target, '/');
894 if (cp == NULL)
895 strcpy(new, tmpname);
896 else if (cp == target)
897 (void) sprintf(new, "/%s", tmpname);
898 else {
899 *cp = '\0';
902 * sizeof (target) = RDIST_BUFSIZ and sizeof (tmpname) = 11
903 * RDIST_BUFSIZ = 50*1024 is much greater than PATH_MAX that is
904 * allowed by the kernel, so it's safe to call snprintf() here
906 (void) snprintf(new, sizeof (new), "%s/%s", target, tmpname);
907 *cp = '/';
910 if (type == S_IFLNK) {
911 int j;
913 ack();
914 cp = buf;
915 for (i = 0; i < size; i += j) {
916 if ((j = read(rem, cp, size - i)) <= 0)
917 cleanup();
918 cp += j;
920 *cp = '\0';
921 if (response() < 0) {
922 err();
923 return;
925 if (symlink(buf, new) < 0) {
926 if (errno != ENOENT || chkparent(new) < 0 ||
927 symlink(buf, new) < 0)
928 goto badn;
930 mode &= 0777;
931 if (opts & COMPARE) {
932 char tbuf[MAXPATHLEN];
934 if ((i = readlink(target, tbuf, MAXPATHLEN)) >= 0 &&
935 i == size && strncmp(buf, tbuf, size) == 0) {
936 (void) unlink(new);
937 ack();
938 return;
940 if (opts & VERIFY)
941 goto differ;
943 goto fixup;
946 if ((f = creat(new, mode & ~06000)) < 0) {
947 if (errno != ENOENT || chkparent(new) < 0 ||
948 (f = creat(new, mode & ~06000)) < 0)
949 goto badn;
952 ack();
953 wrerr = 0;
954 for (i = 0; i < size; i += RDIST_BUFSIZ) {
955 int amt = RDIST_BUFSIZ;
957 cp = buf;
958 if (i + amt > size)
959 amt = size - i;
960 do {
961 int j = read(rem, cp, amt);
962 if (j <= 0) {
963 (void) close(f);
964 (void) unlink(new);
965 cleanup();
967 amt -= j;
968 cp += j;
969 } while (amt > 0);
970 amt = RDIST_BUFSIZ;
971 if (i + amt > size)
972 amt = size - i;
973 if (wrerr == 0 && write(f, buf, amt) != amt) {
974 olderrno = errno;
975 wrerr++;
978 (void) close(f);
980 if (response() < 0) {
981 err();
982 (void) unlink(new);
983 return;
985 if (wrerr) {
986 error("%s:%s: %s\n", host, new, strerror(olderrno));
987 (void) unlink(new);
988 return;
990 if (opts & COMPARE) {
991 FILE *f1, *f2;
992 int c;
994 if ((f1 = fopen(target, "r")) == NULL)
995 goto badt;
996 if ((f2 = fopen(new, "r")) == NULL) {
997 badn:
998 error("%s:%s: %s\n", host, new, strerror(errno));
999 (void) unlink(new);
1000 return;
1002 while ((c = getc(f1)) == getc(f2))
1003 if (c == EOF) {
1004 (void) fclose(f1);
1005 (void) fclose(f2);
1006 (void) unlink(new);
1007 ack();
1008 return;
1010 (void) fclose(f1);
1011 (void) fclose(f2);
1012 if (opts & VERIFY) {
1013 differ:
1014 (void) unlink(new);
1015 sendrem("need to update: %s", target);
1016 return;
1021 * Set last modified time. For type == S_IFDIR, the lstat above filled
1022 * in stb. Otherwise, do it now.
1024 if (type != S_IFDIR)
1025 (void) lstat(new, &stb);
1026 tvp[0].tv_sec = stb.st_atime; /* old atime from target */
1027 tvp[0].tv_usec = 0;
1028 tvp[1].tv_sec = mtime;
1029 tvp[1].tv_usec = 0;
1030 if (utimes(new, tvp) < 0) {
1031 note("%s:utimes failed %s: %s", host, new, strerror(errno));
1033 if (chog(new, owner, group, mode) < 0) {
1034 (void) unlink(new);
1035 return;
1037 fixup:
1038 if (rename(new, target) < 0) {
1039 badt:
1040 error("%s:%s: %s\n", host, target, strerror(errno));
1041 (void) unlink(new);
1042 return;
1044 if (opts & COMPARE) {
1045 sendrem("updated %s", target);
1046 } else
1047 ack();
1051 * Creat a hard link to existing file.
1053 static void
1054 hardlink(cmd)
1055 char *cmd;
1057 register char *cp;
1058 struct stat stb;
1059 char *oldname;
1060 int opts, exists = 0;
1061 char oldnamebuf[RDIST_BUFSIZ];
1063 cp = cmd;
1064 opts = 0;
1065 while (*cp >= '0' && *cp <= '7')
1066 opts = (opts << 3) | (*cp++ - '0');
1067 if (*cp++ != ' ') {
1068 error("hardlink: options not delimited\n");
1069 return;
1071 oldname = cp;
1072 while (*cp && *cp != ' ')
1073 cp++;
1074 if (*cp != ' ') {
1075 error("hardlink: oldname name not delimited\n");
1076 return;
1078 *cp++ = '\0';
1080 if (catname) {
1081 if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
1082 (void) sprintf(tp, "/%s", cp);
1083 } else {
1084 error("%.*s/%s: Name too long\n", tp - target,
1085 target, cp);
1086 return;
1089 if (lstat(target, &stb) == 0) {
1090 int mode = stb.st_mode & S_IFMT;
1091 if (mode != S_IFREG && mode != S_IFLNK) {
1092 error("%s:%s: not a regular file\n", host, target);
1093 return;
1095 exists = 1;
1097 if (chkparent(target) < 0) {
1098 error("%s:%s: %s (no parent)\n",
1099 host, target, strerror(errno));
1100 return;
1102 if (opts & VERIFY) {
1103 struct stat nstb;
1105 if (exists && lstat(oldname, &nstb) == 0 &&
1106 nstb.st_mode == stb.st_mode &&
1107 nstb.st_ino == stb.st_ino &&
1108 nstb.st_dev == stb.st_dev) {
1109 ack();
1110 return;
1111 } else {
1112 sendrem("need to update: %s", target);
1113 return;
1116 if (exists && (unlink(target) < 0)) {
1117 error("%s:%s: %s (unlink)\n",
1118 host, target, strerror(errno));
1119 return;
1121 if (*oldname == '~')
1122 oldname = exptilde(oldnamebuf, sizeof (oldnamebuf), oldname);
1123 if (link(oldname, target) < 0) {
1124 error("%s:can't link %s to %s\n",
1125 host, target, oldname);
1126 return;
1128 ack();
1132 * Check to see if parent directory exists and create one if not.
1135 chkparent(name)
1136 char *name;
1138 register char *cp;
1139 struct stat stb;
1141 cp = rindex(name, '/');
1142 if (cp == NULL || cp == name)
1143 return (0);
1144 *cp = '\0';
1145 if (lstat(name, &stb) < 0) {
1146 if (errno == ENOENT && chkparent(name) >= 0 &&
1147 mkdir(name, 0777 & ~oumask) >= 0) {
1148 *cp = '/';
1149 return (0);
1151 } else if (ISDIR(stb.st_mode)) {
1152 *cp = '/';
1153 return (0);
1155 *cp = '/';
1156 return (-1);
1160 * Change owner, group and mode of file.
1163 chog(file, owner, group, mode)
1164 char *file, *owner, *group;
1165 int mode;
1167 register int i;
1168 uid_t uid, gid;
1169 extern char user[];
1172 * by default, set uid of file to the uid of the person running
1173 * this program.
1175 uid = getuid();
1178 * We'll use available privileges so we just try to do what
1179 * the client specifies. If the chown() fails we'll not
1180 * add the set-[ug]id bits; and if we want to add the set-[ug]id
1181 * bits and we're not permitted to do so, the OS will prevent us
1182 * from doing so.
1184 if (*owner == ':') {
1185 uid = atoi(owner + 1);
1186 } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
1187 if ((pw = getpwnam(owner)) == NULL) {
1188 if (mode & 04000) {
1189 note("%s:%s: unknown login name, "
1190 "clearing setuid", host, owner);
1191 mode &= ~04000;
1193 } else {
1194 uid = pw->pw_uid;
1196 } else {
1197 uid = pw->pw_uid;
1200 if (*group == ':') {
1201 gid = atoi(group + 1);
1202 goto ok;
1205 gid = -1;
1206 if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
1207 if ((*group == ':' &&
1208 (getgrgid(gid = atoi(group + 1)) == NULL)) ||
1209 ((gr = getgrnam(group)) == NULL)) {
1210 if (mode & 02000) {
1211 note("%s:%s: unknown group", host, group);
1212 mode &= ~02000;
1214 } else
1215 gid = gr->gr_gid;
1216 } else
1217 gid = gr->gr_gid;
1219 if (chown(file, uid, gid) < 0 ||
1220 (mode & 07000) && chmod(file, mode) < 0) {
1221 note("%s: chown or chmod failed: file %s: %s",
1222 host, file, strerror(errno));
1224 return (0);
1228 * Check for files on the machine being updated that are not on the master
1229 * machine and remove them.
1231 static void
1232 rmchk(opts)
1233 int opts;
1235 register char *cp, *s;
1236 struct stat stb;
1238 if (debug)
1239 printf("rmchk()\n");
1242 * Tell the remote to clean the files from the last directory sent.
1244 (void) sprintf(buf, "C%o\n", opts & VERIFY);
1245 if (debug)
1246 printf("buf = %s", buf);
1247 (void) deswrite(rem, buf, strlen(buf), 0);
1248 if (response() < 0)
1249 return;
1250 for (;;) {
1251 cp = s = buf;
1252 do {
1253 if (desread(rem, cp, 1, 0) != 1)
1254 lostconn();
1255 } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
1257 switch (*s++) {
1258 case 'Q': /* Query if file should be removed */
1260 * Return the following codes to remove query.
1261 * N\n -- file exists - DON'T remove.
1262 * Y\n -- file doesn't exist - REMOVE.
1264 *--cp = '\0';
1265 (void) sprintf(tp, "/%s", s);
1266 if (debug)
1267 printf("check %s\n", target);
1268 if (except(target))
1269 (void) deswrite(rem, "N\n", 2, 0);
1270 else if (lstat(target, &stb) < 0)
1271 (void) deswrite(rem, "Y\n", 2, 0);
1272 else
1273 (void) deswrite(rem, "N\n", 2, 0);
1274 break;
1276 case '\0':
1277 *--cp = '\0';
1278 if (*s != '\0')
1279 log(lfp, "%s\n", s);
1280 break;
1282 case 'E':
1283 *tp = '\0';
1284 (void) deswrite(rem, "\0\n", 2, 0);
1285 return;
1287 case '\1':
1288 case '\2':
1289 nerrs++;
1290 if (*s != '\n') {
1291 if (!iamremote) {
1292 fflush(stdout);
1293 (void) write(2, s, cp - s);
1295 if (lfp != NULL)
1296 (void) fwrite(s, 1, cp - s, lfp);
1298 if (buf[0] == '\2')
1299 lostconn();
1300 break;
1302 default:
1303 error("rmchk: unexpected response '%s'\n", buf);
1304 (void) deswrite(rem, "\1\n", 2, 0);
1310 * Check the current directory (initialized by the 'T' command to server())
1311 * for extraneous files and remove them.
1313 static void
1314 clean(cp)
1315 register char *cp;
1317 DIR *d;
1318 register struct dirent *dp;
1319 struct stat stb;
1320 char *otp;
1321 int len, opts;
1323 opts = 0;
1324 while (*cp >= '0' && *cp <= '7')
1325 opts = (opts << 3) | (*cp++ - '0');
1326 if (*cp != '\0') {
1327 error("clean: options not delimited\n");
1328 return;
1330 if ((d = opendir(target)) == NULL) {
1331 error("%s:%s: %s\n", host, target, strerror(errno));
1332 return;
1334 ack();
1336 otp = tp;
1337 len = tp - target;
1338 while (dp = readdir(d)) {
1339 if ((strcmp(dp->d_name, ".") == 0) ||
1340 (strcmp(dp->d_name, "..") == 0))
1341 continue;
1342 if ((int)(len + 1 + strlen(dp->d_name)) >=
1343 (int)(RDIST_BUFSIZ - 1)) {
1344 error("%s:%s/%s: Name too long\n",
1345 host, target, dp->d_name);
1346 continue;
1348 tp = otp;
1349 *tp++ = '/';
1350 cp = dp->d_name;
1351 while (*tp++ = *cp++)
1353 tp--;
1354 if (lstat(target, &stb) < 0) {
1355 error("%s:%s: %s\n", host, target, strerror(errno));
1356 continue;
1358 (void) snprintf(buf, sizeof (buf), "Q%s\n", dp->d_name);
1359 (void) write(wrem, buf, strlen(buf));
1360 cp = buf;
1361 do {
1362 if (read(rem, cp, 1) != 1)
1363 cleanup();
1364 } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
1365 *--cp = '\0';
1366 cp = buf;
1367 if (*cp != 'Y')
1368 continue;
1369 if (opts & VERIFY) {
1370 sendrem("need to remove: %s", target);
1371 } else
1372 (void) recursive_remove(&stb);
1374 closedir(d);
1375 (void) write(wrem, "E\n", 2);
1376 (void) response();
1377 tp = otp;
1378 *tp = '\0';
1382 * Remove a file or directory (recursively) and send back an acknowledge
1383 * or an error message.
1385 static void
1386 recursive_remove(stp)
1387 struct stat *stp;
1389 DIR *d;
1390 struct dirent *dp;
1391 register char *cp;
1392 struct stat stb;
1393 char *otp;
1394 int len;
1396 switch (stp->st_mode & S_IFMT) {
1397 case S_IFREG:
1398 case S_IFLNK:
1399 if (unlink(target) < 0)
1400 goto bad;
1401 goto removed;
1403 case S_IFDIR:
1404 break;
1406 default:
1407 error("%s:%s: not a plain file\n", host, target);
1408 return;
1411 if ((d = opendir(target)) == NULL)
1412 goto bad;
1414 otp = tp;
1415 len = tp - target;
1416 while (dp = readdir(d)) {
1417 if ((strcmp(dp->d_name, ".") == 0) ||
1418 (strcmp(dp->d_name, "..") == 0))
1419 continue;
1420 if ((int)(len + 1 + strlen(dp->d_name)) >=
1421 (int)(RDIST_BUFSIZ - 1)) {
1422 error("%s:%s/%s: Name too long\n",
1423 host, target, dp->d_name);
1424 continue;
1426 tp = otp;
1427 *tp++ = '/';
1428 cp = dp->d_name;
1429 while (*tp++ = *cp++)
1431 tp--;
1432 if (lstat(target, &stb) < 0) {
1433 error("%s:%s: %s\n", host, target, strerror(errno));
1434 continue;
1436 recursive_remove(&stb);
1438 closedir(d);
1439 tp = otp;
1440 *tp = '\0';
1441 if (rmdir(target) < 0) {
1442 bad:
1443 error("%s:%s: %s\n", host, target, strerror(errno));
1444 return;
1446 removed:
1447 sendrem("removed %s", target);
1451 * Execute a shell command to handle special cases.
1453 static void
1454 dospecial(cmd)
1455 char *cmd;
1457 int fd[2], status, pid, i;
1458 register char *cp, *s;
1459 char sbuf[RDIST_BUFSIZ];
1461 if (pipe(fd) < 0) {
1462 error("%s\n", strerror(errno));
1463 return;
1465 if ((pid = fork()) == 0) {
1467 * Return everything the shell commands print.
1469 (void) close(0);
1470 (void) close(1);
1471 (void) close(2);
1472 (void) open("/dev/null", 0);
1473 (void) dup(fd[1]);
1474 (void) dup(fd[1]);
1475 (void) close(fd[0]);
1476 (void) close(fd[1]);
1477 execl("/bin/sh", "sh", "-c", cmd, 0);
1478 _exit(127);
1480 (void) close(fd[1]);
1481 s = sbuf;
1482 *s++ = '\0';
1483 while ((i = read(fd[0], buf, RDIST_BUFSIZ)) > 0) {
1484 cp = buf;
1485 do {
1486 *s++ = *cp++;
1487 if (cp[-1] != '\n') {
1488 if (s < &sbuf[RDIST_BUFSIZ - 1])
1489 continue;
1490 *s++ = '\n';
1493 * Throw away blank lines.
1495 if (s == &sbuf[2]) {
1496 s--;
1497 continue;
1499 (void) write(wrem, sbuf, s - sbuf);
1500 s = &sbuf[1];
1501 } while (--i);
1503 if (s > &sbuf[1]) {
1504 *s++ = '\n';
1505 (void) write(wrem, sbuf, s - sbuf);
1507 while ((i = wait(&status)) != pid && i != -1)
1509 if (i == -1)
1510 status = -1;
1511 (void) close(fd[0]);
1512 if (status)
1513 error("shell returned %d\n", status);
1514 else
1515 ack();
1518 /*VARARGS2*/
1519 void
1520 log(fp, fmt, a1, a2, a3)
1521 FILE *fp;
1522 char *fmt;
1523 int a1, a2, a3;
1525 /* Print changes locally if not quiet mode */
1526 if (!qflag)
1527 printf(fmt, a1, a2, a3);
1529 /* Save changes (for mailing) if really updating files */
1530 if (!(options & VERIFY) && fp != NULL)
1531 fprintf(fp, fmt, a1, a2, a3);
1534 /*VARARGS1*/
1535 void
1536 error(fmt, a1, a2, a3)
1537 char *fmt;
1538 int a1, a2, a3;
1540 static FILE *fp;
1542 nerrs++;
1543 if (!fp && !(fp = fdopen(rem, "w")))
1544 return;
1545 if (iamremote) {
1546 (void) fprintf(fp, "%crdist: ", 0x01);
1547 (void) fprintf(fp, fmt, a1, a2, a3);
1548 fflush(fp);
1549 } else {
1550 fflush(stdout);
1551 (void) fprintf(stderr, "rdist: ");
1552 (void) fprintf(stderr, fmt, a1, a2, a3);
1553 fflush(stderr);
1555 if (lfp != NULL) {
1556 (void) fprintf(lfp, "rdist: ");
1557 (void) fprintf(lfp, fmt, a1, a2, a3);
1558 fflush(lfp);
1562 /*VARARGS1*/
1563 void
1564 fatal(fmt, a1, a2, a3)
1565 char *fmt;
1566 int a1, a2, a3;
1568 static FILE *fp;
1570 nerrs++;
1571 if (!fp && !(fp = fdopen(rem, "w")))
1572 return;
1573 if (iamremote) {
1574 (void) fprintf(fp, "%crdist: ", 0x02);
1575 (void) fprintf(fp, fmt, a1, a2, a3);
1576 fflush(fp);
1577 } else {
1578 fflush(stdout);
1579 (void) fprintf(stderr, "rdist: ");
1580 (void) fprintf(stderr, fmt, a1, a2, a3);
1581 fflush(stderr);
1583 if (lfp != NULL) {
1584 (void) fprintf(lfp, "rdist: ");
1585 (void) fprintf(lfp, fmt, a1, a2, a3);
1586 fflush(lfp);
1588 cleanup();
1592 response()
1594 char *cp, *s;
1595 char resp[RDIST_BUFSIZ];
1597 if (debug)
1598 printf("response()\n");
1600 cp = s = resp;
1601 more:
1602 do {
1603 if (desread(rem, cp, 1, 0) != 1)
1604 lostconn();
1605 } while (*cp++ != '\n' && cp < &resp[RDIST_BUFSIZ]);
1607 switch (*s++) {
1608 case '\0':
1609 *--cp = '\0';
1610 if (*s != '\0') {
1611 log(lfp, "%s\n", s);
1612 return (1);
1614 return (0);
1615 case '\3':
1616 *--cp = '\0';
1617 log(lfp, "Note: %s\n", s);
1618 return (response());
1620 default:
1621 s--;
1622 /* fall into... */
1623 case '\1':
1624 case '\2':
1625 nerrs++;
1626 if (*s != '\n') {
1627 if (!iamremote) {
1628 fflush(stdout);
1629 (void) write(2, s, cp - s);
1631 if (lfp != NULL)
1632 (void) fwrite(s, 1, cp - s, lfp);
1634 if (cp == &resp[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
1635 /* preserve status code */
1636 cp = s;
1637 s = resp;
1638 goto more;
1640 if (resp[0] == '\2')
1641 lostconn();
1642 return (-1);
1647 * Remove temporary files and do any cleanup operations before exiting.
1649 void
1650 cleanup()
1652 (void) unlink(Tmpfile);
1653 exit(1);
1656 static void
1657 note(fmt, a1, a2, a3)
1658 char *fmt;
1659 int a1, a2, a3;
1661 static char buf[RDIST_BUFSIZ];
1662 (void) snprintf(buf, sizeof (buf) - 1, fmt, a1, a2, a3);
1663 comment(buf);
1666 static void
1667 comment(s)
1668 char *s;
1670 char three = '\3';
1671 char nl = '\n';
1672 struct iovec iov[3];
1674 iov[0].iov_base = &three;
1675 iov[0].iov_len = sizeof (char);
1676 iov[1].iov_base = s;
1677 iov[1].iov_len = strlen(s);
1678 iov[2].iov_base = &nl;
1679 iov[2].iov_len = sizeof (char);
1680 (void) writev(rem, iov, 3);
1684 * Send message to other end.
1685 * N.B.: uses buf[].
1687 void
1688 sendrem(fmt, a1, a2, a3)
1689 char *fmt;
1690 int a1, a2, a3;
1692 register int len;
1694 buf[0] = '\0';
1695 len = snprintf(buf + 1, sizeof (buf) - 1, fmt, a1, a2, a3) + 2;
1696 if (len > sizeof (buf))
1697 len = sizeof (buf);
1698 buf[len - 1] = '\n';
1699 (void) write(wrem, buf, len);
1703 * strsub(old, new, s)
1705 * Return a pointer to a new string created by replacing substring old
1706 * with substring new in string s. String s is assumed to begin with
1707 * substring old.
1709 char *
1710 strsub(old, new, s)
1711 char *old, *new, *s;
1713 static char pbuf[PATH_MAX];
1714 register char *p, *q, *r, *plim;
1716 /* prepend new to pbuf */
1717 for (p = pbuf, q = new, plim = pbuf + sizeof (pbuf) - 1;
1718 /* CSTYLED */
1719 *q && (p < plim);)
1720 *p++ = *q++;
1721 /* p now points to the byte in pbuf where more copying should begin */
1723 /* skip over the part of s which begins with old */
1724 for (r = old, q = s; *r; q++, r++)
1726 /* q now points to the byte in s where more copying should begin */
1728 while (*q && (p < plim))
1729 *p++ = *q++;
1730 *p = '\0';
1732 return (pbuf);