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