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