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