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