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