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