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