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