1 /* 2 * Copyright (c) 1983 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)server.c 5.17 (Berkeley) 07/06/92"; 10 #endif /* not lint */ 11 12 #include <sys/wait.h> 13 #include "defs.h" 14 15 #define ack() (void) write(rem, "\0\n", 2) 16 #define err() (void) write(rem, "\1\n", 2) 17 18 struct linkbuf *ihead; /* list of files with more than one link */ 19 char buf[BUFSIZ]; /* general purpose buffer */ 20 char target[BUFSIZ]; /* target/source directory name */ 21 char *tp; /* pointer to end of target name */ 22 char *Tdest; /* pointer to last T dest*/ 23 int catname; /* cat name to target name */ 24 char *stp[32]; /* stack of saved tp's for directories */ 25 int oumask; /* old umask for creating files */ 26 27 extern FILE *lfp; /* log file for mailing changes */ 28 29 static int chkparent __P((char *)); 30 static void clean __P((char *)); 31 static void comment __P((char *)); 32 static void dospecial __P((char *)); 33 static int fchog __P((int, char *, char *, char *, int)); 34 static void hardlink __P((char *)); 35 static void note __P((const char *, ...)); 36 static void query __P((char *)); 37 static void recvf __P((char *, int)); 38 static void removeit __P((struct stat *)); 39 static int response __P((void)); 40 static void rmchk __P((int)); 41 static struct linkbuf * 42 savelink __P((struct stat *)); 43 static void sendf __P((char *, int)); 44 static int update __P((char *, int, struct stat *)); 45 46 /* 47 * Server routine to read requests and process them. 48 * Commands are: 49 * Tname - Transmit file if out of date 50 * Vname - Verify if file out of date or not 51 * Qname - Query if file exists. Return mtime & size if it does. 52 */ 53 void 54 server() 55 { 56 char cmdbuf[BUFSIZ]; 57 register char *cp; 58 59 signal(SIGHUP, cleanup); 60 signal(SIGINT, cleanup); 61 signal(SIGQUIT, cleanup); 62 signal(SIGTERM, cleanup); 63 signal(SIGPIPE, cleanup); 64 65 rem = 0; 66 oumask = umask(0); 67 (void) sprintf(buf, "V%d\n", VERSION); 68 (void) write(rem, buf, strlen(buf)); 69 70 for (;;) { 71 cp = cmdbuf; 72 if (read(rem, cp, 1) <= 0) 73 return; 74 if (*cp++ == '\n') { 75 error("server: expected control record\n"); 76 continue; 77 } 78 do { 79 if (read(rem, cp, 1) != 1) 80 cleanup(0); 81 } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]); 82 *--cp = '\0'; 83 cp = cmdbuf; 84 switch (*cp++) { 85 case 'T': /* init target file/directory name */ 86 catname = 1; /* target should be directory */ 87 goto dotarget; 88 89 case 't': /* init target file/directory name */ 90 catname = 0; 91 dotarget: 92 if (exptilde(target, cp) == NULL) 93 continue; 94 tp = target; 95 while (*tp) 96 tp++; 97 ack(); 98 continue; 99 100 case 'R': /* Transfer a regular file. */ 101 recvf(cp, S_IFREG); 102 continue; 103 104 case 'D': /* Transfer a directory. */ 105 recvf(cp, S_IFDIR); 106 continue; 107 108 case 'K': /* Transfer symbolic link. */ 109 recvf(cp, S_IFLNK); 110 continue; 111 112 case 'k': /* Transfer hard link. */ 113 hardlink(cp); 114 continue; 115 116 case 'E': /* End. (of directory) */ 117 *tp = '\0'; 118 if (catname <= 0) { 119 error("server: too many 'E's\n"); 120 continue; 121 } 122 tp = stp[--catname]; 123 *tp = '\0'; 124 ack(); 125 continue; 126 127 case 'C': /* Clean. Cleanup a directory */ 128 clean(cp); 129 continue; 130 131 case 'Q': /* Query. Does the file/directory exist? */ 132 query(cp); 133 continue; 134 135 case 'S': /* Special. Execute commands */ 136 dospecial(cp); 137 continue; 138 139 #ifdef notdef 140 /* 141 * These entries are reserved but not currently used. 142 * The intent is to allow remote hosts to have master copies. 143 * Currently, only the host rdist runs on can have masters. 144 */ 145 case 'X': /* start a new list of files to exclude */ 146 except = bp = NULL; 147 case 'x': /* add name to list of files to exclude */ 148 if (*cp == '\0') { 149 ack(); 150 continue; 151 } 152 if (*cp == '~') { 153 if (exptilde(buf, cp) == NULL) 154 continue; 155 cp = buf; 156 } 157 if (bp == NULL) 158 except = bp = expand(makeblock(NAME, cp), E_VARS); 159 else 160 bp->b_next = expand(makeblock(NAME, cp), E_VARS); 161 while (bp->b_next != NULL) 162 bp = bp->b_next; 163 ack(); 164 continue; 165 166 case 'I': /* Install. Transfer file if out of date. */ 167 opts = 0; 168 while (*cp >= '0' && *cp <= '7') 169 opts = (opts << 3) | (*cp++ - '0'); 170 if (*cp++ != ' ') { 171 error("server: options not delimited\n"); 172 return; 173 } 174 install(cp, opts); 175 continue; 176 177 case 'L': /* Log. save message in log file */ 178 log(lfp, cp); 179 continue; 180 #endif 181 182 case '\1': 183 nerrs++; 184 continue; 185 186 case '\2': 187 return; 188 189 default: 190 error("server: unknown command '%s'\n", cp); 191 case '\0': 192 continue; 193 } 194 } 195 } 196 197 /* 198 * Update the file(s) if they are different. 199 * destdir = 1 if destination should be a directory 200 * (i.e., more than one source is being copied to the same destination). 201 */ 202 void 203 install(src, dest, destdir, opts) 204 char *src, *dest; 205 int destdir, opts; 206 { 207 char *rname; 208 char destcopy[BUFSIZ]; 209 210 if (dest == NULL) { 211 opts &= ~WHOLE; /* WHOLE mode only useful if renaming */ 212 dest = src; 213 } 214 215 if (nflag || debug) { 216 printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install", 217 opts & WHOLE ? " -w" : "", 218 opts & YOUNGER ? " -y" : "", 219 opts & COMPARE ? " -b" : "", 220 opts & REMOVE ? " -R" : "", src, dest); 221 if (nflag) 222 return; 223 } 224 225 rname = exptilde(target, src); 226 if (rname == NULL) 227 return; 228 tp = target; 229 while (*tp) 230 tp++; 231 /* 232 * If we are renaming a directory and we want to preserve 233 * the directory heirarchy (-w), we must strip off the leading 234 * directory name and preserve the rest. 235 */ 236 if (opts & WHOLE) { 237 while (*rname == '/') 238 rname++; 239 destdir = 1; 240 } else { 241 rname = rindex(target, '/'); 242 if (rname == NULL) 243 rname = target; 244 else 245 rname++; 246 } 247 if (debug) 248 printf("target = %s, rname = %s\n", target, rname); 249 /* 250 * Pass the destination file/directory name to remote. 251 */ 252 (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest); 253 if (debug) 254 printf("buf = %s", buf); 255 (void) write(rem, buf, strlen(buf)); 256 if (response() < 0) 257 return; 258 259 if (destdir) { 260 strcpy(destcopy, dest); 261 Tdest = destcopy; 262 } 263 sendf(rname, opts); 264 Tdest = 0; 265 } 266 267 #define protoname() (pw ? pw->pw_name : user) 268 #define protogroup() (gr ? gr->gr_name : group) 269 /* 270 * Transfer the file or directory in target[]. 271 * rname is the name of the file on the remote host. 272 */ 273 static void 274 sendf(rname, opts) 275 char *rname; 276 int opts; 277 { 278 register struct subcmd *sc; 279 struct stat stb; 280 int sizerr, f, u, len; 281 off_t i; 282 DIR *d; 283 struct direct *dp; 284 char *otp, *cp; 285 extern struct subcmd *subcmds; 286 static char user[15], group[15]; 287 288 if (debug) 289 printf("sendf(%s, %x)\n", rname, opts); 290 291 if (except(target)) 292 return; 293 if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) { 294 error("%s: %s\n", target, strerror(errno)); 295 return; 296 } 297 if ((u = update(rname, opts, &stb)) == 0) { 298 if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1) 299 (void) savelink(&stb); 300 return; 301 } 302 303 if (pw == NULL || pw->pw_uid != stb.st_uid) 304 if ((pw = getpwuid(stb.st_uid)) == NULL) { 305 log(lfp, "%s: no password entry for uid %d \n", 306 target, stb.st_uid); 307 pw = NULL; 308 (void)sprintf(user, ":%lu", stb.st_uid); 309 } 310 if (gr == NULL || gr->gr_gid != stb.st_gid) 311 if ((gr = getgrgid(stb.st_gid)) == NULL) { 312 log(lfp, "%s: no name for group %d\n", 313 target, stb.st_gid); 314 gr = NULL; 315 (void)sprintf(group, ":%lu", stb.st_gid); 316 } 317 if (u == 1) { 318 if (opts & VERIFY) { 319 log(lfp, "need to install: %s\n", target); 320 goto dospecial; 321 } 322 log(lfp, "installing: %s\n", target); 323 opts &= ~(COMPARE|REMOVE); 324 } 325 326 switch (stb.st_mode & S_IFMT) { 327 case S_IFDIR: 328 if ((d = opendir(target)) == NULL) { 329 error("%s: %s\n", target, strerror(errno)); 330 return; 331 } 332 (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts, 333 stb.st_mode & 07777, protoname(), protogroup(), rname); 334 if (debug) 335 printf("buf = %s", buf); 336 (void) write(rem, buf, strlen(buf)); 337 if (response() < 0) { 338 closedir(d); 339 return; 340 } 341 342 if (opts & REMOVE) 343 rmchk(opts); 344 345 otp = tp; 346 len = tp - target; 347 while (dp = readdir(d)) { 348 if (!strcmp(dp->d_name, ".") || 349 !strcmp(dp->d_name, "..")) 350 continue; 351 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 352 error("%s/%s: Name too long\n", target, 353 dp->d_name); 354 continue; 355 } 356 tp = otp; 357 *tp++ = '/'; 358 cp = dp->d_name; 359 while (*tp++ = *cp++) 360 ; 361 tp--; 362 sendf(dp->d_name, opts); 363 } 364 closedir(d); 365 (void) write(rem, "E\n", 2); 366 (void) response(); 367 tp = otp; 368 *tp = '\0'; 369 return; 370 371 case S_IFLNK: 372 if (u != 1) 373 opts |= COMPARE; 374 if (stb.st_nlink > 1) { 375 struct linkbuf *lp; 376 377 if ((lp = savelink(&stb)) != NULL) { 378 /* install link */ 379 if (*lp->target == 0) 380 (void) sprintf(buf, "k%o %s %s\n", opts, 381 lp->pathname, rname); 382 else 383 (void) sprintf(buf, "k%o %s/%s %s\n", opts, 384 lp->target, lp->pathname, rname); 385 if (debug) 386 printf("buf = %s", buf); 387 (void) write(rem, buf, strlen(buf)); 388 (void) response(); 389 return; 390 } 391 } 392 (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts, 393 stb.st_mode & 07777, stb.st_size, stb.st_mtime, 394 protoname(), protogroup(), rname); 395 if (debug) 396 printf("buf = %s", buf); 397 (void) write(rem, buf, strlen(buf)); 398 if (response() < 0) 399 return; 400 sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size); 401 (void) write(rem, buf, stb.st_size); 402 if (debug) 403 printf("readlink = %.*s\n", (int)stb.st_size, buf); 404 goto done; 405 406 case S_IFREG: 407 break; 408 409 default: 410 error("%s: not a file or directory\n", target); 411 return; 412 } 413 414 if (u == 2) { 415 if (opts & VERIFY) { 416 log(lfp, "need to update: %s\n", target); 417 goto dospecial; 418 } 419 log(lfp, "updating: %s\n", target); 420 } 421 422 if (stb.st_nlink > 1) { 423 struct linkbuf *lp; 424 425 if ((lp = savelink(&stb)) != NULL) { 426 /* install link */ 427 if (*lp->target == 0) 428 (void) sprintf(buf, "k%o %s %s\n", opts, 429 lp->pathname, rname); 430 else 431 (void) sprintf(buf, "k%o %s/%s %s\n", opts, 432 lp->target, lp->pathname, rname); 433 if (debug) 434 printf("buf = %s", buf); 435 (void) write(rem, buf, strlen(buf)); 436 (void) response(); 437 return; 438 } 439 } 440 441 if ((f = open(target, O_RDONLY, 0)) < 0) { 442 error("%s: %s\n", target, strerror(errno)); 443 return; 444 } 445 (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts, 446 stb.st_mode & 07777, stb.st_size, stb.st_mtime, 447 protoname(), protogroup(), rname); 448 if (debug) 449 printf("buf = %s", buf); 450 (void) write(rem, buf, strlen(buf)); 451 if (response() < 0) { 452 (void) close(f); 453 return; 454 } 455 sizerr = 0; 456 for (i = 0; i < stb.st_size; i += BUFSIZ) { 457 int amt = BUFSIZ; 458 if (i + amt > stb.st_size) 459 amt = stb.st_size - i; 460 if (sizerr == 0 && read(f, buf, amt) != amt) 461 sizerr = 1; 462 (void) write(rem, buf, amt); 463 } 464 (void) close(f); 465 done: 466 if (sizerr) { 467 error("%s: file changed size\n", target); 468 err(); 469 } else 470 ack(); 471 f = response(); 472 if (f < 0 || f == 0 && (opts & COMPARE)) 473 return; 474 dospecial: 475 for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 476 if (sc->sc_type != SPECIAL) 477 continue; 478 if (sc->sc_args != NULL && !inlist(sc->sc_args, target)) 479 continue; 480 log(lfp, "special \"%s\"\n", sc->sc_name); 481 if (opts & VERIFY) 482 continue; 483 (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name); 484 if (debug) 485 printf("buf = %s", buf); 486 (void) write(rem, buf, strlen(buf)); 487 while (response() > 0) 488 ; 489 } 490 } 491 492 static struct linkbuf * 493 savelink(stp) 494 struct stat *stp; 495 { 496 struct linkbuf *lp; 497 498 for (lp = ihead; lp != NULL; lp = lp->nextp) 499 if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) { 500 lp->count--; 501 return(lp); 502 } 503 lp = (struct linkbuf *) malloc(sizeof(*lp)); 504 if (lp == NULL) 505 log(lfp, "out of memory, link information lost\n"); 506 else { 507 lp->nextp = ihead; 508 ihead = lp; 509 lp->inum = stp->st_ino; 510 lp->devnum = stp->st_dev; 511 lp->count = stp->st_nlink - 1; 512 strcpy(lp->pathname, target); 513 if (Tdest) 514 strcpy(lp->target, Tdest); 515 else 516 *lp->target = 0; 517 } 518 return(NULL); 519 } 520 521 /* 522 * Check to see if file needs to be updated on the remote machine. 523 * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date 524 * and 3 if comparing binaries to determine if out of date. 525 */ 526 static int 527 update(rname, opts, stp) 528 char *rname; 529 int opts; 530 struct stat *stp; 531 { 532 register char *cp, *s; 533 register off_t size; 534 register time_t mtime; 535 536 if (debug) 537 printf("update(%s, %x, %x)\n", rname, opts, stp); 538 539 /* 540 * Check to see if the file exists on the remote machine. 541 */ 542 (void) sprintf(buf, "Q%s\n", rname); 543 if (debug) 544 printf("buf = %s", buf); 545 (void) write(rem, buf, strlen(buf)); 546 again: 547 cp = s = buf; 548 do { 549 if (read(rem, cp, 1) != 1) 550 lostconn(0); 551 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 552 553 switch (*s++) { 554 case 'Y': 555 break; 556 557 case 'N': /* file doesn't exist so install it */ 558 return(1); 559 560 case '\1': 561 nerrs++; 562 if (*s != '\n') { 563 if (!iamremote) { 564 fflush(stdout); 565 (void) write(2, s, cp - s); 566 } 567 if (lfp != NULL) 568 (void) fwrite(s, 1, cp - s, lfp); 569 } 570 return(0); 571 572 case '\3': 573 *--cp = '\0'; 574 if (lfp != NULL) 575 log(lfp, "update: note: %s\n", s); 576 goto again; 577 578 default: 579 *--cp = '\0'; 580 error("update: unexpected response '%s'\n", s); 581 return(0); 582 } 583 584 if (*s == '\n') 585 return(2); 586 587 if (opts & COMPARE) 588 return(3); 589 590 size = 0; 591 while (isdigit(*s)) 592 size = size * 10 + (*s++ - '0'); 593 if (*s++ != ' ') { 594 error("update: size not delimited\n"); 595 return(0); 596 } 597 mtime = 0; 598 while (isdigit(*s)) 599 mtime = mtime * 10 + (*s++ - '0'); 600 if (*s != '\n') { 601 error("update: mtime not delimited\n"); 602 return(0); 603 } 604 /* 605 * File needs to be updated? 606 */ 607 if (opts & YOUNGER) { 608 if (stp->st_mtime == mtime) 609 return(0); 610 if (stp->st_mtime < mtime) { 611 log(lfp, "Warning: %s: remote copy is newer\n", target); 612 return(0); 613 } 614 } else if (stp->st_mtime == mtime && stp->st_size == size) 615 return(0); 616 return(2); 617 } 618 619 /* 620 * Query. Check to see if file exists. Return one of the following: 621 * N\n - doesn't exist 622 * Ysize mtime\n - exists and its a regular file (size & mtime of file) 623 * Y\n - exists and its a directory or symbolic link 624 * ^Aerror message\n 625 */ 626 static void 627 query(name) 628 char *name; 629 { 630 struct stat stb; 631 632 if (catname) 633 (void) sprintf(tp, "/%s", name); 634 635 if (lstat(target, &stb) < 0) { 636 if (errno == ENOENT) 637 (void) write(rem, "N\n", 2); 638 else 639 error("%s:%s: %s\n", host, target, strerror(errno)); 640 *tp = '\0'; 641 return; 642 } 643 644 switch (stb.st_mode & S_IFMT) { 645 case S_IFREG: 646 (void) sprintf(buf, "Y%qd %ld\n", stb.st_size, stb.st_mtime); 647 (void) write(rem, buf, strlen(buf)); 648 break; 649 650 case S_IFLNK: 651 case S_IFDIR: 652 (void) write(rem, "Y\n", 2); 653 break; 654 655 default: 656 error("%s: not a file or directory\n", name); 657 break; 658 } 659 *tp = '\0'; 660 } 661 662 static void 663 recvf(cmd, type) 664 char *cmd; 665 int type; 666 { 667 register char *cp; 668 int f, mode, opts, wrerr, olderrno; 669 off_t i, size; 670 time_t mtime; 671 struct stat stb; 672 struct timeval tvp[2]; 673 char *owner, *group; 674 char new[BUFSIZ]; 675 extern char *tempname; 676 677 cp = cmd; 678 opts = 0; 679 while (*cp >= '0' && *cp <= '7') 680 opts = (opts << 3) | (*cp++ - '0'); 681 if (*cp++ != ' ') { 682 error("recvf: options not delimited\n"); 683 return; 684 } 685 mode = 0; 686 while (*cp >= '0' && *cp <= '7') 687 mode = (mode << 3) | (*cp++ - '0'); 688 if (*cp++ != ' ') { 689 error("recvf: mode not delimited\n"); 690 return; 691 } 692 size = 0; 693 while (isdigit(*cp)) 694 size = size * 10 + (*cp++ - '0'); 695 if (*cp++ != ' ') { 696 error("recvf: size not delimited\n"); 697 return; 698 } 699 mtime = 0; 700 while (isdigit(*cp)) 701 mtime = mtime * 10 + (*cp++ - '0'); 702 if (*cp++ != ' ') { 703 error("recvf: mtime not delimited\n"); 704 return; 705 } 706 owner = cp; 707 while (*cp && *cp != ' ') 708 cp++; 709 if (*cp != ' ') { 710 error("recvf: owner name not delimited\n"); 711 return; 712 } 713 *cp++ = '\0'; 714 group = cp; 715 while (*cp && *cp != ' ') 716 cp++; 717 if (*cp != ' ') { 718 error("recvf: group name not delimited\n"); 719 return; 720 } 721 *cp++ = '\0'; 722 723 if (type == S_IFDIR) { 724 if (catname >= sizeof(stp)) { 725 error("%s:%s: too many directory levels\n", 726 host, target); 727 return; 728 } 729 stp[catname] = tp; 730 if (catname++) { 731 *tp++ = '/'; 732 while (*tp++ = *cp++) 733 ; 734 tp--; 735 } 736 if (opts & VERIFY) { 737 ack(); 738 return; 739 } 740 if (lstat(target, &stb) == 0) { 741 if (ISDIR(stb.st_mode)) { 742 if ((stb.st_mode & 07777) == mode) { 743 ack(); 744 return; 745 } 746 buf[0] = '\0'; 747 (void) sprintf(buf + 1, 748 "%s: Warning: remote mode %o != local mode %o\n", 749 target, stb.st_mode & 07777, mode); 750 (void) write(rem, buf, strlen(buf + 1) + 1); 751 return; 752 } 753 errno = ENOTDIR; 754 } else if (errno == ENOENT && (mkdir(target, mode) == 0 || 755 chkparent(target) == 0 && mkdir(target, mode) == 0)) { 756 if (fchog(-1, target, owner, group, mode) == 0) 757 ack(); 758 return; 759 } 760 error("%s:%s: %s\n", host, target, strerror(errno)); 761 tp = stp[--catname]; 762 *tp = '\0'; 763 return; 764 } 765 766 if (catname) 767 (void) sprintf(tp, "/%s", cp); 768 cp = rindex(target, '/'); 769 if (cp == NULL) 770 strcpy(new, tempname); 771 else if (cp == target) 772 (void) sprintf(new, "/%s", tempname); 773 else { 774 *cp = '\0'; 775 (void) sprintf(new, "%s/%s", target, tempname); 776 *cp = '/'; 777 } 778 779 if (type == S_IFLNK) { 780 int j; 781 782 ack(); 783 cp = buf; 784 for (i = 0; i < size; i += j) { 785 if ((j = read(rem, cp, size - i)) <= 0) 786 cleanup(0); 787 cp += j; 788 } 789 *cp = '\0'; 790 if (response() < 0) { 791 err(); 792 return; 793 } 794 if (symlink(buf, new) < 0) { 795 if (errno != ENOENT || chkparent(new) < 0 || 796 symlink(buf, new) < 0) 797 goto badnew1; 798 } 799 mode &= 0777; 800 if (opts & COMPARE) { 801 char tbuf[BUFSIZ]; 802 803 if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 && 804 i == size && strncmp(buf, tbuf, size) == 0) { 805 (void) unlink(new); 806 ack(); 807 return; 808 } 809 if (opts & VERIFY) 810 goto differ; 811 } 812 goto fixup; 813 } 814 815 if ((f = creat(new, mode)) < 0) { 816 if (errno != ENOENT || chkparent(new) < 0 || 817 (f = creat(new, mode)) < 0) 818 goto badnew1; 819 } 820 821 ack(); 822 wrerr = 0; 823 for (i = 0; i < size; i += BUFSIZ) { 824 int amt = BUFSIZ; 825 826 cp = buf; 827 if (i + amt > size) 828 amt = size - i; 829 do { 830 int j = read(rem, cp, amt); 831 832 if (j <= 0) { 833 (void) close(f); 834 (void) unlink(new); 835 cleanup(0); 836 } 837 amt -= j; 838 cp += j; 839 } while (amt > 0); 840 amt = BUFSIZ; 841 if (i + amt > size) 842 amt = size - i; 843 if (wrerr == 0 && write(f, buf, amt) != amt) { 844 olderrno = errno; 845 wrerr++; 846 } 847 } 848 if (response() < 0) { 849 err(); 850 goto badnew2; 851 } 852 if (wrerr) 853 goto badnew1; 854 if (opts & COMPARE) { 855 FILE *f1, *f2; 856 int c; 857 858 if ((f1 = fopen(target, "r")) == NULL) 859 goto badtarget; 860 if ((f2 = fopen(new, "r")) == NULL) { 861 badnew1: error("%s:%s: %s\n", host, new, strerror(errno)); 862 goto badnew2; 863 } 864 while ((c = getc(f1)) == getc(f2)) 865 if (c == EOF) { 866 (void) fclose(f1); 867 (void) fclose(f2); 868 ack(); 869 goto badnew2; 870 } 871 (void) fclose(f1); 872 (void) fclose(f2); 873 if (opts & VERIFY) { 874 differ: buf[0] = '\0'; 875 (void) sprintf(buf + 1, "need to update: %s\n",target); 876 (void) write(rem, buf, strlen(buf + 1) + 1); 877 goto badnew2; 878 } 879 } 880 881 /* 882 * Set last modified time 883 */ 884 tvp[0].tv_sec = time(0); 885 tvp[0].tv_usec = 0; 886 tvp[1].tv_sec = mtime; 887 tvp[1].tv_usec = 0; 888 if (utimes(new, tvp) < 0) 889 note("%s: utimes failed %s: %s\n", host, new, strerror(errno)); 890 891 if (fchog(f, new, owner, group, mode) < 0) { 892 badnew2: (void) close(f); 893 (void) unlink(new); 894 return; 895 } 896 (void) close(f); 897 898 fixup: if (rename(new, target) < 0) { 899 badtarget: error("%s:%s: %s\n", host, target, strerror(errno)); 900 (void) unlink(new); 901 return; 902 } 903 904 if (opts & COMPARE) { 905 buf[0] = '\0'; 906 (void) sprintf(buf + 1, "updated %s\n", target); 907 (void) write(rem, buf, strlen(buf + 1) + 1); 908 } else 909 ack(); 910 } 911 912 /* 913 * Creat a hard link to existing file. 914 */ 915 static void 916 hardlink(cmd) 917 char *cmd; 918 { 919 register char *cp; 920 struct stat stb; 921 char *oldname; 922 int opts, exists = 0; 923 924 cp = cmd; 925 opts = 0; 926 while (*cp >= '0' && *cp <= '7') 927 opts = (opts << 3) | (*cp++ - '0'); 928 if (*cp++ != ' ') { 929 error("hardlink: options not delimited\n"); 930 return; 931 } 932 oldname = cp; 933 while (*cp && *cp != ' ') 934 cp++; 935 if (*cp != ' ') { 936 error("hardlink: oldname name not delimited\n"); 937 return; 938 } 939 *cp++ = '\0'; 940 941 if (catname) { 942 (void) sprintf(tp, "/%s", cp); 943 } 944 if (lstat(target, &stb) == 0) { 945 int mode = stb.st_mode & S_IFMT; 946 if (mode != S_IFREG && mode != S_IFLNK) { 947 error("%s:%s: not a regular file\n", host, target); 948 return; 949 } 950 exists = 1; 951 } 952 if (chkparent(target) < 0 ) { 953 error("%s:%s: %s (no parent)\n", 954 host, target, strerror(errno)); 955 return; 956 } 957 if (exists && (unlink(target) < 0)) { 958 error("%s:%s: %s (unlink)\n", 959 host, target, strerror(errno)); 960 return; 961 } 962 if (link(oldname, target) < 0) { 963 error("%s:can't link %s to %s\n", 964 host, target, oldname); 965 return; 966 } 967 ack(); 968 } 969 970 /* 971 * Check to see if parent directory exists and create one if not. 972 */ 973 static int 974 chkparent(name) 975 char *name; 976 { 977 register char *cp; 978 struct stat stb; 979 980 cp = rindex(name, '/'); 981 if (cp == NULL || cp == name) 982 return(0); 983 *cp = '\0'; 984 if (lstat(name, &stb) < 0) { 985 if (errno == ENOENT && chkparent(name) >= 0 && 986 mkdir(name, 0777 & ~oumask) >= 0) { 987 *cp = '/'; 988 return(0); 989 } 990 } else if (ISDIR(stb.st_mode)) { 991 *cp = '/'; 992 return(0); 993 } 994 *cp = '/'; 995 return(-1); 996 } 997 998 /* 999 * Change owner, group and mode of file. 1000 */ 1001 static int 1002 fchog(fd, file, owner, group, mode) 1003 int fd; 1004 char *file, *owner, *group; 1005 int mode; 1006 { 1007 register int i; 1008 int uid, gid; 1009 extern char user[]; 1010 extern int userid; 1011 1012 uid = userid; 1013 if (userid == 0) { 1014 if (*owner == ':') { 1015 uid = atoi(owner + 1); 1016 } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) { 1017 if ((pw = getpwnam(owner)) == NULL) { 1018 if (mode & 04000) { 1019 note("%s:%s: unknown login name, clearing setuid", 1020 host, owner); 1021 mode &= ~04000; 1022 uid = 0; 1023 } 1024 } else 1025 uid = pw->pw_uid; 1026 } else 1027 uid = pw->pw_uid; 1028 if (*group == ':') { 1029 gid = atoi(group + 1); 1030 goto ok; 1031 } 1032 } else if ((mode & 04000) && strcmp(user, owner) != 0) 1033 mode &= ~04000; 1034 gid = -1; 1035 if (gr == NULL || strcmp(group, gr->gr_name) != 0) { 1036 if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL)) 1037 || ((gr = getgrnam(group)) == NULL)) { 1038 if (mode & 02000) { 1039 note("%s:%s: unknown group", host, group); 1040 mode &= ~02000; 1041 } 1042 } else 1043 gid = gr->gr_gid; 1044 } else 1045 gid = gr->gr_gid; 1046 if (userid && gid >= 0) { 1047 if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++) 1048 if (!(strcmp(user, gr->gr_mem[i]))) 1049 goto ok; 1050 mode &= ~02000; 1051 gid = -1; 1052 } 1053 ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0) 1054 note("%s: %s chown: %s", host, file, strerror(errno)); 1055 else if (mode & 07000 && 1056 (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0)) 1057 note("%s: %s chmod: %s", host, file, strerror(errno)); 1058 return(0); 1059 } 1060 1061 /* 1062 * Check for files on the machine being updated that are not on the master 1063 * machine and remove them. 1064 */ 1065 static void 1066 rmchk(opts) 1067 int opts; 1068 { 1069 register char *cp, *s; 1070 struct stat stb; 1071 1072 if (debug) 1073 printf("rmchk()\n"); 1074 1075 /* 1076 * Tell the remote to clean the files from the last directory sent. 1077 */ 1078 (void) sprintf(buf, "C%o\n", opts & VERIFY); 1079 if (debug) 1080 printf("buf = %s", buf); 1081 (void) write(rem, buf, strlen(buf)); 1082 if (response() < 0) 1083 return; 1084 for (;;) { 1085 cp = s = buf; 1086 do { 1087 if (read(rem, cp, 1) != 1) 1088 lostconn(0); 1089 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 1090 1091 switch (*s++) { 1092 case 'Q': /* Query if file should be removed */ 1093 /* 1094 * Return the following codes to remove query. 1095 * N\n -- file exists - DON'T remove. 1096 * Y\n -- file doesn't exist - REMOVE. 1097 */ 1098 *--cp = '\0'; 1099 (void) sprintf(tp, "/%s", s); 1100 if (debug) 1101 printf("check %s\n", target); 1102 if (except(target)) 1103 (void) write(rem, "N\n", 2); 1104 else if (lstat(target, &stb) < 0) 1105 (void) write(rem, "Y\n", 2); 1106 else 1107 (void) write(rem, "N\n", 2); 1108 break; 1109 1110 case '\0': 1111 *--cp = '\0'; 1112 if (*s != '\0') 1113 log(lfp, "%s\n", s); 1114 break; 1115 1116 case 'E': 1117 *tp = '\0'; 1118 ack(); 1119 return; 1120 1121 case '\1': 1122 case '\2': 1123 nerrs++; 1124 if (*s != '\n') { 1125 if (!iamremote) { 1126 fflush(stdout); 1127 (void) write(2, s, cp - s); 1128 } 1129 if (lfp != NULL) 1130 (void) fwrite(s, 1, cp - s, lfp); 1131 } 1132 if (buf[0] == '\2') 1133 lostconn(0); 1134 break; 1135 1136 default: 1137 error("rmchk: unexpected response '%s'\n", buf); 1138 err(); 1139 } 1140 } 1141 } 1142 1143 /* 1144 * Check the current directory (initialized by the 'T' command to server()) 1145 * for extraneous files and remove them. 1146 */ 1147 static void 1148 clean(cp) 1149 register char *cp; 1150 { 1151 DIR *d; 1152 register struct direct *dp; 1153 struct stat stb; 1154 char *otp; 1155 int len, opts; 1156 1157 opts = 0; 1158 while (*cp >= '0' && *cp <= '7') 1159 opts = (opts << 3) | (*cp++ - '0'); 1160 if (*cp != '\0') { 1161 error("clean: options not delimited\n"); 1162 return; 1163 } 1164 if ((d = opendir(target)) == NULL) { 1165 error("%s:%s: %s\n", host, target, strerror(errno)); 1166 return; 1167 } 1168 ack(); 1169 1170 otp = tp; 1171 len = tp - target; 1172 while (dp = readdir(d)) { 1173 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 1174 continue; 1175 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 1176 error("%s:%s/%s: Name too long\n", 1177 host, target, dp->d_name); 1178 continue; 1179 } 1180 tp = otp; 1181 *tp++ = '/'; 1182 cp = dp->d_name;; 1183 while (*tp++ = *cp++) 1184 ; 1185 tp--; 1186 if (lstat(target, &stb) < 0) { 1187 error("%s:%s: %s\n", host, target, strerror(errno)); 1188 continue; 1189 } 1190 (void) sprintf(buf, "Q%s\n", dp->d_name); 1191 (void) write(rem, buf, strlen(buf)); 1192 cp = buf; 1193 do { 1194 if (read(rem, cp, 1) != 1) 1195 cleanup(0); 1196 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 1197 *--cp = '\0'; 1198 cp = buf; 1199 if (*cp != 'Y') 1200 continue; 1201 if (opts & VERIFY) { 1202 cp = buf; 1203 *cp++ = '\0'; 1204 (void) sprintf(cp, "need to remove: %s\n", target); 1205 (void) write(rem, buf, strlen(cp) + 1); 1206 } else 1207 removeit(&stb); 1208 } 1209 closedir(d); 1210 (void) write(rem, "E\n", 2); 1211 (void) response(); 1212 tp = otp; 1213 *tp = '\0'; 1214 } 1215 1216 /* 1217 * Remove a file or directory (recursively) and send back an acknowledge 1218 * or an error message. 1219 */ 1220 static void 1221 removeit(stp) 1222 struct stat *stp; 1223 { 1224 DIR *d; 1225 struct direct *dp; 1226 register char *cp; 1227 struct stat stb; 1228 char *otp; 1229 int len; 1230 1231 switch (stp->st_mode & S_IFMT) { 1232 case S_IFREG: 1233 case S_IFLNK: 1234 if (unlink(target) < 0) 1235 goto bad; 1236 goto removed; 1237 1238 case S_IFDIR: 1239 break; 1240 1241 default: 1242 error("%s:%s: not a plain file\n", host, target); 1243 return; 1244 } 1245 1246 if ((d = opendir(target)) == NULL) 1247 goto bad; 1248 1249 otp = tp; 1250 len = tp - target; 1251 while (dp = readdir(d)) { 1252 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 1253 continue; 1254 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 1255 error("%s:%s/%s: Name too long\n", 1256 host, target, dp->d_name); 1257 continue; 1258 } 1259 tp = otp; 1260 *tp++ = '/'; 1261 cp = dp->d_name;; 1262 while (*tp++ = *cp++) 1263 ; 1264 tp--; 1265 if (lstat(target, &stb) < 0) { 1266 error("%s:%s: %s\n", host, target, strerror(errno)); 1267 continue; 1268 } 1269 removeit(&stb); 1270 } 1271 closedir(d); 1272 tp = otp; 1273 *tp = '\0'; 1274 if (rmdir(target) < 0) { 1275 bad: 1276 error("%s:%s: %s\n", host, target, strerror(errno)); 1277 return; 1278 } 1279 removed: 1280 cp = buf; 1281 *cp++ = '\0'; 1282 (void) sprintf(cp, "removed %s\n", target); 1283 (void) write(rem, buf, strlen(cp) + 1); 1284 } 1285 1286 /* 1287 * Execute a shell command to handle special cases. 1288 */ 1289 static void 1290 dospecial(cmd) 1291 char *cmd; 1292 { 1293 int fd[2], status, pid, i; 1294 register char *cp, *s; 1295 char sbuf[BUFSIZ]; 1296 extern int userid, groupid; 1297 1298 if (pipe(fd) < 0) { 1299 error("%s\n", strerror(errno)); 1300 return; 1301 } 1302 if ((pid = fork()) == 0) { 1303 /* 1304 * Return everything the shell commands print. 1305 */ 1306 (void) close(0); 1307 (void) close(1); 1308 (void) close(2); 1309 (void) open(_PATH_DEVNULL, O_RDONLY); 1310 (void) dup(fd[1]); 1311 (void) dup(fd[1]); 1312 (void) close(fd[0]); 1313 (void) close(fd[1]); 1314 setgid(groupid); 1315 setuid(userid); 1316 execl(_PATH_BSHELL, "sh", "-c", cmd, 0); 1317 _exit(127); 1318 } 1319 (void) close(fd[1]); 1320 s = sbuf; 1321 *s++ = '\0'; 1322 while ((i = read(fd[0], buf, sizeof(buf))) > 0) { 1323 cp = buf; 1324 do { 1325 *s++ = *cp++; 1326 if (cp[-1] != '\n') { 1327 if (s < &sbuf[sizeof(sbuf)-1]) 1328 continue; 1329 *s++ = '\n'; 1330 } 1331 /* 1332 * Throw away blank lines. 1333 */ 1334 if (s == &sbuf[2]) { 1335 s--; 1336 continue; 1337 } 1338 (void) write(rem, sbuf, s - sbuf); 1339 s = &sbuf[1]; 1340 } while (--i); 1341 } 1342 if (s > &sbuf[1]) { 1343 *s++ = '\n'; 1344 (void) write(rem, sbuf, s - sbuf); 1345 } 1346 while ((i = wait(&status)) != pid && i != -1) 1347 ; 1348 if (i == -1) 1349 status = -1; 1350 (void) close(fd[0]); 1351 if (status) 1352 error("shell returned %d\n", status); 1353 else 1354 ack(); 1355 } 1356 1357 #if __STDC__ 1358 #include <stdarg.h> 1359 #else 1360 #include <varargs.h> 1361 #endif 1362 1363 void 1364 #if __STDC__ 1365 log(FILE *fp, const char *fmt, ...) 1366 #else 1367 log(fp, fmt, va_alist) 1368 FILE *fp; 1369 char *fmt; 1370 va_dcl 1371 #endif 1372 { 1373 va_list ap; 1374 #if __STDC__ 1375 va_start(ap, fmt); 1376 #else 1377 va_start(ap); 1378 #endif 1379 /* Print changes locally if not quiet mode */ 1380 if (!qflag) 1381 (void)vprintf(fmt, ap); 1382 1383 /* Save changes (for mailing) if really updating files */ 1384 if (!(options & VERIFY) && fp != NULL) 1385 (void)vfprintf(fp, fmt, ap); 1386 va_end(ap); 1387 } 1388 1389 void 1390 #if __STDC__ 1391 error(const char *fmt, ...) 1392 #else 1393 error(fmt, va_alist) 1394 char *fmt; 1395 va_dcl 1396 #endif 1397 { 1398 static FILE *fp; 1399 va_list ap; 1400 #if __STDC__ 1401 va_start(ap, fmt); 1402 #else 1403 va_start(ap); 1404 #endif 1405 1406 ++nerrs; 1407 if (!fp && !(fp = fdopen(rem, "w"))) 1408 return; 1409 if (iamremote) { 1410 (void)fprintf(fp, "%crdist: ", 0x01); 1411 (void)vfprintf(fp, fmt, ap); 1412 fflush(fp); 1413 } 1414 else { 1415 fflush(stdout); 1416 (void)fprintf(stderr, "rdist: "); 1417 (void)vfprintf(stderr, fmt, ap); 1418 fflush(stderr); 1419 } 1420 if (lfp != NULL) { 1421 (void)fprintf(lfp, "rdist: "); 1422 (void)vfprintf(lfp, fmt, ap); 1423 fflush(lfp); 1424 } 1425 va_end(ap); 1426 } 1427 1428 void 1429 #if __STDC__ 1430 fatal(const char *fmt, ...) 1431 #else 1432 fatal(fmt, va_alist) 1433 char *fmt; 1434 va_dcl 1435 #endif 1436 { 1437 static FILE *fp; 1438 va_list ap; 1439 #if __STDC__ 1440 va_start(ap, fmt); 1441 #else 1442 va_start(ap); 1443 #endif 1444 1445 ++nerrs; 1446 if (!fp && !(fp = fdopen(rem, "w"))) 1447 return; 1448 if (iamremote) { 1449 (void)fprintf(fp, "%crdist: ", 0x02); 1450 (void)vfprintf(fp, fmt, ap); 1451 fflush(fp); 1452 } 1453 else { 1454 fflush(stdout); 1455 (void)fprintf(stderr, "rdist: "); 1456 (void)vfprintf(stderr, fmt, ap); 1457 fflush(stderr); 1458 } 1459 if (lfp != NULL) { 1460 (void)fprintf(lfp, "rdist: "); 1461 (void)vfprintf(lfp, fmt, ap); 1462 fflush(lfp); 1463 } 1464 cleanup(0); 1465 } 1466 1467 static int 1468 response() 1469 { 1470 char *cp, *s; 1471 char resp[BUFSIZ]; 1472 1473 if (debug) 1474 printf("response()\n"); 1475 1476 cp = s = resp; 1477 do { 1478 if (read(rem, cp, 1) != 1) 1479 lostconn(0); 1480 } while (*cp++ != '\n' && cp < &resp[BUFSIZ]); 1481 1482 switch (*s++) { 1483 case '\0': 1484 *--cp = '\0'; 1485 if (*s != '\0') { 1486 log(lfp, "%s\n", s); 1487 return(1); 1488 } 1489 return(0); 1490 case '\3': 1491 *--cp = '\0'; 1492 log(lfp, "Note: %s\n",s); 1493 return(response()); 1494 1495 default: 1496 s--; 1497 /* fall into... */ 1498 case '\1': 1499 case '\2': 1500 nerrs++; 1501 if (*s != '\n') { 1502 if (!iamremote) { 1503 fflush(stdout); 1504 (void) write(2, s, cp - s); 1505 } 1506 if (lfp != NULL) 1507 (void) fwrite(s, 1, cp - s, lfp); 1508 } 1509 if (resp[0] == '\2') 1510 lostconn(0); 1511 return(-1); 1512 } 1513 } 1514 1515 /* 1516 * Remove temporary files and do any cleanup operations before exiting. 1517 */ 1518 void 1519 cleanup(signo) 1520 int signo; 1521 { 1522 (void) unlink(tempfile); 1523 exit(1); 1524 } 1525 1526 static void 1527 #if __STDC__ 1528 note(const char *fmt, ...) 1529 #else 1530 note(fmt, va_alist) 1531 char *fmt; 1532 va_dcl 1533 #endif 1534 { 1535 static char buf[BUFSIZ]; 1536 va_list ap; 1537 #if __STDC__ 1538 va_start(ap, fmt); 1539 #else 1540 va_start(ap); 1541 #endif 1542 (void)vsnprintf(buf, sizeof(buf), fmt, ap); 1543 va_end(ap); 1544 comment(buf); 1545 } 1546 1547 static void 1548 comment(s) 1549 char *s; 1550 { 1551 char c; 1552 1553 c = '\3'; 1554 write(rem, &c, 1); 1555 write(rem, s, strlen(s)); 1556 c = '\n'; 1557 write(rem, &c, 1); 1558 } 1559