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