1 /* $OpenBSD: server.c,v 1.33 2014/07/12 03:10:03 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 1983 Regents of the University of California. 5 * 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <dirent.h> 33 34 #include "defs.h" 35 36 /* 37 * Server routines 38 */ 39 40 char tempname[sizeof _RDIST_TMP + 1]; /* Tmp file name */ 41 char buf[BUFSIZ]; /* general purpose buffer */ 42 char target[MAXPATHLEN]; /* target/source directory name */ 43 char *ptarget; /* pointer to end of target name */ 44 int catname = 0; /* cat name to target name */ 45 char *sptarget[32]; /* stack of saved ptarget's for directories */ 46 char *fromhost = NULL; /* Client hostname */ 47 static int64_t min_freespace = 0; /* Minimium free space on a filesystem */ 48 static int64_t min_freefiles = 0; /* Minimium free # files on a filesystem */ 49 int oumask; /* Old umask */ 50 51 static int cattarget(char *); 52 static int setownership(char *, int, uid_t, gid_t, int); 53 static int setfilemode(char *, int, int, int); 54 static int fchog(int, char *, char *, char *, int); 55 static int removefile(struct stat *, int); 56 static void doclean(char *); 57 static void clean(char *); 58 static void dospecial(char *); 59 static void docmdspecial(void); 60 static void query(char *); 61 static int chkparent(char *, opt_t); 62 static char *savetarget(char *, opt_t); 63 static void recvfile(char *, opt_t, int, char *, char *, time_t, time_t, off_t); 64 static void recvdir(opt_t, int, char *, char *); 65 static void recvlink(char *, opt_t, int, off_t); 66 static void hardlink(char *); 67 static void setconfig(char *); 68 static void recvit(char *, int); 69 static void dochmog(char *); 70 static void settarget(char *, int); 71 72 /* 73 * Cat "string" onto the target buffer with error checking. 74 */ 75 static int 76 cattarget(char *string) 77 { 78 if (strlen(string) + strlen(target) + 2 > sizeof(target)) { 79 message(MT_INFO, "target buffer is not large enough."); 80 return(-1); 81 } 82 if (!ptarget) { 83 message(MT_INFO, "NULL target pointer set."); 84 return(-10); 85 } 86 87 (void) snprintf(ptarget, sizeof(target) - (ptarget - target), 88 "/%s", string); 89 90 return(0); 91 } 92 93 /* 94 * Set uid and gid ownership of a file. 95 */ 96 static int 97 setownership(char *file, int fd, uid_t uid, gid_t gid, int islink) 98 { 99 int status = -1; 100 101 /* 102 * We assume only the Superuser can change uid ownership. 103 */ 104 if (getuid() != 0) 105 uid = -1; 106 107 if (islink) 108 status = lchown(file, uid, gid); 109 110 if (fd != -1 && !islink) 111 status = fchown(fd, uid, gid); 112 113 if (status < 0 && !islink) 114 status = chown(file, uid, gid); 115 116 if (status < 0) { 117 if (uid == (uid_t)-1) 118 message(MT_NOTICE, "%s: chgrp %d failed: %s", 119 target, gid, SYSERR); 120 else 121 message(MT_NOTICE, "%s: chown %d.%d failed: %s", 122 target, uid, gid, SYSERR); 123 return(-1); 124 } 125 126 return(0); 127 } 128 129 /* 130 * Set mode of a file 131 */ 132 static int 133 setfilemode(char *file, int fd, int mode, int islink) 134 { 135 int status = -1; 136 137 if (mode == -1) 138 return(0); 139 140 if (islink) 141 status = fchmodat(AT_FDCWD, file, mode, AT_SYMLINK_NOFOLLOW); 142 143 if (fd != -1 && !islink) 144 status = fchmod(fd, mode); 145 146 if (status < 0 && !islink) 147 status = chmod(file, mode); 148 149 if (status < 0) { 150 message(MT_NOTICE, "%s: chmod failed: %s", target, SYSERR); 151 return(-1); 152 } 153 154 return(0); 155 } 156 /* 157 * Change owner, group and mode of file. 158 */ 159 static int 160 fchog(int fd, char *file, char *owner, char *group, int mode) 161 { 162 static struct group *gr = NULL; 163 extern char *locuser; 164 int i; 165 struct stat st; 166 uid_t uid; 167 gid_t gid; 168 gid_t primegid = (gid_t)-2; 169 170 uid = userid; 171 if (userid == 0) { /* running as root; take anything */ 172 if (*owner == ':') { 173 uid = (uid_t) atoi(owner + 1); 174 } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) { 175 if ((pw = getpwnam(owner)) == NULL) { 176 if (mode != -1 && IS_ON(mode, S_ISUID)) { 177 message(MT_NOTICE, 178 "%s: unknown login name \"%s\", clearing setuid", 179 target, owner); 180 mode &= ~S_ISUID; 181 uid = 0; 182 } else 183 message(MT_NOTICE, 184 "%s: unknown login name \"%s\"", 185 target, owner); 186 } else 187 uid = pw->pw_uid; 188 } else { 189 uid = pw->pw_uid; 190 primegid = pw->pw_gid; 191 } 192 if (*group == ':') { 193 gid = (gid_t)atoi(group + 1); 194 goto ok; 195 } 196 } else { /* not root, setuid only if user==owner */ 197 struct passwd *lupw; 198 199 if (mode != -1) { 200 if (IS_ON(mode, S_ISUID) && 201 strcmp(locuser, owner) != 0) 202 mode &= ~S_ISUID; 203 if (mode) 204 mode &= ~S_ISVTX; /* and strip sticky too */ 205 } 206 207 if ((lupw = getpwnam(locuser)) != NULL) 208 primegid = lupw->pw_gid; 209 } 210 211 gid = (gid_t)-1; 212 if (gr == NULL || strcmp(group, gr->gr_name) != 0) { 213 if ((*group == ':' && 214 (getgrgid(gid = atoi(group + 1)) == NULL)) 215 || ((gr = (struct group *)getgrnam(group)) == NULL)) { 216 if (mode != -1 && IS_ON(mode, S_ISGID)) { 217 message(MT_NOTICE, 218 "%s: unknown group \"%s\", clearing setgid", 219 target, group); 220 mode &= ~S_ISGID; 221 } else 222 message(MT_NOTICE, 223 "%s: unknown group \"%s\"", 224 target, group); 225 } else 226 gid = gr->gr_gid; 227 } else 228 gid = gr->gr_gid; 229 230 if (userid && gid >= 0 && gid != primegid) { 231 if (gr) 232 for (i = 0; gr->gr_mem[i] != NULL; i++) 233 if (strcmp(locuser, gr->gr_mem[i]) == 0) 234 goto ok; 235 if (mode != -1 && IS_ON(mode, S_ISGID)) { 236 message(MT_NOTICE, 237 "%s: user %s not in group %s, clearing setgid", 238 target, locuser, group); 239 mode &= ~S_ISGID; 240 } 241 gid = (gid_t)-1; 242 } 243 ok: 244 if (stat(file, &st) == -1) { 245 error("%s: Stat failed %s", file, SYSERR); 246 return -1; 247 } 248 /* 249 * Set uid and gid ownership. If that fails, strip setuid and 250 * setgid bits from mode. Once ownership is set, successful 251 * or otherwise, set the new file mode. 252 */ 253 if (setownership(file, fd, uid, gid, S_ISLNK(st.st_mode)) < 0) { 254 if (mode != -1 && IS_ON(mode, S_ISUID)) { 255 message(MT_NOTICE, 256 "%s: chown failed, clearing setuid", target); 257 mode &= ~S_ISUID; 258 } 259 if (mode != -1 && IS_ON(mode, S_ISGID)) { 260 message(MT_NOTICE, 261 "%s: chown failed, clearing setgid", target); 262 mode &= ~S_ISGID; 263 } 264 } 265 (void) setfilemode(file, fd, mode, S_ISLNK(st.st_mode)); 266 267 268 return(0); 269 } 270 271 /* 272 * Remove a file or directory (recursively) and send back an acknowledge 273 * or an error message. 274 */ 275 static int 276 removefile(struct stat *statb, int silent) 277 { 278 DIR *d; 279 static struct dirent *dp; 280 char *cp; 281 struct stat stb; 282 char *optarget; 283 int len, failures = 0; 284 285 switch (statb->st_mode & S_IFMT) { 286 case S_IFREG: 287 case S_IFLNK: 288 case S_IFCHR: 289 case S_IFBLK: 290 case S_IFSOCK: 291 case S_IFIFO: 292 if (unlink(target) < 0) { 293 if (errno == ETXTBSY) { 294 if (!silent) 295 message(MT_REMOTE|MT_NOTICE, 296 "%s: unlink failed: %s", 297 target, SYSERR); 298 return(0); 299 } else { 300 error("%s: unlink failed: %s", target, SYSERR); 301 return(-1); 302 } 303 } 304 goto removed; 305 306 case S_IFDIR: 307 break; 308 309 default: 310 error("%s: not a plain file", target); 311 return(-1); 312 } 313 314 errno = 0; 315 if ((d = opendir(target)) == NULL) { 316 error("%s: opendir failed: %s", target, SYSERR); 317 return(-1); 318 } 319 320 optarget = ptarget; 321 len = ptarget - target; 322 while ((dp = readdir(d)) != NULL) { 323 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || 324 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 325 continue; 326 327 if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) { 328 if (!silent) 329 message(MT_REMOTE|MT_WARNING, 330 "%s/%s: Name too long", 331 target, dp->d_name); 332 continue; 333 } 334 ptarget = optarget; 335 *ptarget++ = '/'; 336 cp = dp->d_name; 337 while ((*ptarget++ = *cp++) != '\0') 338 continue; 339 ptarget--; 340 if (lstat(target, &stb) < 0) { 341 if (!silent) 342 message(MT_REMOTE|MT_WARNING, 343 "%s: lstat failed: %s", 344 target, SYSERR); 345 continue; 346 } 347 if (removefile(&stb, 0) < 0) 348 ++failures; 349 } 350 (void) closedir(d); 351 ptarget = optarget; 352 *ptarget = CNULL; 353 354 if (failures) 355 return(-1); 356 357 if (rmdir(target) < 0) { 358 error("%s: rmdir failed: %s", target, SYSERR); 359 return(-1); 360 } 361 removed: 362 #if NEWWAY 363 if (!silent) 364 message(MT_CHANGE|MT_REMOTE, "%s: removed", target); 365 #else 366 /* 367 * We use MT_NOTICE instead of MT_CHANGE because this function is 368 * sometimes called by other functions that are suppose to return a 369 * single ack() back to the client (rdist). This is a kludge until 370 * the Rdist protocol is re-done. Sigh. 371 */ 372 message(MT_NOTICE|MT_REMOTE, "%s: removed", target); 373 #endif 374 return(0); 375 } 376 377 /* 378 * Check the current directory (initialized by the 'T' command to server()) 379 * for extraneous files and remove them. 380 */ 381 static void 382 doclean(char *cp) 383 { 384 DIR *d; 385 struct dirent *dp; 386 struct stat stb; 387 char *optarget, *ep; 388 int len; 389 opt_t opts; 390 char targ[MAXPATHLEN*4]; 391 392 opts = strtol(cp, &ep, 8); 393 if (*ep != CNULL) { 394 error("clean: options not delimited"); 395 return; 396 } 397 if ((d = opendir(target)) == NULL) { 398 error("%s: opendir failed: %s", target, SYSERR); 399 return; 400 } 401 ack(); 402 403 optarget = ptarget; 404 len = ptarget - target; 405 while ((dp = readdir(d)) != NULL) { 406 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || 407 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 408 continue; 409 410 if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) { 411 message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long", 412 target, dp->d_name); 413 continue; 414 } 415 ptarget = optarget; 416 *ptarget++ = '/'; 417 cp = dp->d_name; 418 while ((*ptarget++ = *cp++) != '\0') 419 continue; 420 ptarget--; 421 if (lstat(target, &stb) < 0) { 422 message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s", 423 target, SYSERR); 424 continue; 425 } 426 427 ENCODE(targ, dp->d_name); 428 (void) sendcmd(CC_QUERY, "%s", targ); 429 (void) remline(cp = buf, sizeof(buf), TRUE); 430 431 if (*cp != CC_YES) 432 continue; 433 434 if (IS_ON(opts, DO_VERIFY)) 435 message(MT_REMOTE|MT_INFO, "%s: need to remove", 436 target); 437 else 438 (void) removefile(&stb, 0); 439 } 440 (void) closedir(d); 441 442 ptarget = optarget; 443 *ptarget = CNULL; 444 } 445 446 /* 447 * Frontend to doclean(). 448 */ 449 static void 450 clean(char *cp) 451 { 452 doclean(cp); 453 (void) sendcmd(CC_END, NULL); 454 (void) response(); 455 } 456 457 /* 458 * Execute a shell command to handle special cases. 459 * We can't really set an alarm timeout here since we 460 * have no idea how long the command should take. 461 */ 462 static void 463 dospecial(char *xcmd) 464 { 465 char cmd[BUFSIZ]; 466 if (DECODE(cmd, xcmd) == -1) { 467 error("dospecial: Cannot decode command."); 468 return; 469 } 470 runcommand(cmd); 471 } 472 473 /* 474 * Do a special cmd command. This differs from normal special 475 * commands in that it's done after an entire command has been updated. 476 * The list of updated target files is sent one at a time with RC_FILE 477 * commands. Each one is added to an environment variable defined by 478 * E_FILES. When an RC_COMMAND is finally received, the E_FILES variable 479 * is stuffed into our environment and a normal dospecial() command is run. 480 */ 481 static void 482 docmdspecial(void) 483 { 484 char *cp; 485 char *cmd, *env = NULL; 486 int n; 487 size_t len; 488 489 /* We're ready */ 490 ack(); 491 492 for ( ; ; ) { 493 n = remline(cp = buf, sizeof(buf), FALSE); 494 if (n <= 0) { 495 error("cmdspecial: premature end of input."); 496 return; 497 } 498 499 switch (*cp++) { 500 case RC_FILE: 501 if (env == NULL) { 502 len = (2 * sizeof(E_FILES)) + strlen(cp) + 10; 503 env = (char *) xmalloc(len); 504 (void) snprintf(env, len, "export %s;%s=%s", 505 E_FILES, E_FILES, cp); 506 } else { 507 len = strlen(env) + 1 + strlen(cp) + 1; 508 env = (char *) xrealloc(env, len); 509 (void) strlcat(env, ":", len); 510 (void) strlcat(env, cp, len); 511 } 512 ack(); 513 break; 514 515 case RC_COMMAND: 516 if (env) { 517 len = strlen(env) + 1 + strlen(cp) + 1; 518 env = (char *) xrealloc(env, len); 519 (void) strlcat(env, ";", len); 520 (void) strlcat(env, cp, len); 521 cmd = env; 522 } else 523 cmd = cp; 524 525 dospecial(cmd); 526 if (env) 527 (void) free(env); 528 return; 529 530 default: 531 error("Unknown cmdspecial command '%s'.", cp); 532 return; 533 } 534 } 535 } 536 537 /* 538 * Query. Check to see if file exists. Return one of the following: 539 * 540 #ifdef NFS_CHECK 541 * QC_ONNFS - resides on a NFS 542 #endif NFS_CHECK 543 #ifdef RO_CHECK 544 * QC_ONRO - resides on a Read-Only filesystem 545 #endif RO_CHECK 546 * QC_NO - doesn't exist 547 * QC_YESsize mtime - exists and its a regular file (size & mtime of file) 548 * QC_YES - exists and its a directory or symbolic link 549 * QC_ERRMSGmessage - error message 550 */ 551 static void 552 query(char *xname) 553 { 554 static struct stat stb; 555 int s = -1, stbvalid = 0; 556 char name[MAXPATHLEN]; 557 558 if (DECODE(name, xname) == -1) { 559 error("query: Cannot decode filename"); 560 return; 561 } 562 563 if (catname && cattarget(name) < 0) 564 return; 565 566 #if defined(NFS_CHECK) 567 if (IS_ON(options, DO_CHKNFS)) { 568 s = is_nfs_mounted(target, &stb, &stbvalid); 569 if (s > 0) 570 (void) sendcmd(QC_ONNFS, NULL); 571 572 /* Either the above check was true or an error occurred */ 573 /* and is_nfs_mounted sent the error message */ 574 if (s != 0) { 575 *ptarget = CNULL; 576 return; 577 } 578 } 579 #endif /* NFS_CHECK */ 580 581 #if defined(RO_CHECK) 582 if (IS_ON(options, DO_CHKREADONLY)) { 583 s = is_ro_mounted(target, &stb, &stbvalid); 584 if (s > 0) 585 (void) sendcmd(QC_ONRO, NULL); 586 587 /* Either the above check was true or an error occurred */ 588 /* and is_ro_mounted sent the error message */ 589 if (s != 0) { 590 *ptarget = CNULL; 591 return; 592 } 593 } 594 #endif /* RO_CHECK */ 595 596 if (IS_ON(options, DO_CHKSYM)) { 597 if (is_symlinked(target, &stb, &stbvalid) > 0) { 598 (void) sendcmd(QC_SYM, NULL); 599 return; 600 } 601 } 602 603 /* 604 * If stbvalid is false, "stb" is not valid because: 605 * a) RO_CHECK and NFS_CHECK were not defined 606 * b) The stat by is_*_mounted() either failed or 607 * does not match "target". 608 */ 609 if (!stbvalid && lstat(target, &stb) < 0) { 610 if (errno == ENOENT) 611 (void) sendcmd(QC_NO, NULL); 612 else 613 error("%s: lstat failed: %s", target, SYSERR); 614 *ptarget = CNULL; 615 return; 616 } 617 618 switch (stb.st_mode & S_IFMT) { 619 case S_IFLNK: 620 case S_IFDIR: 621 case S_IFREG: 622 (void) sendcmd(QC_YES, "%lld %lld %o %s %s", 623 (long long) stb.st_size, 624 (long long) stb.st_mtime, 625 stb.st_mode & 07777, 626 getusername(stb.st_uid, target, options), 627 getgroupname(stb.st_gid, target, options)); 628 break; 629 630 default: 631 error("%s: not a file or directory", target); 632 break; 633 } 634 *ptarget = CNULL; 635 } 636 637 /* 638 * Check to see if parent directory exists and create one if not. 639 */ 640 static int 641 chkparent(char *name, opt_t opts) 642 { 643 char *cp; 644 struct stat stb; 645 int r = -1; 646 647 debugmsg(DM_CALL, "chkparent(%s, %lo) start\n", name, opts); 648 649 cp = strrchr(name, '/'); 650 if (cp == NULL || cp == name) 651 return(0); 652 653 *cp = CNULL; 654 655 if (lstat(name, &stb) < 0) { 656 if (errno == ENOENT && chkparent(name, opts) >= 0) { 657 if (mkdir(name, 0777 & ~oumask) == 0) { 658 message(MT_NOTICE, "%s: mkdir", name); 659 r = 0; 660 } else 661 debugmsg(DM_MISC, 662 "chkparent(%s, %lo) mkdir fail: %s\n", 663 name, opts, SYSERR); 664 } 665 } else /* It exists */ 666 r = 0; 667 668 /* Put back what we took away */ 669 *cp = '/'; 670 671 return(r); 672 } 673 674 /* 675 * Save a copy of 'file' by renaming it. 676 */ 677 static char * 678 savetarget(char *file, opt_t opts) 679 { 680 static char savefile[MAXPATHLEN]; 681 682 if (strlen(file) + sizeof(SAVE_SUFFIX) + 1 > MAXPATHLEN) { 683 error("%s: Cannot save: Save name too long", file); 684 return(NULL); 685 } 686 687 if (IS_ON(opts, DO_HISTORY)) { 688 int i; 689 struct stat st; 690 /* 691 * There is a race here, but the worst that can happen 692 * is to lose a version of the file 693 */ 694 for (i = 1; i < 1000; i++) { 695 (void) snprintf(savefile, sizeof(savefile), 696 "%s;%.3d", file, i); 697 if (stat(savefile, &st) == -1 && errno == ENOENT) 698 break; 699 700 } 701 if (i == 1000) { 702 message(MT_NOTICE, 703 "%s: More than 1000 versions for %s; reusing 1\n", 704 savefile, SYSERR); 705 i = 1; 706 (void) snprintf(savefile, sizeof(savefile), 707 "%s;%.3d", file, i); 708 } 709 } 710 else { 711 (void) snprintf(savefile, sizeof(savefile), "%s%s", 712 file, SAVE_SUFFIX); 713 714 if (unlink(savefile) != 0 && errno != ENOENT) { 715 message(MT_NOTICE, "%s: remove failed: %s", 716 savefile, SYSERR); 717 return(NULL); 718 } 719 } 720 721 if (rename(file, savefile) != 0 && errno != ENOENT) { 722 error("%s -> %s: rename failed: %s", 723 file, savefile, SYSERR); 724 return(NULL); 725 } 726 727 return(savefile); 728 } 729 730 /* 731 * Receive a file 732 */ 733 static void 734 recvfile(char *new, opt_t opts, int mode, char *owner, char *group, 735 time_t mtime, time_t atime, off_t size) 736 { 737 int f, wrerr, olderrno; 738 off_t i; 739 char *cp; 740 char *savefile = NULL; 741 static struct stat statbuff; 742 743 /* 744 * Create temporary file 745 */ 746 if ((f = mkstemp(new)) < 0) { 747 if (errno != ENOENT || chkparent(new, opts) < 0 || 748 (f = mkstemp(new)) < 0) { 749 error("%s: create failed: %s", new, SYSERR); 750 return; 751 } 752 } 753 754 /* 755 * Receive the file itself 756 */ 757 ack(); 758 wrerr = 0; 759 olderrno = 0; 760 for (i = 0; i < size; i += BUFSIZ) { 761 off_t amt = BUFSIZ; 762 763 cp = buf; 764 if (i + amt > size) 765 amt = size - i; 766 do { 767 ssize_t j; 768 769 j = readrem(cp, amt); 770 if (j <= 0) { 771 (void) close(f); 772 (void) unlink(new); 773 fatalerr( 774 "Read error occurred while receiving file."); 775 finish(); 776 } 777 amt -= j; 778 cp += j; 779 } while (amt > 0); 780 amt = BUFSIZ; 781 if (i + amt > size) 782 amt = size - i; 783 if (wrerr == 0 && xwrite(f, buf, amt) != amt) { 784 olderrno = errno; 785 wrerr++; 786 } 787 } 788 789 if (response() < 0) { 790 (void) close(f); 791 (void) unlink(new); 792 return; 793 } 794 795 if (wrerr) { 796 error("%s: Write error: %s", new, strerror(olderrno)); 797 (void) close(f); 798 (void) unlink(new); 799 return; 800 } 801 802 /* 803 * Do file comparison if enabled 804 */ 805 if (IS_ON(opts, DO_COMPARE)) { 806 FILE *f1, *f2; 807 int c; 808 809 errno = 0; /* fopen is not a syscall */ 810 if ((f1 = fopen(target, "r")) == NULL) { 811 error("%s: open for read failed: %s", target, SYSERR); 812 (void) close(f); 813 (void) unlink(new); 814 return; 815 } 816 errno = 0; 817 if ((f2 = fopen(new, "r")) == NULL) { 818 error("%s: open for read failed: %s", new, SYSERR); 819 (void) fclose(f1); 820 (void) close(f); 821 (void) unlink(new); 822 return; 823 } 824 while ((c = getc(f1)) == getc(f2)) 825 if (c == EOF) { 826 debugmsg(DM_MISC, 827 "Files are the same '%s' '%s'.", 828 target, new); 829 (void) fclose(f1); 830 (void) fclose(f2); 831 (void) close(f); 832 (void) unlink(new); 833 /* 834 * This isn't an error per-se, but we 835 * need to indicate to the master that 836 * the file was not updated. 837 */ 838 error(""); 839 return; 840 } 841 debugmsg(DM_MISC, "Files are different '%s' '%s'.", 842 target, new); 843 (void) fclose(f1); 844 (void) fclose(f2); 845 if (IS_ON(opts, DO_VERIFY)) { 846 message(MT_REMOTE|MT_INFO, "%s: need to update", 847 target); 848 (void) close(f); 849 (void) unlink(new); 850 return; 851 } 852 } 853 854 /* 855 * Set owner, group, and file mode 856 */ 857 if (fchog(f, new, owner, group, mode) < 0) { 858 (void) close(f); 859 (void) unlink(new); 860 return; 861 } 862 (void) close(f); 863 864 /* 865 * Perform utimes() after file is closed to make 866 * certain OS's, such as NeXT 2.1, happy. 867 */ 868 if (setfiletime(new, time(NULL), mtime) < 0) 869 message(MT_NOTICE, "%s: utimes failed: %s", new, SYSERR); 870 871 /* 872 * Try to save target file from being over-written 873 */ 874 if (IS_ON(opts, DO_SAVETARGETS)) 875 if ((savefile = savetarget(target, opts)) == NULL) { 876 (void) unlink(new); 877 return; 878 } 879 880 /* 881 * If the target is a directory, we need to remove it first 882 * before we can rename the new file. 883 */ 884 if ((stat(target, &statbuff) == 0) && S_ISDIR(statbuff.st_mode)) { 885 char *saveptr = ptarget; 886 887 ptarget = &target[strlen(target)]; 888 removefile(&statbuff, 0); 889 ptarget = saveptr; 890 } 891 892 /* 893 * Install new (temporary) file as the actual target 894 */ 895 if (rename(new, target) < 0) { 896 static const char fmt[] = "%s -> %s: rename failed: %s"; 897 struct stat stb; 898 /* 899 * If the rename failed due to "Text file busy", then 900 * try to rename the target file and retry the rename. 901 */ 902 switch (errno) { 903 case ETXTBSY: 904 /* Save the target */ 905 if ((savefile = savetarget(target, opts)) != NULL) { 906 /* Retry installing new file as target */ 907 if (rename(new, target) < 0) { 908 error(fmt, new, target, SYSERR); 909 /* Try to put back save file */ 910 if (rename(savefile, target) < 0) 911 error(fmt, 912 savefile, target, SYSERR); 913 (void) unlink(new); 914 } else 915 message(MT_NOTICE, "%s: renamed to %s", 916 target, savefile); 917 /* 918 * XXX: We should remove the savefile here. 919 * But we are nice to nfs clients and 920 * we keep it. 921 */ 922 } 923 break; 924 case EISDIR: 925 /* 926 * See if target is a directory and remove it if it is 927 */ 928 if (lstat(target, &stb) == 0) { 929 if (S_ISDIR(stb.st_mode)) { 930 char *optarget = ptarget; 931 for (ptarget = target; *ptarget; 932 ptarget++); 933 /* If we failed to remove, we'll catch 934 it later */ 935 (void) removefile(&stb, 1); 936 ptarget = optarget; 937 } 938 } 939 if (rename(new, target) >= 0) 940 break; 941 /*FALLTHROUGH*/ 942 943 default: 944 error(fmt, new, target, SYSERR); 945 (void) unlink(new); 946 break; 947 } 948 } 949 950 if (IS_ON(opts, DO_COMPARE)) 951 message(MT_REMOTE|MT_CHANGE, "%s: updated", target); 952 else 953 ack(); 954 } 955 956 /* 957 * Receive a directory 958 */ 959 static void 960 recvdir(opt_t opts, int mode, char *owner, char *group) 961 { 962 static char lowner[100], lgroup[100]; 963 char *cp; 964 struct stat stb; 965 int s; 966 967 s = lstat(target, &stb); 968 if (s == 0) { 969 /* 970 * If target is not a directory, remove it 971 */ 972 if (!S_ISDIR(stb.st_mode)) { 973 if (IS_ON(opts, DO_VERIFY)) 974 message(MT_NOTICE, "%s: need to remove", 975 target); 976 else { 977 if (unlink(target) < 0) { 978 error("%s: remove failed: %s", 979 target, SYSERR); 980 return; 981 } 982 } 983 s = -1; 984 errno = ENOENT; 985 } else { 986 if (!IS_ON(opts, DO_NOCHKMODE) && 987 (stb.st_mode & 07777) != mode) { 988 if (IS_ON(opts, DO_VERIFY)) 989 message(MT_NOTICE, 990 "%s: need to chmod to %o", 991 target, mode); 992 else { 993 if (chmod(target, mode) != 0) 994 message(MT_NOTICE, 995 "%s: chmod from %o to %o failed: %s", 996 target, 997 stb.st_mode & 07777, 998 mode, 999 SYSERR); 1000 else 1001 message(MT_NOTICE, 1002 "%s: chmod from %o to %o", 1003 target, 1004 stb.st_mode & 07777, 1005 mode); 1006 } 1007 } 1008 1009 /* 1010 * Check ownership and set if necessary 1011 */ 1012 lowner[0] = CNULL; 1013 lgroup[0] = CNULL; 1014 1015 if (!IS_ON(opts, DO_NOCHKOWNER) && owner) { 1016 int o; 1017 1018 o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER : 1019 opts; 1020 if ((cp = getusername(stb.st_uid, target, o)) 1021 != NULL) 1022 if (strcmp(owner, cp)) 1023 (void) strlcpy(lowner, cp, 1024 sizeof(lowner)); 1025 } 1026 if (!IS_ON(opts, DO_NOCHKGROUP) && group) { 1027 int o; 1028 1029 o = (group[0] == ':') ? opts & DO_NUMCHKGROUP : 1030 opts; 1031 if ((cp = getgroupname(stb.st_gid, target, o)) 1032 != NULL) 1033 if (strcmp(group, cp)) 1034 (void) strlcpy(lgroup, cp, 1035 sizeof(lgroup)); 1036 } 1037 1038 /* 1039 * Need to set owner and/or group 1040 */ 1041 #define PRN(n) ((n[0] == ':') ? n+1 : n) 1042 if (lowner[0] != CNULL || lgroup[0] != CNULL) { 1043 if (lowner[0] == CNULL && 1044 (cp = getusername(stb.st_uid, 1045 target, opts))) 1046 (void) strlcpy(lowner, cp, 1047 sizeof(lowner)); 1048 if (lgroup[0] == CNULL && 1049 (cp = getgroupname(stb.st_gid, 1050 target, opts))) 1051 (void) strlcpy(lgroup, cp, 1052 sizeof(lgroup)); 1053 1054 if (IS_ON(opts, DO_VERIFY)) 1055 message(MT_NOTICE, 1056 "%s: need to chown from %s:%s to %s:%s", 1057 target, 1058 PRN(lowner), PRN(lgroup), 1059 PRN(owner), PRN(group)); 1060 else { 1061 if (fchog(-1, target, owner, 1062 group, -1) == 0) 1063 message(MT_NOTICE, 1064 "%s: chown from %s:%s to %s:%s", 1065 target, 1066 PRN(lowner), 1067 PRN(lgroup), 1068 PRN(owner), 1069 PRN(group)); 1070 } 1071 } 1072 #undef PRN 1073 ack(); 1074 return; 1075 } 1076 } 1077 1078 if (IS_ON(opts, DO_VERIFY)) { 1079 ack(); 1080 return; 1081 } 1082 1083 /* 1084 * Create the directory 1085 */ 1086 if (s < 0) { 1087 if (errno == ENOENT) { 1088 if (mkdir(target, mode) == 0 || 1089 (chkparent(target, opts) == 0 && 1090 mkdir(target, mode) == 0)) { 1091 message(MT_NOTICE, "%s: mkdir", target); 1092 (void) fchog(-1, target, owner, group, mode); 1093 ack(); 1094 } else { 1095 error("%s: mkdir failed: %s", target, SYSERR); 1096 ptarget = sptarget[--catname]; 1097 *ptarget = CNULL; 1098 } 1099 return; 1100 } 1101 } 1102 error("%s: lstat failed: %s", target, SYSERR); 1103 ptarget = sptarget[--catname]; 1104 *ptarget = CNULL; 1105 } 1106 1107 /* 1108 * Receive a link 1109 */ 1110 static void 1111 recvlink(char *new, opt_t opts, int mode, off_t size) 1112 { 1113 char tbuf[MAXPATHLEN], dbuf[BUFSIZ]; 1114 struct stat stb; 1115 char *optarget; 1116 int uptodate; 1117 off_t i; 1118 1119 /* 1120 * Read basic link info 1121 */ 1122 ack(); 1123 (void) remline(buf, sizeof(buf), TRUE); 1124 1125 if (response() < 0) { 1126 err(); 1127 return; 1128 } 1129 1130 if (DECODE(dbuf, buf) == -1) { 1131 error("recvlink: cannot decode symlink target"); 1132 return; 1133 } 1134 1135 uptodate = 0; 1136 if ((i = readlink(target, tbuf, sizeof(tbuf)-1)) != -1) { 1137 tbuf[i] = '\0'; 1138 if (i == size && strncmp(dbuf, tbuf, (int) size) == 0) 1139 uptodate = 1; 1140 } 1141 mode &= 0777; 1142 1143 if (IS_ON(opts, DO_VERIFY) || uptodate) { 1144 if (uptodate) 1145 message(MT_REMOTE|MT_INFO, ""); 1146 else 1147 message(MT_REMOTE|MT_INFO, "%s: need to update", 1148 target); 1149 if (IS_ON(opts, DO_COMPARE)) 1150 return; 1151 (void) sendcmd(C_END, NULL); 1152 (void) response(); 1153 return; 1154 } 1155 1156 /* 1157 * Make new symlink using a temporary name 1158 */ 1159 if (mktemp(new) == NULL || symlink(dbuf, new) < 0) { 1160 if (errno != ENOENT || chkparent(new, opts) < 0 || 1161 mktemp(new) == NULL || symlink(dbuf, new) < 0) { 1162 error("%s -> %s: symlink failed: %s", new, dbuf, 1163 SYSERR); 1164 return; 1165 } 1166 } 1167 1168 /* 1169 * See if target is a directory and remove it if it is 1170 */ 1171 if (lstat(target, &stb) == 0) { 1172 if (S_ISDIR(stb.st_mode)) { 1173 optarget = ptarget; 1174 for (ptarget = target; *ptarget; ptarget++); 1175 if (removefile(&stb, 0) < 0) { 1176 ptarget = optarget; 1177 (void) unlink(new); 1178 (void) sendcmd(C_END, NULL); 1179 (void) response(); 1180 return; 1181 } 1182 ptarget = optarget; 1183 } 1184 } 1185 1186 /* 1187 * Install link as the target 1188 */ 1189 if (rename(new, target) < 0) { 1190 error("%s -> %s: symlink rename failed: %s", 1191 new, target, SYSERR); 1192 (void) unlink(new); 1193 (void) sendcmd(C_END, NULL); 1194 (void) response(); 1195 return; 1196 } 1197 1198 message(MT_REMOTE|MT_CHANGE, "%s: updated", target); 1199 1200 /* 1201 * Indicate end of receive operation 1202 */ 1203 (void) sendcmd(C_END, NULL); 1204 (void) response(); 1205 } 1206 1207 /* 1208 * Creat a hard link to existing file. 1209 */ 1210 static void 1211 hardlink(char *cmd) 1212 { 1213 struct stat stb; 1214 int exists = 0; 1215 char *xoldname, *xnewname; 1216 char *cp = cmd; 1217 static char expbuf[BUFSIZ]; 1218 char oldname[BUFSIZ], newname[BUFSIZ]; 1219 1220 /* Skip over opts */ 1221 (void) strtol(cp, &cp, 8); 1222 if (*cp++ != ' ') { 1223 error("hardlink: options not delimited"); 1224 return; 1225 } 1226 1227 xoldname = strtok(cp, " "); 1228 if (xoldname == NULL) { 1229 error("hardlink: oldname name not delimited"); 1230 return; 1231 } 1232 1233 if (DECODE(oldname, xoldname) == -1) { 1234 error("hardlink: Cannot decode oldname"); 1235 return; 1236 } 1237 1238 xnewname = strtok(NULL, " "); 1239 if (xnewname == NULL) { 1240 error("hardlink: new name not specified"); 1241 return; 1242 } 1243 1244 if (DECODE(newname, xnewname) == -1) { 1245 error("hardlink: Cannot decode newname"); 1246 return; 1247 } 1248 1249 if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL) { 1250 error("hardlink: tilde expansion failed"); 1251 return; 1252 } 1253 1254 if (catname && cattarget(newname) < 0) { 1255 error("Cannot set newname target."); 1256 return; 1257 } 1258 1259 if (lstat(target, &stb) == 0) { 1260 int mode = stb.st_mode & S_IFMT; 1261 1262 if (mode != S_IFREG && mode != S_IFLNK) { 1263 error("%s: not a regular file", target); 1264 return; 1265 } 1266 exists = 1; 1267 } 1268 1269 if (chkparent(target, options) < 0 ) { 1270 error("%s: no parent: %s ", target, SYSERR); 1271 return; 1272 } 1273 if (exists && (unlink(target) < 0)) { 1274 error("%s: unlink failed: %s", target, SYSERR); 1275 return; 1276 } 1277 if (link(expbuf, target) < 0) { 1278 error("%s: cannot link to %s: %s", target, oldname, SYSERR); 1279 return; 1280 } 1281 ack(); 1282 } 1283 1284 /* 1285 * Set configuration information. 1286 * 1287 * A key letter is followed immediately by the value 1288 * to set. The keys are: 1289 * SC_FREESPACE - Set minimium free space of filesystem 1290 * SC_FREEFILES - Set minimium free number of files of filesystem 1291 */ 1292 static void 1293 setconfig(char *cmd) 1294 { 1295 char *cp = cmd; 1296 char *estr; 1297 const char *errstr; 1298 1299 switch (*cp++) { 1300 case SC_HOSTNAME: /* Set hostname */ 1301 /* 1302 * Only use info if we don't know who this is. 1303 */ 1304 if (!fromhost) { 1305 fromhost = xstrdup(cp); 1306 message(MT_SYSLOG, "startup for %s", fromhost); 1307 setproctitle("serving %s", cp); 1308 } 1309 break; 1310 1311 case SC_FREESPACE: /* Minimium free space */ 1312 min_freespace = (int64_t)strtonum(cp, 0, LLONG_MAX, &errstr); 1313 if (errstr) 1314 fatalerr("Minimum free space is %s: '%s'", errstr, 1315 optarg); 1316 break; 1317 1318 case SC_FREEFILES: /* Minimium free files */ 1319 min_freefiles = (int64_t)strtonum(cp, 0, LLONG_MAX, &errstr); 1320 if (errstr) 1321 fatalerr("Minimum free files is %s: '%s'", errstr, 1322 optarg); 1323 break; 1324 1325 case SC_LOGGING: /* Logging options */ 1326 if ((estr = msgparseopts(cp, TRUE)) != NULL) { 1327 fatalerr("Bad message option string (%s): %s", 1328 cp, estr); 1329 return; 1330 } 1331 break; 1332 1333 case SC_DEFOWNER: 1334 (void) strlcpy(defowner, cp, sizeof(defowner)); 1335 break; 1336 1337 case SC_DEFGROUP: 1338 (void) strlcpy(defgroup, cp, sizeof(defgroup)); 1339 break; 1340 1341 default: 1342 message(MT_NOTICE, "Unknown config command \"%s\".", cp-1); 1343 return; 1344 } 1345 } 1346 1347 /* 1348 * Receive something 1349 */ 1350 static void 1351 recvit(char *cmd, int type) 1352 { 1353 int mode; 1354 opt_t opts; 1355 off_t size; 1356 time_t mtime, atime; 1357 char *owner, *group, *file; 1358 char new[MAXPATHLEN]; 1359 char fileb[MAXPATHLEN]; 1360 int64_t freespace = -1, freefiles = -1; 1361 char *cp = cmd; 1362 1363 /* 1364 * Get rdist option flags 1365 */ 1366 opts = strtol(cp, &cp, 8); 1367 if (*cp++ != ' ') { 1368 error("recvit: options not delimited"); 1369 return; 1370 } 1371 1372 /* 1373 * Get file mode 1374 */ 1375 mode = strtol(cp, &cp, 8); 1376 if (*cp++ != ' ') { 1377 error("recvit: mode not delimited"); 1378 return; 1379 } 1380 1381 /* 1382 * Get file size 1383 */ 1384 size = (off_t) strtoll(cp, &cp, 10); 1385 if (*cp++ != ' ') { 1386 error("recvit: size not delimited"); 1387 return; 1388 } 1389 1390 /* 1391 * Get modification time 1392 */ 1393 mtime = (time_t) strtoll(cp, &cp, 10); 1394 if (*cp++ != ' ') { 1395 error("recvit: mtime not delimited"); 1396 return; 1397 } 1398 1399 /* 1400 * Get access time 1401 */ 1402 atime = (time_t) strtoll(cp, &cp, 10); 1403 if (*cp++ != ' ') { 1404 error("recvit: atime not delimited"); 1405 return; 1406 } 1407 1408 /* 1409 * Get file owner name 1410 */ 1411 owner = strtok(cp, " "); 1412 if (owner == NULL) { 1413 error("recvit: owner name not delimited"); 1414 return; 1415 } 1416 1417 /* 1418 * Get file group name 1419 */ 1420 group = strtok(NULL, " "); 1421 if (group == NULL) { 1422 error("recvit: group name not delimited"); 1423 return; 1424 } 1425 1426 /* 1427 * Get file name. Can't use strtok() since there could 1428 * be white space in the file name. 1429 */ 1430 if (DECODE(fileb, group + strlen(group) + 1) == -1) { 1431 error("recvit: Cannot decode file name"); 1432 return; 1433 } 1434 1435 if (fileb[0] == '\0') { 1436 error("recvit: no file name"); 1437 return; 1438 } 1439 file = fileb; 1440 1441 debugmsg(DM_MISC, 1442 "recvit: opts = %04lo mode = %04o size = %lld mtime = %lld", 1443 opts, mode, (long long) size, (long long)mtime); 1444 debugmsg(DM_MISC, 1445 "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d", 1446 owner, group, file, catname, (type == S_IFDIR) ? 1 : 0); 1447 1448 if (type == S_IFDIR) { 1449 if ((size_t) catname >= sizeof(sptarget)) { 1450 error("%s: too many directory levels", target); 1451 return; 1452 } 1453 sptarget[catname] = ptarget; 1454 if (catname++) { 1455 *ptarget++ = '/'; 1456 while ((*ptarget++ = *file++) != '\0') 1457 continue; 1458 ptarget--; 1459 } 1460 } else { 1461 /* 1462 * Create name of temporary file 1463 */ 1464 if (catname && cattarget(file) < 0) { 1465 error("Cannot set file name."); 1466 return; 1467 } 1468 file = strrchr(target, '/'); 1469 if (file == NULL) 1470 (void) strlcpy(new, tempname, sizeof(new)); 1471 else if (file == target) 1472 (void) snprintf(new, sizeof(new), "/%s", tempname); 1473 else { 1474 *file = CNULL; 1475 (void) snprintf(new, sizeof(new), "%s/%s", target, 1476 tempname); 1477 *file = '/'; 1478 } 1479 } 1480 1481 /* 1482 * Check to see if there is enough free space and inodes 1483 * to install this file. 1484 */ 1485 if (min_freespace || min_freefiles) { 1486 /* Convert file size to kilobytes */ 1487 int64_t fsize = (int64_t)size / 1024; 1488 1489 if (getfilesysinfo(target, &freespace, &freefiles) != 0) 1490 return; 1491 1492 /* 1493 * filesystem values < 0 indicate unsupported or unavailable 1494 * information. 1495 */ 1496 if (min_freespace && (freespace >= 0) && 1497 (freespace - fsize < min_freespace)) { 1498 error( 1499 "%s: Not enough free space on filesystem: min %lld " 1500 "free %lld", target, min_freespace, freespace); 1501 return; 1502 } 1503 if (min_freefiles && (freefiles >= 0) && 1504 (freefiles - 1 < min_freefiles)) { 1505 error( 1506 "%s: Not enough free files on filesystem: min %lld free " 1507 "%lld", target, min_freefiles, freefiles); 1508 return; 1509 } 1510 } 1511 1512 /* 1513 * Call appropriate receive function to receive file 1514 */ 1515 switch (type) { 1516 case S_IFDIR: 1517 recvdir(opts, mode, owner, group); 1518 break; 1519 1520 case S_IFLNK: 1521 recvlink(new, opts, mode, size); 1522 break; 1523 1524 case S_IFREG: 1525 recvfile(new, opts, mode, owner, group, mtime, atime, size); 1526 break; 1527 1528 default: 1529 error("%d: unknown file type", type); 1530 break; 1531 } 1532 } 1533 1534 /* 1535 * Chmog something 1536 */ 1537 static void 1538 dochmog(char *cmd) 1539 { 1540 int mode; 1541 opt_t opts; 1542 char *owner, *group, *file; 1543 char *cp = cmd; 1544 char fileb[MAXPATHLEN]; 1545 1546 /* 1547 * Get rdist option flags 1548 */ 1549 opts = strtol(cp, &cp, 8); 1550 if (*cp++ != ' ') { 1551 error("dochmog: options not delimited"); 1552 return; 1553 } 1554 1555 /* 1556 * Get file mode 1557 */ 1558 mode = strtol(cp, &cp, 8); 1559 if (*cp++ != ' ') { 1560 error("dochmog: mode not delimited"); 1561 return; 1562 } 1563 1564 /* 1565 * Get file owner name 1566 */ 1567 owner = strtok(cp, " "); 1568 if (owner == NULL) { 1569 error("dochmog: owner name not delimited"); 1570 return; 1571 } 1572 1573 /* 1574 * Get file group name 1575 */ 1576 group = strtok(NULL, " "); 1577 if (group == NULL) { 1578 error("dochmog: group name not delimited"); 1579 return; 1580 } 1581 1582 /* 1583 * Get file name. Can't use strtok() since there could 1584 * be white space in the file name. 1585 */ 1586 if (DECODE(fileb, group + strlen(group) + 1) == -1) { 1587 error("dochmog: Cannot decode file name"); 1588 return; 1589 } 1590 1591 if (fileb[0] == '\0') { 1592 error("dochmog: no file name"); 1593 return; 1594 } 1595 file = fileb; 1596 1597 debugmsg(DM_MISC, 1598 "dochmog: opts = %04lo mode = %04o", opts, mode); 1599 debugmsg(DM_MISC, 1600 "dochmog: owner = '%s' group = '%s' file = '%s' catname = %d", 1601 owner, group, file, catname); 1602 1603 if (catname && cattarget(file) < 0) { 1604 error("Cannot set newname target."); 1605 return; 1606 } 1607 1608 (void) fchog(-1, target, owner, group, mode); 1609 1610 ack(); 1611 } 1612 1613 /* 1614 * Set target information 1615 */ 1616 static void 1617 settarget(char *cmd, int isdir) 1618 { 1619 char *cp = cmd; 1620 opt_t opts; 1621 char file[BUFSIZ]; 1622 1623 catname = isdir; 1624 1625 /* 1626 * Parse options for this target 1627 */ 1628 opts = strtol(cp, &cp, 8); 1629 if (*cp++ != ' ') { 1630 error("settarget: options not delimited"); 1631 return; 1632 } 1633 options = opts; 1634 1635 if (DECODE(file, cp) == -1) { 1636 error("settarget: Cannot decode target name"); 1637 return; 1638 } 1639 1640 /* 1641 * Handle target 1642 */ 1643 if (exptilde(target, cp, sizeof(target)) == NULL) 1644 return; 1645 ptarget = target; 1646 while (*ptarget) 1647 ptarget++; 1648 1649 ack(); 1650 } 1651 1652 /* 1653 * Cleanup in preparation for exiting. 1654 */ 1655 void 1656 cleanup(int dummy) 1657 { 1658 /* We don't need to do anything */ 1659 } 1660 1661 /* 1662 * Server routine to read requests and process them. 1663 */ 1664 void 1665 server(void) 1666 { 1667 static char cmdbuf[BUFSIZ]; 1668 char *cp; 1669 int n; 1670 extern jmp_buf finish_jmpbuf; 1671 1672 if (setjmp(finish_jmpbuf)) 1673 return; 1674 (void) signal(SIGHUP, sighandler); 1675 (void) signal(SIGINT, sighandler); 1676 (void) signal(SIGQUIT, sighandler); 1677 (void) signal(SIGTERM, sighandler); 1678 (void) signal(SIGPIPE, sighandler); 1679 (void) umask(oumask = umask(0)); 1680 (void) strlcpy(tempname, _RDIST_TMP, sizeof(tempname)); 1681 if (fromhost) { 1682 message(MT_SYSLOG, "Startup for %s", fromhost); 1683 #if defined(SETARGS) 1684 setproctitle("Serving %s", fromhost); 1685 #endif /* SETARGS */ 1686 } 1687 1688 /* 1689 * Let client know we want it to send it's version number 1690 */ 1691 (void) sendcmd(S_VERSION, NULL); 1692 1693 if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) { 1694 error("server: expected control record"); 1695 return; 1696 } 1697 1698 if (cmdbuf[0] != S_VERSION || !isdigit((unsigned char)cmdbuf[1])) { 1699 error("Expected version command, received: \"%s\".", cmdbuf); 1700 return; 1701 } 1702 1703 proto_version = atoi(&cmdbuf[1]); 1704 if (proto_version != VERSION) { 1705 error("Protocol version %d is not supported.", proto_version); 1706 return; 1707 } 1708 1709 /* Version number is okay */ 1710 ack(); 1711 1712 /* 1713 * Main command loop 1714 */ 1715 for ( ; ; ) { 1716 n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE); 1717 if (n == -1) /* EOF */ 1718 return; 1719 if (n == 0) { 1720 error("server: expected control record"); 1721 continue; 1722 } 1723 1724 switch (*cp++) { 1725 case C_SETCONFIG: /* Configuration info */ 1726 setconfig(cp); 1727 ack(); 1728 continue; 1729 1730 case C_DIRTARGET: /* init target file/directory name */ 1731 settarget(cp, TRUE); 1732 continue; 1733 1734 case C_TARGET: /* init target file/directory name */ 1735 settarget(cp, FALSE); 1736 continue; 1737 1738 case C_RECVREG: /* Transfer a regular file. */ 1739 recvit(cp, S_IFREG); 1740 continue; 1741 1742 case C_RECVDIR: /* Transfer a directory. */ 1743 recvit(cp, S_IFDIR); 1744 continue; 1745 1746 case C_RECVSYMLINK: /* Transfer symbolic link. */ 1747 recvit(cp, S_IFLNK); 1748 continue; 1749 1750 case C_RECVHARDLINK: /* Transfer hard link. */ 1751 hardlink(cp); 1752 continue; 1753 1754 case C_END: /* End of transfer */ 1755 *ptarget = CNULL; 1756 if (catname <= 0) { 1757 error("server: too many '%c's", C_END); 1758 continue; 1759 } 1760 ptarget = sptarget[--catname]; 1761 *ptarget = CNULL; 1762 ack(); 1763 continue; 1764 1765 case C_CLEAN: /* Clean. Cleanup a directory */ 1766 clean(cp); 1767 continue; 1768 1769 case C_QUERY: /* Query file/directory */ 1770 query(cp); 1771 continue; 1772 1773 case C_SPECIAL: /* Special. Execute commands */ 1774 dospecial(cp); 1775 continue; 1776 1777 case C_CMDSPECIAL: /* Cmd Special. Execute commands */ 1778 docmdspecial(); 1779 continue; 1780 1781 case C_CHMOG: /* Set owner, group, mode */ 1782 dochmog(cp); 1783 continue; 1784 1785 case C_ERRMSG: /* Normal error message */ 1786 if (cp && *cp) 1787 message(MT_NERROR|MT_NOREMOTE, "%s", cp); 1788 continue; 1789 1790 case C_FERRMSG: /* Fatal error message */ 1791 if (cp && *cp) 1792 message(MT_FERROR|MT_NOREMOTE, "%s", cp); 1793 return; 1794 1795 default: 1796 error("server: unknown command '%s'", cp - 1); 1797 case CNULL: 1798 continue; 1799 } 1800 } 1801 } 1802