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