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