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