Annotation of src/usr.bin/rdist/docmd.c, Revision 1.2
1.1 deraadt 1: /*
1.2 ! dm 2: * Copyright (c) 1983 Regents of the University of California.
! 3: * All rights reserved.
1.1 deraadt 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:
34: #ifndef lint
1.2 ! dm 35: static char RCSid[] =
! 36: "$Id: docmd.c,v 6.85 1995/12/12 00:20:24 mcooper Exp $";
! 37:
! 38: static char sccsid[] = "@(#)docmd.c 5.1 (Berkeley) 6/6/85";
! 39:
! 40: static char copyright[] =
! 41: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
! 42: All rights reserved.\n";
1.1 deraadt 43: #endif /* not lint */
44:
1.2 ! dm 45: /*
! 46: * Functions for rdist that do command (cmd) related activities.
! 47: */
! 48:
1.1 deraadt 49: #include "defs.h"
1.2 ! dm 50: #include "y.tab.h"
! 51: #include <sys/socket.h>
1.1 deraadt 52: #include <netdb.h>
53:
1.2 ! dm 54: struct subcmd *subcmds; /* list of sub-commands for
! 55: current cmd */
! 56: struct namelist *filelist; /* list of source files */
! 57: extern struct cmd *cmds; /* Initialized by yyparse() */
! 58: time_t lastmod; /* Last modify time */
! 59:
! 60: extern char target[];
! 61: extern char *ptarget;
! 62: extern int activechildren;
! 63: extern int maxchildren;
! 64: extern int amchild;
! 65: extern char *path_rdistd;
! 66:
! 67: static void cmptime();
1.1 deraadt 68:
69: /*
1.2 ! dm 70: * Signal end of connection.
1.1 deraadt 71: */
1.2 ! dm 72: static void closeconn()
1.1 deraadt 73: {
1.2 ! dm 74: debugmsg(DM_CALL, "closeconn() called\n");
1.1 deraadt 75:
1.2 ! dm 76: if (rem_w >= 0) {
! 77: /* We don't care if the connection is still good or not */
! 78: signal(SIGPIPE, SIG_IGN);
! 79:
! 80: (void) sendcmd(C_FERRMSG, NULL);
! 81: (void) close(rem_w);
! 82: (void) close(rem_r); /* This can't hurt */
! 83: rem_w = -1;
! 84: rem_r = -1;
1.1 deraadt 85: }
86: }
87:
88: /*
1.2 ! dm 89: * Notify the list of people the changes that were made.
! 90: * rhost == NULL if we are mailing a list of changes compared to at time
! 91: * stamp file.
1.1 deraadt 92: */
1.2 ! dm 93: static void notify(rhost, to, lmod)
1.1 deraadt 94: char *rhost;
1.2 ! dm 95: register struct namelist *to;
! 96: time_t lmod;
1.1 deraadt 97: {
1.2 ! dm 98: register int fd, len;
! 99: FILE *pf, *popen();
! 100: struct stat stb;
! 101: static char buf[BUFSIZ];
! 102: char *file;
1.1 deraadt 103:
1.2 ! dm 104: if (IS_ON(options, DO_VERIFY) || to == NULL)
! 105: return;
1.1 deraadt 106:
1.2 ! dm 107: if ((file = getnotifyfile()) == NULL)
1.1 deraadt 108: return;
1.2 ! dm 109:
! 110: if (!IS_ON(options, DO_QUIET)) {
! 111: message(MT_INFO, "notify %s%s %s",
! 112: (rhost) ? "@" : "",
! 113: (rhost) ? rhost : "", getnlstr(to));
1.1 deraadt 114: }
115:
116: if (nflag)
1.2 ! dm 117: return;
! 118:
! 119: debugmsg(DM_MISC, "notify() temp file = '%s'", file);
! 120:
! 121: if ((fd = open(file, O_RDONLY)) < 0) {
! 122: error("%s: open for reading failed: %s", file, SYSERR);
! 123: return;
! 124: }
! 125: if (fstat(fd, &stb) < 0) {
! 126: error("%s: fstat failed: %s", file, SYSERR);
! 127: (void) close(fd);
! 128: return;
! 129: }
! 130: if (stb.st_size == 0) {
! 131: (void) close(fd);
! 132: return;
! 133: }
! 134: /*
! 135: * Create a pipe to mailling program.
! 136: * Set IFS to avoid possible security problem with users
! 137: * setting "IFS=/".
! 138: */
! 139: (void) sprintf(buf, "IFS=\" \t\"; export IFS; %s -oi -t",
! 140: _PATH_SENDMAIL);
! 141: pf = popen(buf, "w");
! 142: if (pf == NULL) {
! 143: error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
! 144: (void) unlink(file);
! 145: (void) close(fd);
! 146: return;
! 147: }
! 148: /*
! 149: * Output the proper header information.
! 150: */
! 151: (void) fprintf(pf, "From: rdist (Remote distribution program)\n");
! 152: (void) fprintf(pf, "To:");
! 153: if (!any('@', to->n_name) && rhost != NULL)
! 154: (void) fprintf(pf, " %s@%s", to->n_name, rhost);
! 155: else
! 156: (void) fprintf(pf, " %s", to->n_name);
! 157: to = to->n_next;
! 158: while (to != NULL) {
! 159: if (!any('@', to->n_name) && rhost != NULL)
! 160: (void) fprintf(pf, ", %s@%s", to->n_name, rhost);
! 161: else
! 162: (void) fprintf(pf, ", %s", to->n_name);
! 163: to = to->n_next;
! 164: }
! 165: (void) putc('\n', pf);
! 166: if (rhost != NULL)
! 167: (void) fprintf(pf,
! 168: "Subject: files updated by rdist from %s to %s\n",
! 169: host, rhost);
! 170: else
! 171: (void) fprintf(pf, "Subject: files updated after %s\n",
! 172: ctime(&lmod));
! 173: (void) putc('\n', pf);
! 174: (void) putc('\n', pf);
! 175:
! 176: while ((len = read(fd, buf, sizeof(buf))) > 0)
! 177: (void) fwrite(buf, 1, len, pf);
! 178:
! 179: (void) pclose(pf);
! 180: (void) close(fd);
! 181: (void) unlink(file);
! 182: }
! 183:
! 184: /*
! 185: * XXX Hack for NFS. If a hostname from the distfile
! 186: * ends with a '+', then the normal restriction of
! 187: * skipping files that are on an NFS filesystem is
! 188: * bypassed. We always strip '+' to be consistent.
! 189: */
! 190: static void checkcmd(cmd)
! 191: struct cmd *cmd;
! 192: {
! 193: int l;
! 194:
! 195: if (!cmd || !(cmd->c_name)) {
! 196: debugmsg(DM_MISC, "checkcmd() NULL cmd parameter");
! 197: return;
! 198: }
! 199:
! 200: l = strlen(cmd->c_name);
! 201: if (l <= 0)
! 202: return;
! 203: if (cmd->c_name[l-1] == '+') {
! 204: cmd->c_flags |= CMD_NOCHKNFS;
! 205: cmd->c_name[l-1] = CNULL;
! 206: }
! 207: }
! 208:
! 209: /*
! 210: * Mark all other entries for this command (cmd)
! 211: * as assigned.
! 212: */
! 213: extern void markassigned(cmd, cmdlist)
! 214: struct cmd *cmd;
! 215: struct cmd *cmdlist;
! 216: {
! 217: register struct cmd *pcmd;
! 218:
! 219: for (pcmd = cmdlist; pcmd; pcmd = pcmd->c_next) {
! 220: checkcmd(pcmd);
! 221: if (pcmd->c_type == cmd->c_type &&
! 222: strcmp(pcmd->c_name, cmd->c_name)==0)
! 223: pcmd->c_flags |= CMD_ASSIGNED;
1.1 deraadt 224: }
1.2 ! dm 225: }
! 226:
! 227: /*
! 228: * Mark the command "cmd" as failed for all commands in list cmdlist.
! 229: */
! 230: static void markfailed(cmd, cmdlist)
! 231: struct cmd *cmd;
! 232: struct cmd *cmdlist;
! 233: {
! 234: register struct cmd *pc;
! 235:
! 236: if (!cmd) {
! 237: debugmsg(DM_MISC, "markfailed() NULL cmd parameter");
! 238: return;
1.1 deraadt 239: }
1.2 ! dm 240:
! 241: checkcmd(cmd);
! 242: cmd->c_flags |= CMD_CONNFAILED;
! 243: for (pc = cmdlist; pc; pc = pc->c_next) {
! 244: checkcmd(pc);
! 245: if (pc->c_type == cmd->c_type &&
! 246: strcmp(pc->c_name, cmd->c_name)==0)
! 247: pc->c_flags |= CMD_CONNFAILED;
1.1 deraadt 248: }
1.2 ! dm 249: }
! 250:
! 251: static int remotecmd(rhost, luser, ruser, cmd)
! 252: char *rhost;
! 253: char *luser, *ruser;
! 254: char *cmd;
! 255: {
! 256: int desc;
! 257: #if defined(DIRECT_RCMD)
! 258: static int port = -1;
! 259: #endif /* DIRECT_RCMD */
! 260:
! 261: debugmsg(DM_MISC, "local user = %s remote user = %s\n", luser, ruser);
! 262: debugmsg(DM_MISC, "Remote command = '%s'\n", cmd);
! 263:
! 264: (void) fflush(stdout);
! 265: (void) fflush(stderr);
! 266: (void) signal(SIGALRM, sighandler);
! 267: (void) alarm(RTIMEOUT);
! 268:
! 269: #if defined(DIRECT_RCMD)
! 270: (void) signal(SIGPIPE, sighandler);
! 271:
! 272: if (port < 0) {
! 273: struct servent *sp;
! 274:
! 275: if ((sp = getservbyname("shell", "tcp")) == NULL)
! 276: fatalerr("shell/tcp: unknown service");
! 277: port = sp->s_port;
1.1 deraadt 278: }
1.2 ! dm 279:
! 280: if (becomeroot() != 0)
! 281: exit(1);
! 282: desc = rcmd(&rhost, port, luser, ruser, cmd, 0);
! 283: if (becomeuser() != 0)
! 284: exit(1);
! 285: #else /* !DIRECT_RCMD */
! 286: debugmsg(DM_MISC, "Remote shell command = '%s'\n", path_remsh);
! 287: (void) signal(SIGPIPE, SIG_IGN);
! 288: desc = rshrcmd(&rhost, -1, luser, ruser, cmd, 0);
! 289: if (desc > 0)
! 290: (void) signal(SIGPIPE, sighandler);
! 291: #endif /* DIRECT_RCMD */
! 292:
! 293: (void) alarm(0);
! 294:
! 295: return(desc);
1.1 deraadt 296: }
297:
298: /*
299: * Create a connection to the rdist server on the machine rhost.
1.2 ! dm 300: * Return 0 if the connection fails or 1 if it succeeds.
1.1 deraadt 301: */
1.2 ! dm 302: static int makeconn(rhost)
1.1 deraadt 303: char *rhost;
304: {
305: register char *ruser, *cp;
306: static char *cur_host = NULL;
1.2 ! dm 307: extern char *locuser;
! 308: extern long min_freefiles, min_freespace;
! 309: extern char *remotemsglist;
! 310: char tuser[BUFSIZ], buf[BUFSIZ];
! 311: u_char respbuff[BUFSIZ];
1.1 deraadt 312: int n;
313:
1.2 ! dm 314: debugmsg(DM_CALL, "makeconn(%s)", rhost);
1.1 deraadt 315:
1.2 ! dm 316: /*
! 317: * See if we're already connected to this host
! 318: */
! 319: if (cur_host != NULL && rem_w >= 0) {
1.1 deraadt 320: if (strcmp(cur_host, rhost) == 0)
321: return(1);
322: closeconn();
323: }
1.2 ! dm 324:
! 325: /*
! 326: * Determine remote user and current host names
! 327: */
1.1 deraadt 328: cur_host = rhost;
1.2 ! dm 329: cp = strchr(rhost, '@');
! 330:
1.1 deraadt 331: if (cp != NULL) {
332: char c = *cp;
333:
1.2 ! dm 334: *cp = CNULL;
! 335: (void) strncpy((char *)tuser, rhost, sizeof(tuser)-1);
1.1 deraadt 336: *cp = c;
337: rhost = cp + 1;
338: ruser = tuser;
1.2 ! dm 339: if (*ruser == CNULL)
! 340: ruser = locuser;
1.1 deraadt 341: else if (!okname(ruser))
342: return(0);
343: } else
1.2 ! dm 344: ruser = locuser;
! 345:
! 346: if (!IS_ON(options, DO_QUIET))
! 347: message(MT_VERBOSE, "updating host %s", rhost);
1.1 deraadt 348:
1.2 ! dm 349: (void) sprintf(buf, "%.*s -S", sizeof(buf)-5, path_rdistd);
! 350:
! 351: if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0)
! 352: return(0);
1.1 deraadt 353:
1.2 ! dm 354: /*
! 355: * First thing received should be S_VERSION
! 356: */
! 357: n = remline(respbuff, sizeof(respbuff), TRUE);
! 358: if (n <= 0 || respbuff[0] != S_VERSION) {
! 359: error("Unexpected input from server: \"%s\".", respbuff);
! 360: closeconn();
! 361: return(0);
1.1 deraadt 362: }
363:
1.2 ! dm 364: /*
! 365: * For future compatibility we check to see if the server
! 366: * sent it's version number to us. If it did, we use it,
! 367: * otherwise, we send our version number to the server and let
! 368: * it decide if it can handle our protocol version.
! 369: */
! 370: if (respbuff[1] == CNULL) {
! 371: /*
! 372: * The server wants us to send it our version number
! 373: */
! 374: (void) sendcmd(S_VERSION, "%d", VERSION);
! 375: if (response() < 0)
! 376: return(0);
1.1 deraadt 377: } else {
1.2 ! dm 378: /*
! 379: * The server sent it's version number to us
! 380: */
! 381: proto_version = atoi(&respbuff[1]);
! 382: if (proto_version != VERSION) {
! 383: fatalerr(
! 384: "Server version (%d) is not the same as local version (%d).",
! 385: proto_version, VERSION);
! 386: return(0);
! 387: }
1.1 deraadt 388: }
389:
1.2 ! dm 390: /*
! 391: * Send config commands
! 392: */
! 393: if (host[0]) {
! 394: (void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host);
! 395: if (response() < 0)
! 396: return(0);
! 397: }
! 398: if (min_freespace) {
! 399: (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREESPACE,
! 400: min_freespace);
! 401: if (response() < 0)
! 402: return(0);
! 403: }
! 404: if (min_freefiles) {
! 405: (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREEFILES,
! 406: min_freefiles);
! 407: if (response() < 0)
! 408: return(0);
! 409: }
! 410: if (remotemsglist) {
! 411: (void) sendcmd(C_SETCONFIG, "%c%s", SC_LOGGING, remotemsglist);
! 412: if (response() < 0)
! 413: return(0);
1.1 deraadt 414: }
415:
416: return(1);
417: }
418:
419: /*
1.2 ! dm 420: * Process commands for sending files to other machines.
1.1 deraadt 421: */
1.2 ! dm 422: static void doarrow(cmd, filev)
! 423: struct cmd *cmd;
1.1 deraadt 424: char **filev;
425: {
1.2 ! dm 426: register struct namelist *f;
1.1 deraadt 427: register struct subcmd *sc;
428: register char **cpp;
1.2 ! dm 429: int n, ddir, destdir, opts = options;
! 430: struct namelist *files;
! 431: struct subcmd *sbcmds;
! 432: char *rhost;
! 433: int didupdate = 0;
! 434:
! 435: if (!cmd) {
! 436: debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
! 437: return;
! 438: }
1.1 deraadt 439:
1.2 ! dm 440: files = cmd->c_files;
! 441: sbcmds = cmd->c_cmds;
! 442: rhost = cmd->c_name;
1.1 deraadt 443:
444: if (files == NULL) {
1.2 ! dm 445: error("No files to be updated on %s for target \"%s\"",
! 446: rhost, cmd->c_label);
1.1 deraadt 447: return;
448: }
449:
1.2 ! dm 450: debugmsg(DM_CALL, "doarrow(%x, %s, %x) start",
! 451: files, A(rhost), sbcmds);
! 452:
! 453: if (nflag)
! 454: (void) printf("updating host %s\n", rhost);
1.1 deraadt 455: else {
1.2 ! dm 456: if (cmd->c_flags & CMD_CONNFAILED) {
! 457: debugmsg(DM_MISC,
! 458: "makeconn %s failed before; skipping\n",
! 459: rhost);
! 460: return;
! 461: }
! 462:
! 463: if (setjmp(finish_jmpbuf)) {
! 464: debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
! 465: markfailed(cmd, cmds);
! 466: return;
! 467: }
! 468:
! 469: if (!makeconn(rhost)) {
! 470: markfailed(cmd, cmds);
1.1 deraadt 471: return;
472: }
473: }
474:
1.2 ! dm 475: subcmds = sbcmds;
! 476: filelist = files;
! 477:
! 478: n = 0;
! 479: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
! 480: if (sc->sc_type != INSTALL)
! 481: continue;
! 482: n++;
! 483: /*
! 484: * destination is a directory if one of the following is true:
! 485: * a) more than one name specified on left side of -> directive
! 486: * b) basename of destination in "install" directive is "."
! 487: * (e.g. install /tmp/.;)
! 488: * c) name on left side of -> directive is a directory on local system.
! 489: *
! 490: * We need 2 destdir flags (destdir and ddir) because single directory
! 491: * source is handled differently. In this case, ddir is 0 (which
! 492: * tells install() not to send DIRTARGET directive to remote rdistd)
! 493: * and destdir is 1 (which tells remfilename() how to build the FILE
! 494: * variables correctly). In every other case, destdir and ddir will
! 495: * have the same value.
! 496: */
! 497: ddir = files->n_next != NULL; /* destination is a directory */
! 498: if (!ddir) {
! 499: struct stat s;
! 500: int isadir = 0;
! 501:
! 502: if (lstat(files->n_name, &s) == 0)
! 503: isadir = S_ISDIR(s.st_mode);
! 504: if (!isadir && sc->sc_name && *sc->sc_name)
! 505: ddir = !strcmp(xbasename(sc->sc_name),".");
! 506: destdir = isadir | ddir;
! 507: } else
! 508: destdir = ddir;
! 509:
! 510: debugmsg(DM_MISC,
! 511: "Debug files->n_next= %d, destdir=%d, ddir=%d",
! 512: files->n_next, destdir, ddir);
! 513:
! 514: if (!sc->sc_name || !*sc->sc_name) {
! 515: destdir = 0;
! 516: ddir = 0;
! 517: }
! 518:
! 519: debugmsg(DM_MISC,
! 520: "Debug sc->sc_name=%x, destdir=%d, ddir=%d",
! 521: sc->sc_name, destdir, ddir);
! 522:
1.1 deraadt 523: for (f = files; f != NULL; f = f->n_next) {
524: if (filev) {
525: for (cpp = filev; *cpp; cpp++)
526: if (strcmp(f->n_name, *cpp) == 0)
527: goto found;
528: continue;
529: }
530: found:
1.2 ! dm 531: if (install(f->n_name, sc->sc_name, ddir, destdir,
! 532: sc->sc_options) > 0)
! 533: ++didupdate;
! 534: opts = sc->sc_options;
1.1 deraadt 535: }
536:
1.2 ! dm 537: } /* end loop for each INSTALL command */
! 538:
! 539: /* if no INSTALL commands present, do default install */
! 540: if (!n) {
! 541: for (f = files; f != NULL; f = f->n_next) {
! 542: if (filev) {
! 543: for (cpp = filev; *cpp; cpp++)
! 544: if (strcmp(f->n_name, *cpp) == 0)
! 545: goto found2;
! 546: continue;
! 547: }
! 548: found2:
! 549: /* ddir & destdir set to zero for default install */
! 550: if (install(f->n_name, NULL, 0, 0, options) > 0)
! 551: ++didupdate;
! 552: }
! 553: }
! 554:
! 555: done:
! 556: /*
! 557: * Run any commands for the entire cmd
! 558: */
! 559: if (didupdate > 0) {
! 560: runcmdspecial(cmd, filev, opts);
! 561: didupdate = 0;
! 562: }
! 563:
! 564: if (!nflag)
! 565: (void) signal(SIGPIPE, cleanup);
! 566:
! 567: for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
1.1 deraadt 568: if (sc->sc_type == NOTIFY)
1.2 ! dm 569: notify(rhost, sc->sc_args, (time_t) 0);
! 570:
! 571: if (!nflag) {
! 572: register struct linkbuf *nextl, *l;
! 573:
! 574: for (l = ihead; l != NULL; free((char *)l), l = nextl) {
! 575: nextl = l->nextp;
! 576: if (contimedout || IS_ON(opts, DO_IGNLNKS) ||
! 577: l->count == 0)
! 578: continue;
! 579: message(MT_WARNING, "%s: Warning: %d %s link%s",
! 580: l->pathname, abs(l->count),
! 581: (l->count > 0) ? "missing" : "extra",
! 582: (l->count == 1) ? "" : "s");
! 583: }
! 584: ihead = NULL;
! 585: }
! 586: }
! 587:
! 588: okname(name)
! 589: register char *name;
! 590: {
! 591: register char *cp = name;
! 592: register int c, isbad;
! 593:
! 594: for (isbad = FALSE; *cp && !isbad; ++cp) {
! 595: c = *cp;
! 596: if (c & 0200)
! 597: isbad = TRUE;
! 598: if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
! 599: isbad = TRUE;
! 600: }
! 601:
! 602: if (isbad) {
! 603: error("Invalid user name \"%s\"\n", name);
! 604: return(0);
! 605: }
! 606: return(1);
! 607: }
! 608:
! 609: static void rcmptime(st, sbcmds, env)
! 610: struct stat *st;
! 611: struct subcmd *sbcmds;
! 612: char **env;
! 613: {
! 614: register DIR *d;
! 615: register DIRENTRY *dp;
! 616: register char *cp;
! 617: char *optarget;
! 618: int len;
! 619:
! 620: debugmsg(DM_CALL, "rcmptime(%x) start", st);
! 621:
! 622: if ((d = opendir((char *) target)) == NULL) {
! 623: error("%s: open directory failed: %s", target, SYSERR);
! 624: return;
! 625: }
! 626: optarget = ptarget;
! 627: len = ptarget - target;
! 628: while (dp = readdir(d)) {
! 629: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
! 630: continue;
! 631: if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) {
! 632: error("%s/%s: Name too long\n", target, dp->d_name);
! 633: continue;
! 634: }
! 635: ptarget = optarget;
! 636: *ptarget++ = '/';
! 637: cp = dp->d_name;
! 638: while (*ptarget++ = *cp++)
! 639: ;
! 640: ptarget--;
! 641: cmptime(target, sbcmds, env);
! 642: }
! 643: (void) closedir((DIR *) d);
! 644: ptarget = optarget;
! 645: *ptarget = '\0';
1.1 deraadt 646: }
647:
648: /*
649: * Compare the mtime of file to the list of time stamps.
650: */
1.2 ! dm 651: static void cmptime(name, sbcmds, env)
1.1 deraadt 652: char *name;
1.2 ! dm 653: struct subcmd *sbcmds;
! 654: char **env;
1.1 deraadt 655: {
1.2 ! dm 656: struct subcmd *sc;
1.1 deraadt 657: struct stat stb;
1.2 ! dm 658: int inlist();
1.1 deraadt 659:
1.2 ! dm 660: debugmsg(DM_CALL, "cmptime(%s)", name);
1.1 deraadt 661:
662: if (except(name))
663: return;
664:
665: if (nflag) {
1.2 ! dm 666: (void) printf("comparing dates: %s\n", name);
1.1 deraadt 667: return;
668: }
669:
670: /*
671: * first time cmptime() is called?
672: */
1.2 ! dm 673: if (ptarget == NULL) {
1.1 deraadt 674: if (exptilde(target, name) == NULL)
675: return;
1.2 ! dm 676: ptarget = name = target;
! 677: while (*ptarget)
! 678: ptarget++;
1.1 deraadt 679: }
1.2 ! dm 680: if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
! 681: error("%s: cannot access file: %s", name, SYSERR);
1.1 deraadt 682: return;
683: }
684:
1.2 ! dm 685: if (S_ISDIR(stb.st_mode)) {
! 686: rcmptime(&stb, sbcmds, env);
1.1 deraadt 687: return;
1.2 ! dm 688: } else if (!S_ISREG(stb.st_mode)) {
! 689: error("%s: not a plain file", name);
1.1 deraadt 690: return;
691: }
692:
1.2 ! dm 693: if (stb.st_mtime > lastmod) {
! 694: message(MT_INFO, "%s: file is newer", name);
! 695: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
! 696: char buf[BUFSIZ];
! 697: if (sc->sc_type != SPECIAL)
! 698: continue;
! 699: if (sc->sc_args != NULL && !inlist(sc->sc_args, name))
! 700: continue;
! 701: (void) sprintf(buf, "%s=%s;%s",
! 702: E_LOCFILE, name, sc->sc_name);
! 703: message(MT_CHANGE, "special \"%s\"", buf);
! 704: if (*env) {
! 705: int len = strlen(*env);
! 706: *env = (char *) xrealloc(*env, len +
! 707: strlen(name) + 2);
! 708: *env[len] = CNULL;
! 709: (void) strcat(*env, name);
! 710: (void) strcat(*env, ":");
! 711: }
! 712: if (IS_ON(options, DO_VERIFY))
! 713: continue;
! 714:
! 715: runcommand(buf);
! 716: }
! 717: }
1.1 deraadt 718: }
719:
1.2 ! dm 720: /*
! 721: * Process commands for comparing files to time stamp files.
! 722: */
! 723: static void dodcolon(cmd, filev)
! 724: struct cmd *cmd;
! 725: char **filev;
1.1 deraadt 726: {
1.2 ! dm 727: register struct subcmd *sc;
! 728: register struct namelist *f;
! 729: register char *cp, **cpp;
! 730: struct stat stb;
! 731: struct namelist *files = cmd->c_files;
! 732: struct subcmd *sbcmds = cmd->c_cmds;
! 733: char *env, *stamp = cmd->c_name;
1.1 deraadt 734:
1.2 ! dm 735: debugmsg(DM_CALL, "dodcolon()");
1.1 deraadt 736:
1.2 ! dm 737: if (files == NULL) {
! 738: error("No files to be updated for target \"%s\"",
! 739: cmd->c_label);
1.1 deraadt 740: return;
741: }
1.2 ! dm 742: if (stat(stamp, &stb) < 0) {
! 743: error("%s: stat failed: %s", stamp, SYSERR);
! 744: return;
! 745: }
! 746:
! 747: debugmsg(DM_MISC, "%s: mtime %d\n", stamp, stb.st_mtime);
! 748:
! 749: env = NULL;
! 750: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
! 751: if (sc->sc_type == CMDSPECIAL) {
! 752: env = (char *) xmalloc(sizeof(E_FILES) + 3);
! 753: (void) sprintf(env, "%s='", E_FILES);
! 754: break;
! 755: }
! 756: }
! 757:
! 758: subcmds = sbcmds;
! 759: filelist = files;
! 760:
! 761: lastmod = stb.st_mtime;
! 762: if (!nflag && !IS_ON(options, DO_VERIFY))
! 763: /*
! 764: * Set atime and mtime to current time
! 765: */
! 766: (void) setfiletime(stamp, (time_t) 0, (time_t) 0);
! 767:
! 768: for (f = files; f != NULL; f = f->n_next) {
! 769: if (filev) {
! 770: for (cpp = filev; *cpp; cpp++)
! 771: if (strcmp(f->n_name, *cpp) == 0)
! 772: goto found;
1.1 deraadt 773: continue;
774: }
1.2 ! dm 775: found:
! 776: ptarget = NULL;
! 777: cmptime(f->n_name, sbcmds, &env);
! 778: }
! 779:
! 780: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
! 781: if (sc->sc_type == NOTIFY)
! 782: notify((char *)NULL, sc->sc_args, (time_t)lastmod);
! 783: else if (sc->sc_type == CMDSPECIAL && env) {
! 784: char *p;
! 785: int len = strlen(env);
! 786:
! 787: env = xrealloc(env,
! 788: len + strlen(sc->sc_name) + 2);
! 789: env[len] = CNULL;
! 790: if (*(p = &env[len - 1]) == ':')
! 791: *p = CNULL;
! 792: (void) strcat(env, "';");
! 793: (void) strcat(env, sc->sc_name);
! 794: message(MT_CHANGE, "cmdspecial \"%s\"", env);
! 795: if (!nflag && IS_OFF(options, DO_VERIFY))
! 796: runcommand(env);
! 797: (void) free(env);
! 798: env = NULL; /* so cmdspecial is only called once */
! 799: }
1.1 deraadt 800: }
1.2 ! dm 801: if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
! 802: (void) unlink(cp);
1.1 deraadt 803: }
804:
805: /*
1.2 ! dm 806: * Return TRUE if file is in the exception list.
1.1 deraadt 807: */
1.2 ! dm 808: extern int except(file)
! 809: char *file;
1.1 deraadt 810: {
1.2 ! dm 811: register struct subcmd *sc;
! 812: register struct namelist *nl;
! 813:
! 814: debugmsg(DM_CALL, "except(%s)", file);
1.1 deraadt 815:
1.2 ! dm 816: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
! 817: if (sc->sc_type == EXCEPT) {
! 818: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
! 819: if (strcmp(file, nl->n_name) == 0)
! 820: return(1);
! 821: continue;
! 822: }
! 823: if (sc->sc_type == PATTERN) {
! 824: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
! 825: char *cp, *re_comp();
! 826:
! 827: if ((cp = re_comp(nl->n_name)) != NULL) {
! 828: error("Regex error \"%s\" for \"%s\".",
! 829: cp, nl->n_name);
! 830: return(0);
! 831: }
! 832: if (re_exec(file) > 0)
! 833: return(1);
! 834: }
! 835: }
1.1 deraadt 836: }
1.2 ! dm 837: return(0);
! 838: }
! 839:
! 840: /*
! 841: * Do a specific command for a specific host
! 842: */
! 843: static void docmdhost(cmd, filev)
! 844: struct cmd *cmd;
! 845: char **filev;
! 846: {
! 847: checkcmd(cmd);
1.1 deraadt 848:
849: /*
1.2 ! dm 850: * If we're multi-threaded and we're the parent, spawn a
! 851: * new child process.
1.1 deraadt 852: */
1.2 ! dm 853: if (do_fork && !amchild) {
! 854: int pid;
! 855:
! 856: /*
! 857: * If we're at maxchildren, wait for number of active
! 858: * children to fall below max number of children.
! 859: */
! 860: while (activechildren >= maxchildren)
! 861: waitup();
! 862:
! 863: pid = spawn(cmd, cmds);
! 864: if (pid == 0)
! 865: /* Child */
! 866: amchild = 1;
! 867: else
! 868: /* Parent */
! 869: return;
1.1 deraadt 870: }
1.2 ! dm 871:
1.1 deraadt 872: /*
1.2 ! dm 873: * Disable NFS checks
1.1 deraadt 874: */
1.2 ! dm 875: if (cmd->c_flags & CMD_NOCHKNFS)
! 876: FLAG_OFF(options, DO_CHKNFS);
! 877:
! 878: if (!nflag) {
! 879: currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
! 880: #if defined(SETARGS)
! 881: setproctitle("update %s", currenthost);
! 882: #endif /* SETARGS */
1.1 deraadt 883: }
884:
1.2 ! dm 885: switch (cmd->c_type) {
! 886: case ARROW:
! 887: doarrow(cmd, filev);
! 888: break;
! 889: case DCOLON:
! 890: dodcolon(cmd, filev);
! 891: break;
! 892: default:
! 893: fatalerr("illegal command type %d", cmd->c_type);
! 894: }
1.1 deraadt 895: }
896:
897: /*
1.2 ! dm 898: * Do a specific command (cmd)
1.1 deraadt 899: */
1.2 ! dm 900: static void docmd(cmd, argc, argv)
! 901: struct cmd *cmd;
! 902: int argc;
! 903: char **argv;
1.1 deraadt 904: {
1.2 ! dm 905: register struct namelist *f;
! 906: register int i;
1.1 deraadt 907:
1.2 ! dm 908: if (argc) {
! 909: for (i = 0; i < argc; i++) {
! 910: if (cmd->c_label != NULL &&
! 911: strcmp(cmd->c_label, argv[i]) == 0) {
! 912: docmdhost(cmd, (char **) NULL);
! 913: return;
! 914: }
! 915: for (f = cmd->c_files; f != NULL; f = f->n_next)
! 916: if (strcmp(f->n_name, argv[i]) == 0) {
! 917: docmdhost(cmd, &argv[i]);
! 918: return;
! 919: }
! 920: }
! 921: } else
! 922: docmdhost(cmd, (char **) NULL);
1.1 deraadt 923: }
924:
925: /*
1.2 ! dm 926: *
! 927: * Multiple hosts are updated at once via a "ring" of at most
! 928: * maxchildren rdist processes. The parent rdist fork()'s a child
! 929: * for a given host. That child will update the given target files
! 930: * and then continue scanning through the remaining targets looking
! 931: * for more work for a given host. Meanwhile, the parent gets the
! 932: * next target command and makes sure that it hasn't encountered
! 933: * that host yet since the children are responsible for everything
! 934: * for that host. If no children have done this host, then check
! 935: * to see if the number of active proc's is less than maxchildren.
! 936: * If so, then spawn a new child for that host. Otherwise, wait
! 937: * for a child to finish.
! 938: *
! 939: */
! 940:
! 941: /*
! 942: * Do the commands in cmds (initialized by yyparse).
1.1 deraadt 943: */
1.2 ! dm 944: extern void docmds(hostlist, argc, argv)
! 945: struct namelist *hostlist;
! 946: int argc;
! 947: char **argv;
1.1 deraadt 948: {
1.2 ! dm 949: register struct cmd *c;
! 950: register char *cp;
! 951: register int i;
! 952:
! 953: (void) signal(SIGHUP, sighandler);
! 954: (void) signal(SIGINT, sighandler);
! 955: (void) signal(SIGQUIT, sighandler);
! 956: (void) signal(SIGTERM, sighandler);
! 957:
! 958: if (!nflag)
! 959: mysetlinebuf(stdout); /* Make output (mostly) clean */
! 960:
! 961: #if defined(USE_STATDB)
! 962: if (!nflag && (dostatdb || juststatdb)) {
! 963: extern long reccount;
! 964: message(MT_INFO, "Making stat database [%s] ... \n",
! 965: gettimestr());
! 966: if (mkstatdb() < 0)
! 967: error("Warning: Make stat database failed.");
! 968: message(MT_INFO,
! 969: "Stat database created: %d files stored [%s].\n",
! 970: reccount, gettimestr());
! 971: if (juststatdb)
! 972: return;
! 973: }
! 974: #endif /* USE_STATDB */
! 975:
! 976: /*
! 977: * Print errors for any command line targets we didn't find.
! 978: * If any errors are found, return to main() which will then exit.
! 979: */
! 980: for (i = 0; i < argc; i++) {
! 981: int found;
1.1 deraadt 982:
1.2 ! dm 983: for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
! 984: if (c->c_label && argv[i] &&
! 985: strcmp(c->c_label, argv[i]) == 0) {
! 986: found = TRUE;
! 987: break;
! 988: }
! 989: }
! 990: if (!found)
! 991: error("Label \"%s\" is not defined in the distfile.",
! 992: argv[i]);
! 993: }
! 994: if (nerrs)
! 995: return;
1.1 deraadt 996:
1.2 ! dm 997: /*
! 998: * Main command loop. Loop through all the commands.
! 999: */
! 1000: for (c = cmds; c != NULL; c = c->c_next) {
! 1001: checkcmd(c);
! 1002: if (do_fork) {
! 1003: /*
! 1004: * Let the children take care of their assigned host
! 1005: */
! 1006: if (amchild) {
! 1007: if (strcmp(c->c_name, currenthost) != 0)
! 1008: continue;
! 1009: } else if (c->c_flags & CMD_ASSIGNED) {
! 1010: /* This cmd has been previously assigned */
! 1011: debugmsg(DM_MISC, "prev assigned: %s\n",
! 1012: c->c_name);
1.1 deraadt 1013: continue;
1014: }
1015: }
1.2 ! dm 1016:
! 1017: if (hostlist) {
! 1018: /* Do specific hosts as specified on command line */
! 1019: register struct namelist *nlptr;
! 1020:
! 1021: for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
! 1022: /*
! 1023: * Try an exact match and then a match
! 1024: * without '@' (if present).
! 1025: */
! 1026: if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
! 1027: ((cp = strchr(c->c_name, '@')) &&
! 1028: strcmp(++cp, nlptr->n_name) == 0))
! 1029: docmd(c, argc, argv);
! 1030: continue;
! 1031: } else
! 1032: /* Do all of the command */
! 1033: docmd(c, argc, argv);
1.1 deraadt 1034: }
1035:
1.2 ! dm 1036: if (do_fork) {
! 1037: /*
! 1038: * We're multi-threaded, so do appropriate shutdown
! 1039: * actions based on whether we're the parent or a child.
! 1040: */
! 1041: if (amchild) {
! 1042: if (!IS_ON(options, DO_QUIET))
! 1043: message(MT_VERBOSE, "updating of %s finished",
! 1044: currenthost);
! 1045: closeconn();
! 1046: cleanup();
! 1047: exit(nerrs);
! 1048: }
1.1 deraadt 1049:
1.2 ! dm 1050: /*
! 1051: * Wait for all remaining active children to finish
! 1052: */
! 1053: while (activechildren > 0) {
! 1054: debugmsg(DM_MISC,
! 1055: "Waiting for %d children to finish.\n",
! 1056: activechildren);
! 1057: waitup();
! 1058: }
! 1059: } else if (!nflag) {
! 1060: /*
! 1061: * We're single-threaded so close down current connection
! 1062: */
! 1063: closeconn();
! 1064: cleanup();
1.1 deraadt 1065: }
1066: }