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