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