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