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[] = "@(#)docmd.c 5.1 (Berkeley) 6/6/85"; 9: #endif not lint 10: 11: #include "defs.h" 12: #include <setjmp.h> 13: #include <netdb.h> 14: 15: #ifndef RDIST 16: #define RDIST "/usr/ucb/rdist" 17: #endif 18: 19: FILE *lfp; /* log file for recording files updated */ 20: struct subcmd *subcmds; /* list of sub-commands for current cmd */ 21: jmp_buf env; 22: 23: int cleanup(); 24: int lostconn(); 25: 26: /* 27: * Do the commands in cmds (initialized by yyparse). 28: */ 29: docmds(dhosts, argc, argv) 30: char **dhosts; 31: int argc; 32: char **argv; 33: { 34: register struct cmd *c; 35: register struct namelist *f; 36: register char **cpp; 37: extern struct cmd *cmds; 38: 39: signal(SIGHUP, cleanup); 40: signal(SIGINT, cleanup); 41: signal(SIGQUIT, cleanup); 42: signal(SIGTERM, cleanup); 43: 44: for (c = cmds; c != NULL; c = c->c_next) { 45: if (dhosts != NULL && *dhosts != NULL) { 46: for (cpp = dhosts; *cpp; cpp++) 47: if (strcmp(c->c_name, *cpp) == 0) 48: goto fndhost; 49: continue; 50: } 51: fndhost: 52: if (argc) { 53: for (cpp = argv; *cpp; cpp++) { 54: if (c->c_label != NULL && 55: strcmp(c->c_label, *cpp) == 0) { 56: cpp = NULL; 57: goto found; 58: } 59: for (f = c->c_files; f != NULL; f = f->n_next) 60: if (strcmp(f->n_name, *cpp) == 0) 61: goto found; 62: } 63: continue; 64: } else 65: cpp = NULL; 66: found: 67: switch (c->c_type) { 68: case ARROW: 69: doarrow(cpp, c->c_files, c->c_name, c->c_cmds); 70: break; 71: case DCOLON: 72: dodcolon(cpp, c->c_files, c->c_name, c->c_cmds); 73: break; 74: default: 75: fatal("illegal command type %d\n", c->c_type); 76: } 77: } 78: closeconn(); 79: } 80: 81: /* 82: * Process commands for sending files to other machines. 83: */ 84: doarrow(filev, files, rhost, cmds) 85: char **filev; 86: struct namelist *files; 87: char *rhost; 88: struct subcmd *cmds; 89: { 90: register struct namelist *f; 91: register struct subcmd *sc; 92: register char **cpp; 93: int n, ddir, opts = options; 94: 95: if (debug) 96: printf("doarrow(%x, %s, %x)\n", files, rhost, cmds); 97: 98: if (files == NULL) { 99: error("no files to be updated\n"); 100: return; 101: } 102: 103: subcmds = cmds; 104: ddir = files->n_next != NULL; /* destination is a directory */ 105: if (nflag) 106: printf("updating host %s\n", rhost); 107: else { 108: if (setjmp(env)) 109: goto done; 110: signal(SIGPIPE, lostconn); 111: if (!makeconn(rhost)) 112: return; 113: if ((lfp = fopen(tmpfile, "w")) == NULL) { 114: fatal("cannot open %s\n", tmpfile); 115: exit(1); 116: } 117: } 118: for (f = files; f != NULL; f = f->n_next) { 119: if (filev) { 120: for (cpp = filev; *cpp; cpp++) 121: if (strcmp(f->n_name, *cpp) == 0) 122: goto found; 123: if (!nflag) 124: (void) fclose(lfp); 125: continue; 126: } 127: found: 128: n = 0; 129: for (sc = cmds; sc != NULL; sc = sc->sc_next) { 130: if (sc->sc_type != INSTALL) 131: continue; 132: n++; 133: install(f->n_name, sc->sc_name, 134: sc->sc_name == NULL ? 0 : ddir, sc->sc_options); 135: opts = sc->sc_options; 136: } 137: if (n == 0) 138: install(f->n_name, NULL, 0, options); 139: } 140: done: 141: if (!nflag) { 142: (void) signal(SIGPIPE, cleanup); 143: (void) fclose(lfp); 144: lfp = NULL; 145: } 146: for (sc = cmds; sc != NULL; sc = sc->sc_next) 147: if (sc->sc_type == NOTIFY) 148: notify(tmpfile, rhost, sc->sc_args, 0); 149: if (!nflag) { 150: (void) unlink(tmpfile); 151: for (; ihead != NULL; ihead = ihead->nextp) { 152: free(ihead); 153: if ((opts & IGNLNKS) || ihead->count == 0) 154: continue; 155: log(lfp, "%s: Warning: missing links\n", 156: ihead->pathname); 157: } 158: } 159: } 160: 161: /* 162: * Create a connection to the rdist server on the machine rhost. 163: */ 164: makeconn(rhost) 165: char *rhost; 166: { 167: register char *ruser, *cp; 168: static char *cur_host = NULL; 169: static int port = -1; 170: char tuser[20]; 171: int n; 172: extern char user[]; 173: extern int userid; 174: 175: if (debug) 176: printf("makeconn(%s)\n", rhost); 177: 178: if (cur_host != NULL && rem >= 0) { 179: if (strcmp(cur_host, rhost) == 0) 180: return(1); 181: closeconn(); 182: } 183: cur_host = rhost; 184: cp = index(rhost, '@'); 185: if (cp != NULL) { 186: char c = *cp; 187: 188: *cp = '\0'; 189: strncpy(tuser, rhost, sizeof(tuser)-1); 190: *cp = c; 191: rhost = cp + 1; 192: ruser = tuser; 193: if (*ruser == '\0') 194: ruser = user; 195: else if (!okname(ruser)) 196: return(0); 197: } else 198: ruser = user; 199: if (!qflag) 200: printf("updating host %s\n", rhost); 201: (void) sprintf(buf, "%s -Server%s", RDIST, qflag ? " -q" : ""); 202: if (port < 0) { 203: struct servent *sp; 204: 205: if ((sp = getservbyname("shell", "tcp")) == NULL) 206: fatal("shell/tcp: unknown service"); 207: port = sp->s_port; 208: } 209: 210: if (debug) { 211: printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser); 212: printf("buf = %s\n", buf); 213: } 214: 215: fflush(stdout); 216: setreuid(userid, 0); 217: rem = rcmd(&rhost, port, user, ruser, buf, 0); 218: setreuid(0, userid); 219: if (rem < 0) 220: return(0); 221: cp = buf; 222: if (read(rem, cp, 1) != 1) 223: lostconn(); 224: if (*cp == 'V') { 225: do { 226: if (read(rem, cp, 1) != 1) 227: lostconn(); 228: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 229: *--cp = '\0'; 230: cp = buf; 231: n = 0; 232: while (*cp >= '0' && *cp <= '9') 233: n = (n * 10) + (*cp++ - '0'); 234: if (*cp == '\0' && n == VERSION) 235: return(1); 236: error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n); 237: } else 238: error("connection failed: version numbers don't match\n"); 239: closeconn(); 240: return(0); 241: } 242: 243: /* 244: * Signal end of previous connection. 245: */ 246: closeconn() 247: { 248: if (debug) 249: printf("closeconn()\n"); 250: 251: if (rem >= 0) { 252: (void) write(rem, "\2\n", 2); 253: (void) close(rem); 254: rem = -1; 255: } 256: } 257: 258: lostconn() 259: { 260: if (iamremote) 261: cleanup(); 262: log(lfp, "rdist: lost connection\n"); 263: longjmp(env, 1); 264: } 265: 266: okname(name) 267: register char *name; 268: { 269: register char *cp = name; 270: register int c; 271: 272: do { 273: c = *cp; 274: if (c & 0200) 275: goto bad; 276: if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 277: goto bad; 278: cp++; 279: } while (*cp); 280: return(1); 281: bad: 282: error("invalid user name %s\n", name); 283: return(0); 284: } 285: 286: time_t lastmod; 287: FILE *tfp; 288: extern char target[], *tp; 289: 290: /* 291: * Process commands for comparing files to time stamp files. 292: */ 293: dodcolon(filev, files, stamp, cmds) 294: char **filev; 295: struct namelist *files; 296: char *stamp; 297: struct subcmd *cmds; 298: { 299: register struct subcmd *sc; 300: register struct namelist *f; 301: register char **cpp; 302: struct timeval tv[2]; 303: struct timezone tz; 304: struct stat stb; 305: 306: if (debug) 307: printf("dodcolon()\n"); 308: 309: if (files == NULL) { 310: error("no files to be updated\n"); 311: return; 312: } 313: if (stat(stamp, &stb) < 0) { 314: error("%s: %s\n", stamp, sys_errlist[errno]); 315: return; 316: } 317: if (debug) 318: printf("%s: %d\n", stamp, stb.st_mtime); 319: 320: subcmds = cmds; 321: lastmod = stb.st_mtime; 322: if (nflag || (options & VERIFY)) 323: tfp = NULL; 324: else { 325: if ((tfp = fopen(tmpfile, "w")) == NULL) { 326: error("%s: %s\n", stamp, sys_errlist[errno]); 327: return; 328: } 329: (void) gettimeofday(&tv[0], &tz); 330: tv[1] = tv[0]; 331: (void) utimes(stamp, tv); 332: } 333: 334: for (f = files; f != NULL; f = f->n_next) { 335: if (filev) { 336: for (cpp = filev; *cpp; cpp++) 337: if (strcmp(f->n_name, *cpp) == 0) 338: goto found; 339: continue; 340: } 341: found: 342: tp = NULL; 343: cmptime(f->n_name); 344: } 345: 346: if (tfp != NULL) 347: (void) fclose(tfp); 348: for (sc = cmds; sc != NULL; sc = sc->sc_next) 349: if (sc->sc_type == NOTIFY) 350: notify(tmpfile, NULL, sc->sc_args, lastmod); 351: if (!nflag && !(options & VERIFY)) 352: (void) unlink(tmpfile); 353: } 354: 355: /* 356: * Compare the mtime of file to the list of time stamps. 357: */ 358: cmptime(name) 359: char *name; 360: { 361: struct stat stb; 362: 363: if (debug) 364: printf("cmptime(%s)\n", name); 365: 366: if (except(name)) 367: return; 368: 369: if (nflag) { 370: printf("comparing dates: %s\n", name); 371: return; 372: } 373: 374: /* 375: * first time cmptime() is called? 376: */ 377: if (tp == NULL) { 378: if (exptilde(target, name) == NULL) 379: return; 380: tp = name = target; 381: while (*tp) 382: tp++; 383: } 384: if (access(name, 4) < 0 || stat(name, &stb) < 0) { 385: error("%s: %s\n", name, sys_errlist[errno]); 386: return; 387: } 388: 389: switch (stb.st_mode & S_IFMT) { 390: case S_IFREG: 391: break; 392: 393: case S_IFDIR: 394: rcmptime(&stb); 395: return; 396: 397: default: 398: error("%s: not a plain file\n", name); 399: return; 400: } 401: 402: if (stb.st_mtime > lastmod) 403: log(tfp, "new: %s\n", name); 404: } 405: 406: rcmptime(st) 407: struct stat *st; 408: { 409: register DIR *d; 410: register struct direct *dp; 411: register char *cp; 412: char *otp; 413: int len; 414: 415: if (debug) 416: printf("rcmptime(%x)\n", st); 417: 418: if ((d = opendir(target)) == NULL) { 419: error("%s: %s\n", target, sys_errlist[errno]); 420: return; 421: } 422: otp = tp; 423: len = tp - target; 424: while (dp = readdir(d)) { 425: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 426: continue; 427: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 428: error("%s/%s: Name too long\n", target, dp->d_name); 429: continue; 430: } 431: tp = otp; 432: *tp++ = '/'; 433: cp = dp->d_name; 434: while (*tp++ = *cp++) 435: ; 436: tp--; 437: cmptime(target); 438: } 439: closedir(d); 440: tp = otp; 441: *tp = '\0'; 442: } 443: 444: /* 445: * Notify the list of people the changes that were made. 446: * rhost == NULL if we are mailing a list of changes compared to at time 447: * stamp file. 448: */ 449: notify(file, rhost, to, lmod) 450: char *file, *rhost; 451: register struct namelist *to; 452: time_t lmod; 453: { 454: register int fd, len; 455: FILE *pf, *popen(); 456: struct stat stb; 457: 458: if ((options & VERIFY) || to == NULL) 459: return; 460: if (!qflag) { 461: printf("notify "); 462: if (rhost) 463: printf("@%s ", rhost); 464: prnames(to); 465: } 466: if (nflag) 467: return; 468: 469: if ((fd = open(file, 0)) < 0) { 470: error("%s: %s\n", file, sys_errlist[errno]); 471: return; 472: } 473: if (fstat(fd, &stb) < 0) { 474: error("%s: %s\n", file, sys_errlist[errno]); 475: (void) close(fd); 476: return; 477: } 478: if (stb.st_size == 0) { 479: (void) close(fd); 480: return; 481: } 482: /* 483: * Create a pipe to mailling program. 484: */ 485: pf = popen(MAILCMD, "w"); 486: if (pf == NULL) { 487: error("notify: \"%s\" failed\n", MAILCMD); 488: (void) close(fd); 489: return; 490: } 491: /* 492: * Output the proper header information. 493: */ 494: fprintf(pf, "From: rdist (Remote distribution program)\n"); 495: fprintf(pf, "To:"); 496: if (!any('@', to->n_name) && rhost != NULL) 497: fprintf(pf, " %s@%s", to->n_name, rhost); 498: else 499: fprintf(pf, " %s", to->n_name); 500: to = to->n_next; 501: while (to != NULL) { 502: if (!any('@', to->n_name) && rhost != NULL) 503: fprintf(pf, ", %s@%s", to->n_name, rhost); 504: else 505: fprintf(pf, ", %s", to->n_name); 506: to = to->n_next; 507: } 508: putc('\n', pf); 509: if (rhost != NULL) 510: fprintf(pf, "Subject: files updated by rdist from %s to %s\n", 511: host, rhost); 512: else 513: fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod)); 514: putc('\n', pf); 515: 516: while ((len = read(fd, buf, BUFSIZ)) > 0) 517: (void) fwrite(buf, 1, len, pf); 518: (void) close(fd); 519: (void) pclose(pf); 520: } 521: 522: /* 523: * Return true if name is in the list. 524: */ 525: inlist(list, file) 526: struct namelist *list; 527: char *file; 528: { 529: register struct namelist *nl; 530: 531: for (nl = list; nl != NULL; nl = nl->n_next) 532: if (!strcmp(file, nl->n_name)) 533: return(1); 534: return(0); 535: } 536: 537: /* 538: * Return TRUE if file is in the exception list. 539: */ 540: except(file) 541: char *file; 542: { 543: register struct subcmd *sc; 544: register struct namelist *nl; 545: 546: if (debug) 547: printf("except(%s)\n", file); 548: 549: for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 550: if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN) 551: continue; 552: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) { 553: if (sc->sc_type == EXCEPT) { 554: if (!strcmp(file, nl->n_name)) 555: return(1); 556: continue; 557: } 558: re_comp(nl->n_name); 559: if (re_exec(file) > 0) 560: return(1); 561: } 562: } 563: return(0); 564: } 565: 566: char * 567: colon(cp) 568: register char *cp; 569: { 570: 571: while (*cp) { 572: if (*cp == ':') 573: return(cp); 574: if (*cp == '/') 575: return(0); 576: cp++; 577: } 578: return(0); 579: }