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