1: /*
   2:  * Copyright (c) 1983 Regents of the University of California.
   3:  * All rights reserved.  The Berkeley software License Agreement
   4:  * specifies the terms and conditions for redistribution.
   5:  */
   6: 
   7: #ifndef lint
   8: static char sccsid[] = "@(#)server.c	5.3 (Berkeley) 6/7/86";
   9: #endif not lint
  10: 
  11: #include "defs.h"
  12: 
  13: #define ack()   (void) write(rem, "\0\n", 2)
  14: #define err()   (void) write(rem, "\1\n", 2)
  15: 
  16: struct  linkbuf *ihead;     /* list of files with more than one link */
  17: char    buf[BUFSIZ];        /* general purpose buffer */
  18: char    target[BUFSIZ];     /* target/source directory name */
  19: char    *tp;            /* pointer to end of target name */
  20: char    *Tdest;         /* pointer to last T dest*/
  21: int catname;        /* cat name to target name */
  22: char    *stp[32];       /* stack of saved tp's for directories */
  23: int oumask;         /* old umask for creating files */
  24: 
  25: extern  FILE *lfp;      /* log file for mailing changes */
  26: 
  27: int cleanup();
  28: struct  linkbuf *savelink();
  29: 
  30: /*
  31:  * Server routine to read requests and process them.
  32:  * Commands are:
  33:  *	Tname	- Transmit file if out of date
  34:  *	Vname	- Verify if file out of date or not
  35:  *	Qname	- Query if file exists. Return mtime & size if it does.
  36:  */
  37: server()
  38: {
  39:     char cmdbuf[BUFSIZ];
  40:     register char *cp;
  41: 
  42:     signal(SIGHUP, cleanup);
  43:     signal(SIGINT, cleanup);
  44:     signal(SIGQUIT, cleanup);
  45:     signal(SIGTERM, cleanup);
  46:     signal(SIGPIPE, cleanup);
  47: 
  48:     rem = 0;
  49:     oumask = umask(0);
  50:     (void) sprintf(buf, "V%d\n", VERSION);
  51:     (void) write(rem, buf, strlen(buf));
  52: 
  53:     for (;;) {
  54:         cp = cmdbuf;
  55:         if (read(rem, cp, 1) <= 0)
  56:             return;
  57:         if (*cp++ == '\n') {
  58:             error("server: expected control record\n");
  59:             continue;
  60:         }
  61:         do {
  62:             if (read(rem, cp, 1) != 1)
  63:                 cleanup();
  64:         } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
  65:         *--cp = '\0';
  66:         cp = cmdbuf;
  67:         switch (*cp++) {
  68:         case 'T':  /* init target file/directory name */
  69:             catname = 1;    /* target should be directory */
  70:             goto dotarget;
  71: 
  72:         case 't':  /* init target file/directory name */
  73:             catname = 0;
  74:         dotarget:
  75:             if (exptilde(target, cp) == NULL)
  76:                 continue;
  77:             tp = target;
  78:             while (*tp)
  79:                 tp++;
  80:             ack();
  81:             continue;
  82: 
  83:         case 'R':  /* Transfer a regular file. */
  84:             recvf(cp, S_IFREG);
  85:             continue;
  86: 
  87:         case 'D':  /* Transfer a directory. */
  88:             recvf(cp, S_IFDIR);
  89:             continue;
  90: 
  91:         case 'K':  /* Transfer symbolic link. */
  92:             recvf(cp, S_IFLNK);
  93:             continue;
  94: 
  95:         case 'k':  /* Transfer hard link. */
  96:             hardlink(cp);
  97:             continue;
  98: 
  99:         case 'E':  /* End. (of directory) */
 100:             *tp = '\0';
 101:             if (catname <= 0) {
 102:                 error("server: too many 'E's\n");
 103:                 continue;
 104:             }
 105:             tp = stp[--catname];
 106:             *tp = '\0';
 107:             ack();
 108:             continue;
 109: 
 110:         case 'C':  /* Clean. Cleanup a directory */
 111:             clean(cp);
 112:             continue;
 113: 
 114:         case 'Q':  /* Query. Does the file/directory exist? */
 115:             query(cp);
 116:             continue;
 117: 
 118:         case 'S':  /* Special. Execute commands */
 119:             dospecial(cp);
 120:             continue;
 121: 
 122: #ifdef notdef
 123:         /*
 124: 		 * These entries are reserved but not currently used.
 125: 		 * The intent is to allow remote hosts to have master copies.
 126: 		 * Currently, only the host rdist runs on can have masters.
 127: 		 */
 128:         case 'X':  /* start a new list of files to exclude */
 129:             except = bp = NULL;
 130:         case 'x':  /* add name to list of files to exclude */
 131:             if (*cp == '\0') {
 132:                 ack();
 133:                 continue;
 134:             }
 135:             if (*cp == '~') {
 136:                 if (exptilde(buf, cp) == NULL)
 137:                     continue;
 138:                 cp = buf;
 139:             }
 140:             if (bp == NULL)
 141:                 except = bp = expand(makeblock(NAME, cp), E_VARS);
 142:             else
 143:                 bp->b_next = expand(makeblock(NAME, cp), E_VARS);
 144:             while (bp->b_next != NULL)
 145:                 bp = bp->b_next;
 146:             ack();
 147:             continue;
 148: 
 149:         case 'I':  /* Install. Transfer file if out of date. */
 150:             opts = 0;
 151:             while (*cp >= '0' && *cp <= '7')
 152:                 opts = (opts << 3) | (*cp++ - '0');
 153:             if (*cp++ != ' ') {
 154:                 error("server: options not delimited\n");
 155:                 return;
 156:             }
 157:             install(cp, opts);
 158:             continue;
 159: 
 160:         case 'L':  /* Log. save message in log file */
 161:             log(lfp, cp);
 162:             continue;
 163: #endif
 164: 
 165:         case '\1':
 166:             nerrs++;
 167:             continue;
 168: 
 169:         case '\2':
 170:             return;
 171: 
 172:         default:
 173:             error("server: unknown command '%s'\n", cp);
 174:         case '\0':
 175:             continue;
 176:         }
 177:     }
 178: }
 179: 
 180: /*
 181:  * Update the file(s) if they are different.
 182:  * destdir = 1 if destination should be a directory
 183:  * (i.e., more than one source is being copied to the same destination).
 184:  */
 185: install(src, dest, destdir, opts)
 186:     char *src, *dest;
 187:     int destdir, opts;
 188: {
 189:     char *rname;
 190:     char destcopy[BUFSIZ];
 191: 
 192:     if (dest == NULL) {
 193:         opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
 194:         dest = src;
 195:     }
 196: 
 197:     if (nflag || debug) {
 198:         printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
 199:             opts & WHOLE ? " -w" : "",
 200:             opts & YOUNGER ? " -y" : "",
 201:             opts & COMPARE ? " -b" : "",
 202:             opts & REMOVE ? " -R" : "", src, dest);
 203:         if (nflag)
 204:             return;
 205:     }
 206: 
 207:     rname = exptilde(target, src);
 208:     if (rname == NULL)
 209:         return;
 210:     tp = target;
 211:     while (*tp)
 212:         tp++;
 213:     /*
 214: 	 * If we are renaming a directory and we want to preserve
 215: 	 * the directory heirarchy (-w), we must strip off the leading
 216: 	 * directory name and preserve the rest.
 217: 	 */
 218:     if (opts & WHOLE) {
 219:         while (*rname == '/')
 220:             rname++;
 221:         destdir = 1;
 222:     } else {
 223:         rname = rindex(target, '/');
 224:         if (rname == NULL)
 225:             rname = target;
 226:         else
 227:             rname++;
 228:     }
 229:     if (debug)
 230:         printf("target = %s, rname = %s\n", target, rname);
 231:     /*
 232: 	 * Pass the destination file/directory name to remote.
 233: 	 */
 234:     (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
 235:     if (debug)
 236:         printf("buf = %s", buf);
 237:     (void) write(rem, buf, strlen(buf));
 238:     if (response() < 0)
 239:         return;
 240: 
 241:     if (destdir) {
 242:         strcpy(destcopy, dest);
 243:         Tdest = destcopy;
 244:     }
 245:     sendf(rname, opts);
 246:     Tdest = 0;
 247: }
 248: 
 249: #define protoname() (pw ? pw->pw_name : user)
 250: #define protogroup() (gr ? gr->gr_name : group)
 251: /*
 252:  * Transfer the file or directory in target[].
 253:  * rname is the name of the file on the remote host.
 254:  */
 255: sendf(rname, opts)
 256:     char *rname;
 257:     int opts;
 258: {
 259:     register struct subcmd *sc;
 260:     struct stat stb;
 261:     int sizerr, f, u, len;
 262:     off_t i;
 263:     DIR *d;
 264:     struct direct *dp;
 265:     char *otp, *cp;
 266:     extern struct subcmd *subcmds;
 267:     static char user[15], group[15];
 268: 
 269:     if (debug)
 270:         printf("sendf(%s, %x)\n", rname, opts);
 271: 
 272:     if (except(target))
 273:         return;
 274:     if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
 275:         error("%s: %s\n", target, sys_errlist[errno]);
 276:         return;
 277:     }
 278:     if ((u = update(rname, opts, &stb)) == 0) {
 279:         if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
 280:             (void) savelink(&stb);
 281:         return;
 282:     }
 283: 
 284:     if (pw == NULL || pw->pw_uid != stb.st_uid)
 285:         if ((pw = getpwuid(stb.st_uid)) == NULL) {
 286:             log(lfp, "%s: no password entry for uid \n", target);
 287:             pw = NULL;
 288:             sprintf(user, ":%d", stb.st_uid);
 289:         }
 290:     if (gr == NULL || gr->gr_gid != stb.st_gid)
 291:         if ((gr = getgrgid(stb.st_gid)) == NULL) {
 292:             log(lfp, "%s: no name for group %d\n", target);
 293:             gr = NULL;
 294:             sprintf(group, ":%d", stb.st_gid);
 295:         }
 296:     if (u == 1) {
 297:         if (opts & VERIFY) {
 298:             log(lfp, "need to install: %s\n", target);
 299:             goto dospecial;
 300:         }
 301:         log(lfp, "installing: %s\n", target);
 302:         opts &= ~(COMPARE|REMOVE);
 303:     }
 304: 
 305:     switch (stb.st_mode & S_IFMT) {
 306:     case S_IFDIR:
 307:         if ((d = opendir(target)) == NULL) {
 308:             error("%s: %s\n", target, sys_errlist[errno]);
 309:             return;
 310:         }
 311:         (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
 312:             stb.st_mode & 07777, protoname(), protogroup(), rname);
 313:         if (debug)
 314:             printf("buf = %s", buf);
 315:         (void) write(rem, buf, strlen(buf));
 316:         if (response() < 0) {
 317:             closedir(d);
 318:             return;
 319:         }
 320: 
 321:         if (opts & REMOVE)
 322:             rmchk(opts);
 323: 
 324:         otp = tp;
 325:         len = tp - target;
 326:         while (dp = readdir(d)) {
 327:             if (!strcmp(dp->d_name, ".") ||
 328:                 !strcmp(dp->d_name, ".."))
 329:                 continue;
 330:             if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
 331:                 error("%s/%s: Name too long\n", target,
 332:                     dp->d_name);
 333:                 continue;
 334:             }
 335:             tp = otp;
 336:             *tp++ = '/';
 337:             cp = dp->d_name;
 338:             while (*tp++ = *cp++)
 339:                 ;
 340:             tp--;
 341:             sendf(dp->d_name, opts);
 342:         }
 343:         closedir(d);
 344:         (void) write(rem, "E\n", 2);
 345:         (void) response();
 346:         tp = otp;
 347:         *tp = '\0';
 348:         return;
 349: 
 350:     case S_IFLNK:
 351:         if (u != 1)
 352:             opts |= COMPARE;
 353:         if (stb.st_nlink > 1) {
 354:             struct linkbuf *lp;
 355: 
 356:             if ((lp = savelink(&stb)) != NULL) {
 357:                 /* install link */
 358:                 if (*lp->target == 0)
 359:                 (void) sprintf(buf, "k%o %s %s\n", opts,
 360:                     lp->pathname, rname);
 361:                 else
 362:                 (void) sprintf(buf, "k%o %s/%s %s\n", opts,
 363:                     lp->target, lp->pathname, rname);
 364:                 if (debug)
 365:                     printf("buf = %s", buf);
 366:                 (void) write(rem, buf, strlen(buf));
 367:                 (void) response();
 368:                 return;
 369:             }
 370:         }
 371:         (void) sprintf(buf, "K%o %o %ld %ld %s %s %s\n", opts,
 372:             stb.st_mode & 07777, stb.st_size, stb.st_mtime,
 373:             protoname(), protogroup(), rname);
 374:         if (debug)
 375:             printf("buf = %s", buf);
 376:         (void) write(rem, buf, strlen(buf));
 377:         if (response() < 0)
 378:             return;
 379:         sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
 380:         (void) write(rem, buf, stb.st_size);
 381:         if (debug)
 382:             printf("readlink = %.*s\n", stb.st_size, buf);
 383:         goto done;
 384: 
 385:     case S_IFREG:
 386:         break;
 387: 
 388:     default:
 389:         error("%s: not a file or directory\n", target);
 390:         return;
 391:     }
 392: 
 393:     if (u == 2) {
 394:         if (opts & VERIFY) {
 395:             log(lfp, "need to update: %s\n", target);
 396:             goto dospecial;
 397:         }
 398:         log(lfp, "updating: %s\n", target);
 399:     }
 400: 
 401:     if (stb.st_nlink > 1) {
 402:         struct linkbuf *lp;
 403: 
 404:         if ((lp = savelink(&stb)) != NULL) {
 405:             /* install link */
 406:             if (*lp->target == 0)
 407:             (void) sprintf(buf, "k%o %s %s\n", opts,
 408:                 lp->pathname, rname);
 409:             else
 410:             (void) sprintf(buf, "k%o %s/%s %s\n", opts,
 411:                 lp->target, lp->pathname, rname);
 412:             if (debug)
 413:                 printf("buf = %s", buf);
 414:             (void) write(rem, buf, strlen(buf));
 415:             (void) response();
 416:             return;
 417:         }
 418:     }
 419: 
 420:     if ((f = open(target, 0)) < 0) {
 421:         error("%s: %s\n", target, sys_errlist[errno]);
 422:         return;
 423:     }
 424:     (void) sprintf(buf, "R%o %o %ld %ld %s %s %s\n", opts,
 425:         stb.st_mode & 07777, stb.st_size, stb.st_mtime,
 426:         protoname(), protogroup(), rname);
 427:     if (debug)
 428:         printf("buf = %s", buf);
 429:     (void) write(rem, buf, strlen(buf));
 430:     if (response() < 0) {
 431:         (void) close(f);
 432:         return;
 433:     }
 434:     sizerr = 0;
 435:     for (i = 0; i < stb.st_size; i += BUFSIZ) {
 436:         int amt = BUFSIZ;
 437:         if (i + amt > stb.st_size)
 438:             amt = stb.st_size - i;
 439:         if (sizerr == 0 && read(f, buf, amt) != amt)
 440:             sizerr = 1;
 441:         (void) write(rem, buf, amt);
 442:     }
 443:     (void) close(f);
 444: done:
 445:     if (sizerr) {
 446:         error("%s: file changed size\n", target);
 447:         err();
 448:     } else
 449:         ack();
 450:     f = response();
 451:     if (f < 0 || f == 0 && (opts & COMPARE))
 452:         return;
 453: dospecial:
 454:     for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
 455:         if (sc->sc_type != SPECIAL)
 456:             continue;
 457:         if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
 458:             continue;
 459:         log(lfp, "special \"%s\"\n", sc->sc_name);
 460:         if (opts & VERIFY)
 461:             continue;
 462:         (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
 463:         if (debug)
 464:             printf("buf = %s", buf);
 465:         (void) write(rem, buf, strlen(buf));
 466:         while (response() > 0)
 467:             ;
 468:     }
 469: }
 470: 
 471: struct linkbuf *
 472: savelink(stp)
 473:     struct stat *stp;
 474: {
 475:     struct linkbuf *lp;
 476:     int found = 0;
 477: 
 478:     for (lp = ihead; lp != NULL; lp = lp->nextp)
 479:         if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
 480:             lp->count--;
 481:             return(lp);
 482:         }
 483:     lp = (struct linkbuf *) malloc(sizeof(*lp));
 484:     if (lp == NULL)
 485:         log(lfp, "out of memory, link information lost\n");
 486:     else {
 487:         lp->nextp = ihead;
 488:         ihead = lp;
 489:         lp->inum = stp->st_ino;
 490:         lp->devnum = stp->st_dev;
 491:         lp->count = stp->st_nlink - 1;
 492:         strcpy(lp->pathname, target);
 493:         if (Tdest)
 494:             strcpy(lp->target, Tdest);
 495:         else
 496:             *lp->target = 0;
 497:     }
 498:     return(NULL);
 499: }
 500: 
 501: /*
 502:  * Check to see if file needs to be updated on the remote machine.
 503:  * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
 504:  * and 3 if comparing binaries to determine if out of date.
 505:  */
 506: update(rname, opts, stp)
 507:     char *rname;
 508:     int opts;
 509:     struct stat *stp;
 510: {
 511:     register char *cp, *s;
 512:     register off_t size;
 513:     register time_t mtime;
 514: 
 515:     if (debug)
 516:         printf("update(%s, %x, %x)\n", rname, opts, stp);
 517: 
 518:     /*
 519: 	 * Check to see if the file exists on the remote machine.
 520: 	 */
 521:     (void) sprintf(buf, "Q%s\n", rname);
 522:     if (debug)
 523:         printf("buf = %s", buf);
 524:     (void) write(rem, buf, strlen(buf));
 525: again:
 526:     cp = s = buf;
 527:     do {
 528:         if (read(rem, cp, 1) != 1)
 529:             lostconn();
 530:     } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
 531: 
 532:     switch (*s++) {
 533:     case 'Y':
 534:         break;
 535: 
 536:     case 'N':  /* file doesn't exist so install it */
 537:         return(1);
 538: 
 539:     case '\1':
 540:         nerrs++;
 541:         if (*s != '\n') {
 542:             if (!iamremote) {
 543:                 fflush(stdout);
 544:                 (void) write(2, s, cp - s);
 545:             }
 546:             if (lfp != NULL)
 547:                 (void) fwrite(s, 1, cp - s, lfp);
 548:         }
 549:         return(0);
 550: 
 551:     case '\3':
 552:         *--cp = '\0';
 553:         if (lfp != NULL)
 554:             log(lfp, "update: note: %s\n", s);
 555:         goto again;
 556: 
 557:     default:
 558:         *--cp = '\0';
 559:         error("update: unexpected response '%s'\n", s);
 560:         return(0);
 561:     }
 562: 
 563:     if (*s == '\n')
 564:         return(2);
 565: 
 566:     if (opts & COMPARE)
 567:         return(3);
 568: 
 569:     size = 0;
 570:     while (isdigit(*s))
 571:         size = size * 10 + (*s++ - '0');
 572:     if (*s++ != ' ') {
 573:         error("update: size not delimited\n");
 574:         return(0);
 575:     }
 576:     mtime = 0;
 577:     while (isdigit(*s))
 578:         mtime = mtime * 10 + (*s++ - '0');
 579:     if (*s != '\n') {
 580:         error("update: mtime not delimited\n");
 581:         return(0);
 582:     }
 583:     /*
 584: 	 * File needs to be updated?
 585: 	 */
 586:     if (opts & YOUNGER) {
 587:         if (stp->st_mtime == mtime)
 588:             return(0);
 589:         if (stp->st_mtime < mtime) {
 590:             log(lfp, "Warning: %s: remote copy is newer\n", target);
 591:             return(0);
 592:         }
 593:     } else if (stp->st_mtime == mtime && stp->st_size == size)
 594:         return(0);
 595:     return(2);
 596: }
 597: 
 598: /*
 599:  * Query. Check to see if file exists. Return one of the following:
 600:  *	N\n		- doesn't exist
 601:  *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
 602:  *	Y\n		- exists and its a directory or symbolic link
 603:  *	^Aerror message\n
 604:  */
 605: query(name)
 606:     char *name;
 607: {
 608:     struct stat stb;
 609: 
 610:     if (catname)
 611:         (void) sprintf(tp, "/%s", name);
 612: 
 613:     if (lstat(target, &stb) < 0) {
 614:         if (errno == ENOENT)
 615:             (void) write(rem, "N\n", 2);
 616:         else
 617:             error("%s:%s: %s\n", host, target, sys_errlist[errno]);
 618:         *tp = '\0';
 619:         return;
 620:     }
 621: 
 622:     switch (stb.st_mode & S_IFMT) {
 623:     case S_IFREG:
 624:         (void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
 625:         (void) write(rem, buf, strlen(buf));
 626:         break;
 627: 
 628:     case S_IFLNK:
 629:     case S_IFDIR:
 630:         (void) write(rem, "Y\n", 2);
 631:         break;
 632: 
 633:     default:
 634:         error("%s: not a file or directory\n", name);
 635:         break;
 636:     }
 637:     *tp = '\0';
 638: }
 639: 
 640: recvf(cmd, type)
 641:     char *cmd;
 642:     int type;
 643: {
 644:     register char *cp;
 645:     int f, mode, opts, wrerr, olderrno;
 646:     off_t i, size;
 647:     time_t mtime;
 648:     struct stat stb;
 649:     struct timeval tvp[2];
 650:     char *owner, *group;
 651:     char new[BUFSIZ];
 652:     extern char *tmpname;
 653: 
 654:     cp = cmd;
 655:     opts = 0;
 656:     while (*cp >= '0' && *cp <= '7')
 657:         opts = (opts << 3) | (*cp++ - '0');
 658:     if (*cp++ != ' ') {
 659:         error("recvf: options not delimited\n");
 660:         return;
 661:     }
 662:     mode = 0;
 663:     while (*cp >= '0' && *cp <= '7')
 664:         mode = (mode << 3) | (*cp++ - '0');
 665:     if (*cp++ != ' ') {
 666:         error("recvf: mode not delimited\n");
 667:         return;
 668:     }
 669:     size = 0;
 670:     while (isdigit(*cp))
 671:         size = size * 10 + (*cp++ - '0');
 672:     if (*cp++ != ' ') {
 673:         error("recvf: size not delimited\n");
 674:         return;
 675:     }
 676:     mtime = 0;
 677:     while (isdigit(*cp))
 678:         mtime = mtime * 10 + (*cp++ - '0');
 679:     if (*cp++ != ' ') {
 680:         error("recvf: mtime not delimited\n");
 681:         return;
 682:     }
 683:     owner = cp;
 684:     while (*cp && *cp != ' ')
 685:         cp++;
 686:     if (*cp != ' ') {
 687:         error("recvf: owner name not delimited\n");
 688:         return;
 689:     }
 690:     *cp++ = '\0';
 691:     group = cp;
 692:     while (*cp && *cp != ' ')
 693:         cp++;
 694:     if (*cp != ' ') {
 695:         error("recvf: group name not delimited\n");
 696:         return;
 697:     }
 698:     *cp++ = '\0';
 699: 
 700:     if (type == S_IFDIR) {
 701:         if (catname >= sizeof(stp)) {
 702:             error("%s:%s: too many directory levels\n",
 703:                 host, target);
 704:             return;
 705:         }
 706:         stp[catname] = tp;
 707:         if (catname++) {
 708:             *tp++ = '/';
 709:             while (*tp++ = *cp++)
 710:                 ;
 711:             tp--;
 712:         }
 713:         if (opts & VERIFY) {
 714:             ack();
 715:             return;
 716:         }
 717:         if (lstat(target, &stb) == 0) {
 718:             if (ISDIR(stb.st_mode)) {
 719:                 if ((stb.st_mode & 07777) == mode) {
 720:                     ack();
 721:                     return;
 722:                 }
 723:                 buf[0] = '\0';
 724:                 (void) sprintf(buf + 1,
 725:                     "%s: Warning: remote mode %o != local mode %o\n",
 726:                     target, stb.st_mode & 07777, mode);
 727:                 (void) write(rem, buf, strlen(buf + 1) + 1);
 728:                 return;
 729:             }
 730:             errno = ENOTDIR;
 731:         } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
 732:             chkparent(target) == 0 && mkdir(target, mode) == 0)) {
 733:             if (chog(target, owner, group, mode) == 0)
 734:                 ack();
 735:             return;
 736:         }
 737:         error("%s:%s: %s\n", host, target, sys_errlist[errno]);
 738:         tp = stp[--catname];
 739:         *tp = '\0';
 740:         return;
 741:     }
 742: 
 743:     if (catname)
 744:         (void) sprintf(tp, "/%s", cp);
 745:     cp = rindex(target, '/');
 746:     if (cp == NULL)
 747:         strcpy(new, tmpname);
 748:     else if (cp == target)
 749:         (void) sprintf(new, "/%s", tmpname);
 750:     else {
 751:         *cp = '\0';
 752:         (void) sprintf(new, "%s/%s", target, tmpname);
 753:         *cp = '/';
 754:     }
 755: 
 756:     if (type == S_IFLNK) {
 757:         int j;
 758: 
 759:         ack();
 760:         cp = buf;
 761:         for (i = 0; i < size; i += j) {
 762:             if ((j = read(rem, cp, size - i)) <= 0)
 763:                 cleanup();
 764:             cp += j;
 765:         }
 766:         *cp = '\0';
 767:         if (response() < 0) {
 768:             err();
 769:             return;
 770:         }
 771:         if (symlink(buf, new) < 0) {
 772:             if (errno != ENOENT || chkparent(new) < 0 ||
 773:                 symlink(buf, new) < 0)
 774:                 goto badn;
 775:         }
 776:         mode &= 0777;
 777:         if (opts & COMPARE) {
 778:             char tbuf[BUFSIZ];
 779: 
 780:             if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
 781:                 i == size && strncmp(buf, tbuf, size) == 0) {
 782:                 (void) unlink(new);
 783:                 ack();
 784:                 return;
 785:             }
 786:             if (opts & VERIFY)
 787:                 goto differ;
 788:         }
 789:         goto fixup;
 790:     }
 791: 
 792:     if ((f = creat(new, mode)) < 0) {
 793:         if (errno != ENOENT || chkparent(new) < 0 ||
 794:             (f = creat(new, mode)) < 0)
 795:             goto badn;
 796:     }
 797: 
 798:     ack();
 799:     wrerr = 0;
 800:     for (i = 0; i < size; i += BUFSIZ) {
 801:         int amt = BUFSIZ;
 802: 
 803:         cp = buf;
 804:         if (i + amt > size)
 805:             amt = size - i;
 806:         do {
 807:             int j = read(rem, cp, amt);
 808: 
 809:             if (j <= 0) {
 810:                 (void) close(f);
 811:                 (void) unlink(new);
 812:                 cleanup();
 813:             }
 814:             amt -= j;
 815:             cp += j;
 816:         } while (amt > 0);
 817:         amt = BUFSIZ;
 818:         if (i + amt > size)
 819:             amt = size - i;
 820:         if (wrerr == 0 && write(f, buf, amt) != amt) {
 821:             olderrno = errno;
 822:             wrerr++;
 823:         }
 824:     }
 825:     (void) close(f);
 826:     if (response() < 0) {
 827:         err();
 828:         (void) unlink(new);
 829:         return;
 830:     }
 831:     if (wrerr) {
 832:         error("%s:%s: %s\n", host, new, sys_errlist[olderrno]);
 833:         (void) unlink(new);
 834:         return;
 835:     }
 836:     if (opts & COMPARE) {
 837:         FILE *f1, *f2;
 838:         int c;
 839: 
 840:         if ((f1 = fopen(target, "r")) == NULL)
 841:             goto badt;
 842:         if ((f2 = fopen(new, "r")) == NULL) {
 843:         badn:
 844:             error("%s:%s: %s\n", host, new, sys_errlist[errno]);
 845:             (void) unlink(new);
 846:             return;
 847:         }
 848:         while ((c = getc(f1)) == getc(f2))
 849:             if (c == EOF) {
 850:                 (void) fclose(f1);
 851:                 (void) fclose(f2);
 852:                 (void) unlink(new);
 853:                 ack();
 854:                 return;
 855:             }
 856:         (void) fclose(f1);
 857:         (void) fclose(f2);
 858:         if (opts & VERIFY) {
 859:         differ:
 860:             (void) unlink(new);
 861:             buf[0] = '\0';
 862:             (void) sprintf(buf + 1, "need to update: %s\n",target);
 863:             (void) write(rem, buf, strlen(buf + 1) + 1);
 864:             return;
 865:         }
 866:     }
 867: 
 868:     /*
 869: 	 * Set last modified time
 870: 	 */
 871:     tvp[0].tv_sec = stb.st_atime;   /* old atime from target */
 872:     tvp[0].tv_usec = 0;
 873:     tvp[1].tv_sec = mtime;
 874:     tvp[1].tv_usec = 0;
 875:     if (utimes(new, tvp) < 0) {
 876:         note("%s:utimes failed %s: %s\n", host, new, sys_errlist[errno]);
 877:     }
 878:     if (chog(new, owner, group, mode) < 0) {
 879:         (void) unlink(new);
 880:         return;
 881:     }
 882: fixup:
 883:     if (rename(new, target) < 0) {
 884: badt:
 885:         error("%s:%s: %s\n", host, target, sys_errlist[errno]);
 886:         (void) unlink(new);
 887:         return;
 888:     }
 889:     if (opts & COMPARE) {
 890:         buf[0] = '\0';
 891:         (void) sprintf(buf + 1, "updated %s\n", target);
 892:         (void) write(rem, buf, strlen(buf + 1) + 1);
 893:     } else
 894:         ack();
 895: }
 896: 
 897: /*
 898:  * Creat a hard link to existing file.
 899:  */
 900: hardlink(cmd)
 901:     char *cmd;
 902: {
 903:     register char *cp;
 904:     struct stat stb;
 905:     char *oldname;
 906:     int opts, exists = 0;
 907: 
 908:     cp = cmd;
 909:     opts = 0;
 910:     while (*cp >= '0' && *cp <= '7')
 911:         opts = (opts << 3) | (*cp++ - '0');
 912:     if (*cp++ != ' ') {
 913:         error("hardlink: options not delimited\n");
 914:         return;
 915:     }
 916:     oldname = cp;
 917:     while (*cp && *cp != ' ')
 918:         cp++;
 919:     if (*cp != ' ') {
 920:         error("hardlink: oldname name not delimited\n");
 921:         return;
 922:     }
 923:     *cp++ = '\0';
 924: 
 925:     if (catname) {
 926:         (void) sprintf(tp, "/%s", cp);
 927:     }
 928:     if (lstat(target, &stb) == 0) {
 929:         int mode = stb.st_mode & S_IFMT;
 930:         if (mode != S_IFREG && mode != S_IFLNK) {
 931:             error("%s:%s: not a regular file\n", host, target);
 932:             return;
 933:         }
 934:         exists = 1;
 935:     }
 936:     if (chkparent(target) < 0 ) {
 937:         error("%s:%s: %s (no parent)\n",
 938:             host, target, sys_errlist[errno]);
 939:         return;
 940:     }
 941:     if (exists && (unlink(target) < 0)) {
 942:         error("%s:%s: %s (unlink)\n",
 943:             host, target, sys_errlist[errno]);
 944:         return;
 945:     }
 946:     if (link(oldname, target) < 0) {
 947:         error("%s:can't link %s to %s\n",
 948:             host, target, oldname);
 949:         return;
 950:     }
 951:     ack();
 952: }
 953: 
 954: /*
 955:  * Check to see if parent directory exists and create one if not.
 956:  */
 957: chkparent(name)
 958:     char *name;
 959: {
 960:     register char *cp;
 961:     struct stat stb;
 962: 
 963:     cp = rindex(name, '/');
 964:     if (cp == NULL || cp == name)
 965:         return(0);
 966:     *cp = '\0';
 967:     if (lstat(name, &stb) < 0) {
 968:         if (errno == ENOENT && chkparent(name) >= 0 &&
 969:             mkdir(name, 0777 & ~oumask) >= 0) {
 970:             *cp = '/';
 971:             return(0);
 972:         }
 973:     } else if (ISDIR(stb.st_mode)) {
 974:         *cp = '/';
 975:         return(0);
 976:     }
 977:     *cp = '/';
 978:     return(-1);
 979: }
 980: 
 981: /*
 982:  * Change owner, group and mode of file.
 983:  */
 984: chog(file, owner, group, mode)
 985:     char *file, *owner, *group;
 986:     int mode;
 987: {
 988:     register int i;
 989:     int uid, gid;
 990:     extern char user[];
 991:     extern int userid;
 992: 
 993:     uid = userid;
 994:     if (userid == 0) {
 995:         if (*owner == ':') {
 996:             uid = atoi(owner + 1);
 997:         } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
 998:             if ((pw = getpwnam(owner)) == NULL) {
 999:                 if (mode & 04000) {
1000:                     note("%s:%s: unknown login name, clearing setuid",
1001:                         host, owner);
1002:                     mode &= ~04000;
1003:                     uid = 0;
1004:                 }
1005:             } else
1006:                 uid = pw->pw_uid;
1007:         } else
1008:             uid = pw->pw_uid;
1009:         if (*group == ':') {
1010:             gid = atoi(group + 1);
1011:             goto ok;
1012:         }
1013:     } else if ((mode & 04000) && strcmp(user, owner) != 0)
1014:         mode &= ~04000;
1015:     gid = -1;
1016:     if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
1017:         if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
1018:            || ((gr = getgrnam(group)) == NULL)) {
1019:             if (mode & 02000) {
1020:                 note("%s:%s: unknown group", host, group);
1021:                 mode &= ~02000;
1022:             }
1023:         } else
1024:             gid = gr->gr_gid;
1025:     } else
1026:         gid = gr->gr_gid;
1027:     if (userid && gid >= 0) {
1028:         if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
1029:             if (!(strcmp(user, gr->gr_mem[i])))
1030:                 goto ok;
1031:         mode &= ~02000;
1032:         gid = -1;
1033:     }
1034: ok:
1035:     if (userid)
1036:         setreuid(userid, 0);
1037:     if (chown(file, uid, gid) < 0 ||
1038:         (mode & 06000) && chmod(file, mode) < 0) {
1039:         note("%s: chown or chmod failed: file %s:  %s",
1040:                  host, file, sys_errlist[errno]);
1041:     }
1042:     if (userid)
1043:         setreuid(0, userid);
1044:     return(0);
1045: }
1046: 
1047: /*
1048:  * Check for files on the machine being updated that are not on the master
1049:  * machine and remove them.
1050:  */
1051: rmchk(opts)
1052:     int opts;
1053: {
1054:     register char *cp, *s;
1055:     struct stat stb;
1056: 
1057:     if (debug)
1058:         printf("rmchk()\n");
1059: 
1060:     /*
1061: 	 * Tell the remote to clean the files from the last directory sent.
1062: 	 */
1063:     (void) sprintf(buf, "C%o\n", opts & VERIFY);
1064:     if (debug)
1065:         printf("buf = %s", buf);
1066:     (void) write(rem, buf, strlen(buf));
1067:     if (response() < 0)
1068:         return;
1069:     for (;;) {
1070:         cp = s = buf;
1071:         do {
1072:             if (read(rem, cp, 1) != 1)
1073:                 lostconn();
1074:         } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1075: 
1076:         switch (*s++) {
1077:         case 'Q': /* Query if file should be removed */
1078:             /*
1079: 			 * Return the following codes to remove query.
1080: 			 * N\n -- file exists - DON'T remove.
1081: 			 * Y\n -- file doesn't exist - REMOVE.
1082: 			 */
1083:             *--cp = '\0';
1084:             (void) sprintf(tp, "/%s", s);
1085:             if (debug)
1086:                 printf("check %s\n", target);
1087:             if (except(target))
1088:                 (void) write(rem, "N\n", 2);
1089:             else if (lstat(target, &stb) < 0)
1090:                 (void) write(rem, "Y\n", 2);
1091:             else
1092:                 (void) write(rem, "N\n", 2);
1093:             break;
1094: 
1095:         case '\0':
1096:             *--cp = '\0';
1097:             if (*s != '\0')
1098:                 log(lfp, "%s\n", s);
1099:             break;
1100: 
1101:         case 'E':
1102:             *tp = '\0';
1103:             ack();
1104:             return;
1105: 
1106:         case '\1':
1107:         case '\2':
1108:             nerrs++;
1109:             if (*s != '\n') {
1110:                 if (!iamremote) {
1111:                     fflush(stdout);
1112:                     (void) write(2, s, cp - s);
1113:                 }
1114:                 if (lfp != NULL)
1115:                     (void) fwrite(s, 1, cp - s, lfp);
1116:             }
1117:             if (buf[0] == '\2')
1118:                 lostconn();
1119:             break;
1120: 
1121:         default:
1122:             error("rmchk: unexpected response '%s'\n", buf);
1123:             err();
1124:         }
1125:     }
1126: }
1127: 
1128: /*
1129:  * Check the current directory (initialized by the 'T' command to server())
1130:  * for extraneous files and remove them.
1131:  */
1132: clean(cp)
1133:     register char *cp;
1134: {
1135:     DIR *d;
1136:     register struct direct *dp;
1137:     struct stat stb;
1138:     char *otp;
1139:     int len, opts;
1140: 
1141:     opts = 0;
1142:     while (*cp >= '0' && *cp <= '7')
1143:         opts = (opts << 3) | (*cp++ - '0');
1144:     if (*cp != '\0') {
1145:         error("clean: options not delimited\n");
1146:         return;
1147:     }
1148:     if ((d = opendir(target)) == NULL) {
1149:         error("%s:%s: %s\n", host, target, sys_errlist[errno]);
1150:         return;
1151:     }
1152:     ack();
1153: 
1154:     otp = tp;
1155:     len = tp - target;
1156:     while (dp = readdir(d)) {
1157:         if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1158:             continue;
1159:         if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1160:             error("%s:%s/%s: Name too long\n",
1161:                 host, target, dp->d_name);
1162:             continue;
1163:         }
1164:         tp = otp;
1165:         *tp++ = '/';
1166:         cp = dp->d_name;;
1167:         while (*tp++ = *cp++)
1168:             ;
1169:         tp--;
1170:         if (lstat(target, &stb) < 0) {
1171:             error("%s:%s: %s\n", host, target, sys_errlist[errno]);
1172:             continue;
1173:         }
1174:         (void) sprintf(buf, "Q%s\n", dp->d_name);
1175:         (void) write(rem, buf, strlen(buf));
1176:         cp = buf;
1177:         do {
1178:             if (read(rem, cp, 1) != 1)
1179:                 cleanup();
1180:         } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1181:         *--cp = '\0';
1182:         cp = buf;
1183:         if (*cp != 'Y')
1184:             continue;
1185:         if (opts & VERIFY) {
1186:             cp = buf;
1187:             *cp++ = '\0';
1188:             (void) sprintf(cp, "need to remove: %s\n", target);
1189:             (void) write(rem, buf, strlen(cp) + 1);
1190:         } else
1191:             remove(&stb);
1192:     }
1193:     closedir(d);
1194:     (void) write(rem, "E\n", 2);
1195:     (void) response();
1196:     tp = otp;
1197:     *tp = '\0';
1198: }
1199: 
1200: /*
1201:  * Remove a file or directory (recursively) and send back an acknowledge
1202:  * or an error message.
1203:  */
1204: remove(stp)
1205:     struct stat *stp;
1206: {
1207:     DIR *d;
1208:     struct direct *dp;
1209:     register char *cp;
1210:     struct stat stb;
1211:     char *otp;
1212:     int len;
1213: 
1214:     switch (stp->st_mode & S_IFMT) {
1215:     case S_IFREG:
1216:     case S_IFLNK:
1217:         if (unlink(target) < 0)
1218:             goto bad;
1219:         goto removed;
1220: 
1221:     case S_IFDIR:
1222:         break;
1223: 
1224:     default:
1225:         error("%s:%s: not a plain file\n", host, target);
1226:         return;
1227:     }
1228: 
1229:     if ((d = opendir(target)) == NULL)
1230:         goto bad;
1231: 
1232:     otp = tp;
1233:     len = tp - target;
1234:     while (dp = readdir(d)) {
1235:         if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1236:             continue;
1237:         if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1238:             error("%s:%s/%s: Name too long\n",
1239:                 host, target, dp->d_name);
1240:             continue;
1241:         }
1242:         tp = otp;
1243:         *tp++ = '/';
1244:         cp = dp->d_name;;
1245:         while (*tp++ = *cp++)
1246:             ;
1247:         tp--;
1248:         if (lstat(target, &stb) < 0) {
1249:             error("%s:%s: %s\n", host, target, sys_errlist[errno]);
1250:             continue;
1251:         }
1252:         remove(&stb);
1253:     }
1254:     closedir(d);
1255:     tp = otp;
1256:     *tp = '\0';
1257:     if (rmdir(target) < 0) {
1258: bad:
1259:         error("%s:%s: %s\n", host, target, sys_errlist[errno]);
1260:         return;
1261:     }
1262: removed:
1263:     cp = buf;
1264:     *cp++ = '\0';
1265:     (void) sprintf(cp, "removed %s\n", target);
1266:     (void) write(rem, buf, strlen(cp) + 1);
1267: }
1268: 
1269: /*
1270:  * Execute a shell command to handle special cases.
1271:  */
1272: dospecial(cmd)
1273:     char *cmd;
1274: {
1275:     int fd[2], status, pid, i;
1276:     register char *cp, *s;
1277:     char sbuf[BUFSIZ];
1278:     extern int userid, groupid;
1279: 
1280:     if (pipe(fd) < 0) {
1281:         error("%s\n", sys_errlist[errno]);
1282:         return;
1283:     }
1284:     if ((pid = fork()) == 0) {
1285:         /*
1286: 		 * Return everything the shell commands print.
1287: 		 */
1288:         (void) close(0);
1289:         (void) close(1);
1290:         (void) close(2);
1291:         (void) open("/dev/null", 0);
1292:         (void) dup(fd[1]);
1293:         (void) dup(fd[1]);
1294:         (void) close(fd[0]);
1295:         (void) close(fd[1]);
1296:         setgid(groupid);
1297:         setuid(userid);
1298:         execl("/bin/sh", "sh", "-c", cmd, 0);
1299:         _exit(127);
1300:     }
1301:     (void) close(fd[1]);
1302:     s = sbuf;
1303:     *s++ = '\0';
1304:     while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
1305:         cp = buf;
1306:         do {
1307:             *s++ = *cp++;
1308:             if (cp[-1] != '\n') {
1309:                 if (s < &sbuf[sizeof(sbuf)-1])
1310:                     continue;
1311:                 *s++ = '\n';
1312:             }
1313:             /*
1314: 			 * Throw away blank lines.
1315: 			 */
1316:             if (s == &sbuf[2]) {
1317:                 s--;
1318:                 continue;
1319:             }
1320:             (void) write(rem, sbuf, s - sbuf);
1321:             s = &sbuf[1];
1322:         } while (--i);
1323:     }
1324:     if (s > &sbuf[1]) {
1325:         *s++ = '\n';
1326:         (void) write(rem, sbuf, s - sbuf);
1327:     }
1328:     while ((i = wait(&status)) != pid && i != -1)
1329:         ;
1330:     if (i == -1)
1331:         status = -1;
1332:     (void) close(fd[0]);
1333:     if (status)
1334:         error("shell returned %d\n", status);
1335:     else
1336:         ack();
1337: }
1338: 
1339: /*VARARGS2*/
1340: log(fp, fmt, a1, a2, a3)
1341:     FILE *fp;
1342:     char *fmt;
1343:     int a1, a2, a3;
1344: {
1345:     /* Print changes locally if not quiet mode */
1346:     if (!qflag)
1347:         printf(fmt, a1, a2, a3);
1348: 
1349:     /* Save changes (for mailing) if really updating files */
1350:     if (!(options & VERIFY) && fp != NULL)
1351:         fprintf(fp, fmt, a1, a2, a3);
1352: }
1353: 
1354: /*VARARGS1*/
1355: error(fmt, a1, a2, a3)
1356:     char *fmt;
1357:     int a1, a2, a3;
1358: {
1359:     nerrs++;
1360:     strcpy(buf, "\1rdist: ");
1361:     (void) sprintf(buf+8, fmt, a1, a2, a3);
1362:     if (!iamremote) {
1363:         fflush(stdout);
1364:         (void) write(2, buf+1, strlen(buf+1));
1365:     } else
1366:         (void) write(rem, buf, strlen(buf));
1367:     if (lfp != NULL)
1368:         (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
1369: }
1370: 
1371: /*VARARGS1*/
1372: fatal(fmt, a1, a2,a3)
1373:     char *fmt;
1374:     int a1, a2, a3;
1375: {
1376:     nerrs++;
1377:     strcpy(buf, "\2rdist: ");
1378:     (void) sprintf(buf+8, fmt, a1, a2, a3);
1379:     if (!iamremote) {
1380:         fflush(stdout);
1381:         (void) write(2, buf+1, strlen(buf+1));
1382:     } else
1383:         (void) write(rem, buf, strlen(buf));
1384:     if (lfp != NULL)
1385:         (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
1386:     cleanup();
1387: }
1388: 
1389: response()
1390: {
1391:     char *cp, *s;
1392:     char resp[BUFSIZ];
1393: 
1394:     if (debug)
1395:         printf("response()\n");
1396: 
1397:     cp = s = resp;
1398:     do {
1399:         if (read(rem, cp, 1) != 1)
1400:             lostconn();
1401:     } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
1402: 
1403:     switch (*s++) {
1404:     case '\0':
1405:         *--cp = '\0';
1406:         if (*s != '\0') {
1407:             log(lfp, "%s\n", s);
1408:             return(1);
1409:         }
1410:         return(0);
1411:     case '\3':
1412:         *--cp = '\0';
1413:         log(lfp, "Note: %s\n",s);
1414:         return(response());
1415: 
1416:     default:
1417:         s--;
1418:         /* fall into... */
1419:     case '\1':
1420:     case '\2':
1421:         nerrs++;
1422:         if (*s != '\n') {
1423:             if (!iamremote) {
1424:                 fflush(stdout);
1425:                 (void) write(2, s, cp - s);
1426:             }
1427:             if (lfp != NULL)
1428:                 (void) fwrite(s, 1, cp - s, lfp);
1429:         }
1430:         if (resp[0] == '\2')
1431:             lostconn();
1432:         return(-1);
1433:     }
1434: }
1435: 
1436: /*
1437:  * Remove temporary files and do any cleanup operations before exiting.
1438:  */
1439: cleanup()
1440: {
1441:     (void) unlink(tmpfile);
1442:     exit(1);
1443: }
1444: 
1445: note(fmt, a1, a2, a3)
1446: {
1447:     static char buf[BUFSIZ];
1448:     sprintf(buf, fmt, a1, a2, a3);
1449:     comment(buf);
1450: }
1451: 
1452: comment(s)
1453: char *s;
1454: {
1455:     char c = '\3';
1456:     write(rem, &c, 1);
1457:     write(rem, s, strlen(s));
1458:     c = '\n';
1459:     write(rem, &c, 1);
1460: }

Defined functions

chkparent defined in line 957; used 5 times
chog defined in line 984; used 2 times
clean defined in line 1132; used 1 times
cleanup defined in line 1439; used 18 times
comment defined in line 1452; used 1 times
dospecial defined in line 1272; used 1 times
error defined in line 1355; used 58 times
hardlink defined in line 900; used 1 times
  • in line 96
install defined in line 185; used 3 times
log defined in line 1340; used 17 times
note defined in line 1445; used 4 times
query defined in line 605; used 1 times
recvf defined in line 640; used 3 times
remove defined in line 1204; used 2 times
response defined in line 1389; used 14 times
rmchk defined in line 1051; used 1 times
savelink defined in line 471; used 4 times
sendf defined in line 255; used 2 times
server defined in line 37; used 1 times
update defined in line 506; used 1 times

Defined variables

Tdest defined in line 20; used 4 times
buf defined in line 17; used 241 times
catname defined in line 21; used 11 times
ihead defined in line 16; used 12 times
oumask defined in line 23; used 2 times
sccsid defined in line 8; never used
stp defined in line 22; used 21 times
target defined in line 18; used 90 times
tp defined in line 19; used 59 times

Defined macros

ack defined in line 13; used 17 times
err defined in line 14; used 4 times
protogroup defined in line 250; used 3 times
protoname defined in line 249; used 3 times
Last modified: 1986-06-07
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3751
Valid CSS Valid XHTML 1.0 Strict