Annotation of src/usr.bin/rdist/docmd.c, Revision 1.12
1.12 ! deraadt 1: /* $OpenBSD: docmd.c,v 1.11 2002/06/12 06:07:16 mpech 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.12 ! deraadt 42: "$OpenBSD: docmd.c,v 1.11 2002/06/12 06:07:16 mpech 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:
67: extern char target[];
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.1 deraadt 693: if (exptilde(target, name) == NULL)
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) {
724: int len = strlen(*env);
725: *env = (char *) xrealloc(*env, len +
726: strlen(name) + 2);
727: *env[len] = CNULL;
728: (void) strcat(*env, name);
729: (void) strcat(*env, ":");
730: }
731: if (IS_ON(options, DO_VERIFY))
732: continue;
733:
734: runcommand(buf);
735: }
736: }
1.1 deraadt 737: }
738:
1.2 dm 739: /*
740: * Process commands for comparing files to time stamp files.
741: */
742: static void dodcolon(cmd, filev)
743: struct cmd *cmd;
744: char **filev;
1.1 deraadt 745: {
1.10 mpech 746: struct subcmd *sc;
747: struct namelist *f;
748: char *cp, **cpp;
1.2 dm 749: struct stat stb;
750: struct namelist *files = cmd->c_files;
751: struct subcmd *sbcmds = cmd->c_cmds;
752: char *env, *stamp = cmd->c_name;
1.1 deraadt 753:
1.2 dm 754: debugmsg(DM_CALL, "dodcolon()");
1.1 deraadt 755:
1.2 dm 756: if (files == NULL) {
757: error("No files to be updated for target \"%s\"",
758: cmd->c_label);
1.1 deraadt 759: return;
760: }
1.2 dm 761: if (stat(stamp, &stb) < 0) {
762: error("%s: stat failed: %s", stamp, SYSERR);
763: return;
764: }
765:
766: debugmsg(DM_MISC, "%s: mtime %d\n", stamp, stb.st_mtime);
767:
768: env = NULL;
769: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
770: if (sc->sc_type == CMDSPECIAL) {
771: env = (char *) xmalloc(sizeof(E_FILES) + 3);
1.12 ! deraadt 772: (void) snprintf(env, sizeof(E_FILES) + 3, "%s='", E_FILES);
1.2 dm 773: break;
774: }
775: }
776:
777: subcmds = sbcmds;
778: filelist = files;
779:
780: lastmod = stb.st_mtime;
781: if (!nflag && !IS_ON(options, DO_VERIFY))
782: /*
783: * Set atime and mtime to current time
784: */
785: (void) setfiletime(stamp, (time_t) 0, (time_t) 0);
786:
787: for (f = files; f != NULL; f = f->n_next) {
788: if (filev) {
789: for (cpp = filev; *cpp; cpp++)
790: if (strcmp(f->n_name, *cpp) == 0)
791: goto found;
1.1 deraadt 792: continue;
793: }
1.2 dm 794: found:
795: ptarget = NULL;
796: cmptime(f->n_name, sbcmds, &env);
797: }
798:
799: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
800: if (sc->sc_type == NOTIFY)
1.7 millert 801: notify(NULL, sc->sc_args, (time_t)lastmod);
1.2 dm 802: else if (sc->sc_type == CMDSPECIAL && env) {
803: char *p;
804: int len = strlen(env);
805:
806: env = xrealloc(env,
807: len + strlen(sc->sc_name) + 2);
808: env[len] = CNULL;
809: if (*(p = &env[len - 1]) == ':')
810: *p = CNULL;
811: (void) strcat(env, "';");
812: (void) strcat(env, sc->sc_name);
813: message(MT_CHANGE, "cmdspecial \"%s\"", env);
814: if (!nflag && IS_OFF(options, DO_VERIFY))
815: runcommand(env);
816: (void) free(env);
817: env = NULL; /* so cmdspecial is only called once */
818: }
1.1 deraadt 819: }
1.2 dm 820: if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
821: (void) unlink(cp);
1.1 deraadt 822: }
823:
824: /*
1.2 dm 825: * Return TRUE if file is in the exception list.
1.1 deraadt 826: */
1.2 dm 827: extern int except(file)
828: char *file;
1.1 deraadt 829: {
1.10 mpech 830: struct subcmd *sc;
831: struct namelist *nl;
1.2 dm 832:
833: debugmsg(DM_CALL, "except(%s)", file);
1.1 deraadt 834:
1.2 dm 835: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
836: if (sc->sc_type == EXCEPT) {
837: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
838: if (strcmp(file, nl->n_name) == 0)
839: return(1);
840: continue;
841: }
842: if (sc->sc_type == PATTERN) {
843: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
844: char *cp, *re_comp();
845:
846: if ((cp = re_comp(nl->n_name)) != NULL) {
847: error("Regex error \"%s\" for \"%s\".",
848: cp, nl->n_name);
849: return(0);
850: }
851: if (re_exec(file) > 0)
852: return(1);
853: }
854: }
1.1 deraadt 855: }
1.2 dm 856: return(0);
857: }
858:
859: /*
860: * Do a specific command for a specific host
861: */
862: static void docmdhost(cmd, filev)
863: struct cmd *cmd;
864: char **filev;
865: {
866: checkcmd(cmd);
1.1 deraadt 867:
868: /*
1.2 dm 869: * If we're multi-threaded and we're the parent, spawn a
870: * new child process.
1.1 deraadt 871: */
1.2 dm 872: if (do_fork && !amchild) {
1.11 mpech 873: pid_t pid;
1.2 dm 874:
875: /*
876: * If we're at maxchildren, wait for number of active
877: * children to fall below max number of children.
878: */
879: while (activechildren >= maxchildren)
880: waitup();
881:
882: pid = spawn(cmd, cmds);
883: if (pid == 0)
884: /* Child */
885: amchild = 1;
886: else
887: /* Parent */
888: return;
1.1 deraadt 889: }
1.2 dm 890:
1.1 deraadt 891: /*
1.2 dm 892: * Disable NFS checks
1.1 deraadt 893: */
1.2 dm 894: if (cmd->c_flags & CMD_NOCHKNFS)
895: FLAG_OFF(options, DO_CHKNFS);
896:
897: if (!nflag) {
898: currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
1.7 millert 899: #if defined(SETARGS) || defined(HAVE_SETPROCTITLE)
1.2 dm 900: setproctitle("update %s", currenthost);
1.7 millert 901: #endif /* SETARGS || HAVE_SETPROCTITLE */
1.1 deraadt 902: }
903:
1.2 dm 904: switch (cmd->c_type) {
905: case ARROW:
906: doarrow(cmd, filev);
907: break;
908: case DCOLON:
909: dodcolon(cmd, filev);
910: break;
911: default:
912: fatalerr("illegal command type %d", cmd->c_type);
913: }
1.1 deraadt 914: }
915:
916: /*
1.2 dm 917: * Do a specific command (cmd)
1.1 deraadt 918: */
1.2 dm 919: static void docmd(cmd, argc, argv)
920: struct cmd *cmd;
921: int argc;
922: char **argv;
1.1 deraadt 923: {
1.10 mpech 924: struct namelist *f;
925: int i;
1.1 deraadt 926:
1.2 dm 927: if (argc) {
928: for (i = 0; i < argc; i++) {
929: if (cmd->c_label != NULL &&
930: strcmp(cmd->c_label, argv[i]) == 0) {
1.7 millert 931: docmdhost(cmd, NULL);
1.2 dm 932: return;
933: }
934: for (f = cmd->c_files; f != NULL; f = f->n_next)
935: if (strcmp(f->n_name, argv[i]) == 0) {
936: docmdhost(cmd, &argv[i]);
937: return;
938: }
939: }
940: } else
1.7 millert 941: docmdhost(cmd, NULL);
1.1 deraadt 942: }
943:
944: /*
1.2 dm 945: *
946: * Multiple hosts are updated at once via a "ring" of at most
947: * maxchildren rdist processes. The parent rdist fork()'s a child
948: * for a given host. That child will update the given target files
949: * and then continue scanning through the remaining targets looking
950: * for more work for a given host. Meanwhile, the parent gets the
951: * next target command and makes sure that it hasn't encountered
952: * that host yet since the children are responsible for everything
953: * for that host. If no children have done this host, then check
954: * to see if the number of active proc's is less than maxchildren.
955: * If so, then spawn a new child for that host. Otherwise, wait
956: * for a child to finish.
957: *
958: */
959:
960: /*
961: * Do the commands in cmds (initialized by yyparse).
1.1 deraadt 962: */
1.2 dm 963: extern void docmds(hostlist, argc, argv)
964: struct namelist *hostlist;
965: int argc;
966: char **argv;
1.1 deraadt 967: {
1.10 mpech 968: struct cmd *c;
969: char *cp;
970: int i;
1.2 dm 971:
972: (void) signal(SIGHUP, sighandler);
973: (void) signal(SIGINT, sighandler);
974: (void) signal(SIGQUIT, sighandler);
975: (void) signal(SIGTERM, sighandler);
976:
977: if (!nflag)
978: mysetlinebuf(stdout); /* Make output (mostly) clean */
979:
980: #if defined(USE_STATDB)
981: if (!nflag && (dostatdb || juststatdb)) {
982: extern long reccount;
983: message(MT_INFO, "Making stat database [%s] ... \n",
984: gettimestr());
985: if (mkstatdb() < 0)
986: error("Warning: Make stat database failed.");
987: message(MT_INFO,
988: "Stat database created: %d files stored [%s].\n",
989: reccount, gettimestr());
990: if (juststatdb)
991: return;
992: }
993: #endif /* USE_STATDB */
994:
995: /*
996: * Print errors for any command line targets we didn't find.
997: * If any errors are found, return to main() which will then exit.
998: */
999: for (i = 0; i < argc; i++) {
1000: int found;
1.1 deraadt 1001:
1.2 dm 1002: for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
1003: if (c->c_label && argv[i] &&
1004: strcmp(c->c_label, argv[i]) == 0) {
1005: found = TRUE;
1006: break;
1007: }
1008: }
1009: if (!found)
1010: error("Label \"%s\" is not defined in the distfile.",
1011: argv[i]);
1012: }
1013: if (nerrs)
1014: return;
1.1 deraadt 1015:
1.2 dm 1016: /*
1017: * Main command loop. Loop through all the commands.
1018: */
1019: for (c = cmds; c != NULL; c = c->c_next) {
1020: checkcmd(c);
1021: if (do_fork) {
1022: /*
1023: * Let the children take care of their assigned host
1024: */
1025: if (amchild) {
1026: if (strcmp(c->c_name, currenthost) != 0)
1027: continue;
1028: } else if (c->c_flags & CMD_ASSIGNED) {
1029: /* This cmd has been previously assigned */
1030: debugmsg(DM_MISC, "prev assigned: %s\n",
1031: c->c_name);
1.1 deraadt 1032: continue;
1033: }
1034: }
1.2 dm 1035:
1036: if (hostlist) {
1037: /* Do specific hosts as specified on command line */
1.10 mpech 1038: struct namelist *nlptr;
1.2 dm 1039:
1040: for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
1041: /*
1042: * Try an exact match and then a match
1043: * without '@' (if present).
1044: */
1045: if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
1046: ((cp = strchr(c->c_name, '@')) &&
1047: strcmp(++cp, nlptr->n_name) == 0))
1048: docmd(c, argc, argv);
1049: continue;
1050: } else
1051: /* Do all of the command */
1052: docmd(c, argc, argv);
1.1 deraadt 1053: }
1054:
1.2 dm 1055: if (do_fork) {
1056: /*
1057: * We're multi-threaded, so do appropriate shutdown
1058: * actions based on whether we're the parent or a child.
1059: */
1060: if (amchild) {
1061: if (!IS_ON(options, DO_QUIET))
1062: message(MT_VERBOSE, "updating of %s finished",
1063: currenthost);
1064: closeconn();
1065: cleanup();
1066: exit(nerrs);
1067: }
1.1 deraadt 1068:
1.2 dm 1069: /*
1070: * Wait for all remaining active children to finish
1071: */
1072: while (activechildren > 0) {
1073: debugmsg(DM_MISC,
1074: "Waiting for %d children to finish.\n",
1075: activechildren);
1076: waitup();
1077: }
1078: } else if (!nflag) {
1079: /*
1080: * We're single-threaded so close down current connection
1081: */
1082: closeconn();
1083: cleanup();
1.1 deraadt 1084: }
1085: }