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