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