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