Annotation of src/usr.bin/rdist/docmd.c, Revision 1.8
1.8 ! millert 1: /* $OpenBSD: docmd.c,v 1.7 1998/06/26 21:21:09 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.8 ! millert 42: "$OpenBSD: docmd.c,v 1.7 1998/06/26 21:21:09 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:
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.2 dm 102: register struct namelist *to;
103: time_t lmod;
1.1 deraadt 104: {
1.2 dm 105: register int fd, len;
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: */
146: (void) sprintf(buf, "IFS=\" \t\"; export IFS; %s -oi -t",
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: {
224: register struct cmd *pcmd;
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: {
241: register struct cmd *pc;
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: {
313: register char *ruser, *cp;
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.2 dm 357: (void) sprintf(buf, "%.*s -S", sizeof(buf)-5, path_rdistd);
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.2 dm 434: register struct namelist *f;
1.1 deraadt 435: register struct subcmd *sc;
436: register char **cpp;
1.2 dm 437: int n, ddir, destdir, opts = options;
438: struct namelist *files;
439: struct subcmd *sbcmds;
440: char *rhost;
441: int didupdate = 0;
442:
1.7 millert 443: #ifdef __GNUC__
444: (void)&didupdate;
445: (void)&opts;
446: #endif
447:
1.3 dm 448: if (setjmp_ok) {
449: error("reentrant call to doarrow");
450: abort();
451: }
452:
1.2 dm 453: if (!cmd) {
454: debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
455: return;
456: }
1.1 deraadt 457:
1.2 dm 458: files = cmd->c_files;
459: sbcmds = cmd->c_cmds;
460: rhost = cmd->c_name;
1.1 deraadt 461:
462: if (files == NULL) {
1.2 dm 463: error("No files to be updated on %s for target \"%s\"",
464: rhost, cmd->c_label);
1.1 deraadt 465: return;
466: }
467:
1.2 dm 468: debugmsg(DM_CALL, "doarrow(%x, %s, %x) start",
469: files, A(rhost), sbcmds);
470:
471: if (nflag)
472: (void) printf("updating host %s\n", rhost);
1.1 deraadt 473: else {
1.2 dm 474: if (cmd->c_flags & CMD_CONNFAILED) {
475: debugmsg(DM_MISC,
476: "makeconn %s failed before; skipping\n",
477: rhost);
478: return;
479: }
480:
481: if (setjmp(finish_jmpbuf)) {
1.3 dm 482: setjmp_ok = FALSE;
1.2 dm 483: debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
484: markfailed(cmd, cmds);
485: return;
486: }
1.3 dm 487: setjmp_ok = TRUE;
1.2 dm 488:
489: if (!makeconn(rhost)) {
1.3 dm 490: setjmp_ok = FALSE;
1.2 dm 491: markfailed(cmd, cmds);
1.1 deraadt 492: return;
493: }
494: }
495:
1.2 dm 496: subcmds = sbcmds;
497: filelist = files;
498:
499: n = 0;
500: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
501: if (sc->sc_type != INSTALL)
502: continue;
503: n++;
504: /*
505: * destination is a directory if one of the following is true:
506: * a) more than one name specified on left side of -> directive
507: * b) basename of destination in "install" directive is "."
508: * (e.g. install /tmp/.;)
509: * c) name on left side of -> directive is a directory on local system.
510: *
511: * We need 2 destdir flags (destdir and ddir) because single directory
512: * source is handled differently. In this case, ddir is 0 (which
513: * tells install() not to send DIRTARGET directive to remote rdistd)
514: * and destdir is 1 (which tells remfilename() how to build the FILE
515: * variables correctly). In every other case, destdir and ddir will
516: * have the same value.
517: */
518: ddir = files->n_next != NULL; /* destination is a directory */
519: if (!ddir) {
520: struct stat s;
521: int isadir = 0;
522:
523: if (lstat(files->n_name, &s) == 0)
524: isadir = S_ISDIR(s.st_mode);
525: if (!isadir && sc->sc_name && *sc->sc_name)
526: ddir = !strcmp(xbasename(sc->sc_name),".");
527: destdir = isadir | ddir;
528: } else
529: destdir = ddir;
530:
531: debugmsg(DM_MISC,
532: "Debug files->n_next= %d, destdir=%d, ddir=%d",
533: files->n_next, destdir, ddir);
534:
535: if (!sc->sc_name || !*sc->sc_name) {
536: destdir = 0;
537: ddir = 0;
538: }
539:
540: debugmsg(DM_MISC,
541: "Debug sc->sc_name=%x, destdir=%d, ddir=%d",
542: sc->sc_name, destdir, ddir);
543:
1.1 deraadt 544: for (f = files; f != NULL; f = f->n_next) {
545: if (filev) {
546: for (cpp = filev; *cpp; cpp++)
547: if (strcmp(f->n_name, *cpp) == 0)
548: goto found;
549: continue;
550: }
551: found:
1.2 dm 552: if (install(f->n_name, sc->sc_name, ddir, destdir,
553: sc->sc_options) > 0)
554: ++didupdate;
555: opts = sc->sc_options;
1.1 deraadt 556: }
557:
1.2 dm 558: } /* end loop for each INSTALL command */
559:
560: /* if no INSTALL commands present, do default install */
561: if (!n) {
562: for (f = files; f != NULL; f = f->n_next) {
563: if (filev) {
564: for (cpp = filev; *cpp; cpp++)
565: if (strcmp(f->n_name, *cpp) == 0)
566: goto found2;
567: continue;
568: }
569: found2:
570: /* ddir & destdir set to zero for default install */
571: if (install(f->n_name, NULL, 0, 0, options) > 0)
572: ++didupdate;
573: }
574: }
575:
576: done:
577: /*
578: * Run any commands for the entire cmd
579: */
580: if (didupdate > 0) {
581: runcmdspecial(cmd, filev, opts);
582: didupdate = 0;
583: }
584:
585: if (!nflag)
586: (void) signal(SIGPIPE, cleanup);
587:
588: for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
1.1 deraadt 589: if (sc->sc_type == NOTIFY)
1.2 dm 590: notify(rhost, sc->sc_args, (time_t) 0);
591:
592: if (!nflag) {
593: register struct linkbuf *nextl, *l;
594:
1.8 ! millert 595: for (l = ihead; l != NULL; freelinkinfo(l), l = nextl) {
1.2 dm 596: nextl = l->nextp;
597: if (contimedout || IS_ON(opts, DO_IGNLNKS) ||
598: l->count == 0)
599: continue;
600: message(MT_WARNING, "%s: Warning: %d %s link%s",
601: l->pathname, abs(l->count),
602: (l->count > 0) ? "missing" : "extra",
603: (l->count == 1) ? "" : "s");
604: }
605: ihead = NULL;
606: }
1.3 dm 607: setjmp_ok = FALSE;
1.2 dm 608: }
609:
1.7 millert 610: int
1.2 dm 611: okname(name)
612: register char *name;
613: {
614: register char *cp = name;
615: register int c, isbad;
616:
617: for (isbad = FALSE; *cp && !isbad; ++cp) {
618: c = *cp;
619: if (c & 0200)
620: isbad = TRUE;
621: if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
622: isbad = TRUE;
623: }
624:
625: if (isbad) {
626: error("Invalid user name \"%s\"\n", name);
627: return(0);
628: }
629: return(1);
630: }
631:
632: static void rcmptime(st, sbcmds, env)
633: struct stat *st;
634: struct subcmd *sbcmds;
635: char **env;
636: {
637: register DIR *d;
638: register DIRENTRY *dp;
639: register char *cp;
640: char *optarget;
641: int len;
642:
643: debugmsg(DM_CALL, "rcmptime(%x) start", st);
644:
645: if ((d = opendir((char *) target)) == NULL) {
646: error("%s: open directory failed: %s", target, SYSERR);
647: return;
648: }
649: optarget = ptarget;
650: len = ptarget - target;
1.7 millert 651: while ((dp = readdir(d))) {
1.2 dm 652: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
653: continue;
654: if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) {
655: error("%s/%s: Name too long\n", target, dp->d_name);
656: continue;
657: }
658: ptarget = optarget;
659: *ptarget++ = '/';
660: cp = dp->d_name;
1.7 millert 661: while ((*ptarget++ = *cp++))
1.2 dm 662: ;
663: ptarget--;
664: cmptime(target, sbcmds, env);
665: }
666: (void) closedir((DIR *) d);
667: ptarget = optarget;
668: *ptarget = '\0';
1.1 deraadt 669: }
670:
671: /*
672: * Compare the mtime of file to the list of time stamps.
673: */
1.2 dm 674: static void cmptime(name, sbcmds, env)
1.1 deraadt 675: char *name;
1.2 dm 676: struct subcmd *sbcmds;
677: char **env;
1.1 deraadt 678: {
1.2 dm 679: struct subcmd *sc;
1.1 deraadt 680: struct stat stb;
1.2 dm 681: int inlist();
1.1 deraadt 682:
1.2 dm 683: debugmsg(DM_CALL, "cmptime(%s)", name);
1.1 deraadt 684:
685: if (except(name))
686: return;
687:
688: if (nflag) {
1.2 dm 689: (void) printf("comparing dates: %s\n", name);
1.1 deraadt 690: return;
691: }
692:
693: /*
694: * first time cmptime() is called?
695: */
1.2 dm 696: if (ptarget == NULL) {
1.1 deraadt 697: if (exptilde(target, name) == NULL)
698: return;
1.2 dm 699: ptarget = name = target;
700: while (*ptarget)
701: ptarget++;
1.1 deraadt 702: }
1.2 dm 703: if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
704: error("%s: cannot access file: %s", name, SYSERR);
1.1 deraadt 705: return;
706: }
707:
1.2 dm 708: if (S_ISDIR(stb.st_mode)) {
709: rcmptime(&stb, sbcmds, env);
1.1 deraadt 710: return;
1.2 dm 711: } else if (!S_ISREG(stb.st_mode)) {
712: error("%s: not a plain file", name);
1.1 deraadt 713: return;
714: }
715:
1.2 dm 716: if (stb.st_mtime > lastmod) {
717: message(MT_INFO, "%s: file is newer", name);
718: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
719: char buf[BUFSIZ];
720: if (sc->sc_type != SPECIAL)
721: continue;
722: if (sc->sc_args != NULL && !inlist(sc->sc_args, name))
723: continue;
724: (void) sprintf(buf, "%s=%s;%s",
725: E_LOCFILE, name, sc->sc_name);
726: message(MT_CHANGE, "special \"%s\"", buf);
727: if (*env) {
728: int len = strlen(*env);
729: *env = (char *) xrealloc(*env, len +
730: strlen(name) + 2);
731: *env[len] = CNULL;
732: (void) strcat(*env, name);
733: (void) strcat(*env, ":");
734: }
735: if (IS_ON(options, DO_VERIFY))
736: continue;
737:
738: runcommand(buf);
739: }
740: }
1.1 deraadt 741: }
742:
1.2 dm 743: /*
744: * Process commands for comparing files to time stamp files.
745: */
746: static void dodcolon(cmd, filev)
747: struct cmd *cmd;
748: char **filev;
1.1 deraadt 749: {
1.2 dm 750: register struct subcmd *sc;
751: register struct namelist *f;
752: register char *cp, **cpp;
753: struct stat stb;
754: struct namelist *files = cmd->c_files;
755: struct subcmd *sbcmds = cmd->c_cmds;
756: char *env, *stamp = cmd->c_name;
1.1 deraadt 757:
1.2 dm 758: debugmsg(DM_CALL, "dodcolon()");
1.1 deraadt 759:
1.2 dm 760: if (files == NULL) {
761: error("No files to be updated for target \"%s\"",
762: cmd->c_label);
1.1 deraadt 763: return;
764: }
1.2 dm 765: if (stat(stamp, &stb) < 0) {
766: error("%s: stat failed: %s", stamp, SYSERR);
767: return;
768: }
769:
770: debugmsg(DM_MISC, "%s: mtime %d\n", stamp, stb.st_mtime);
771:
772: env = NULL;
773: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
774: if (sc->sc_type == CMDSPECIAL) {
775: env = (char *) xmalloc(sizeof(E_FILES) + 3);
776: (void) sprintf(env, "%s='", E_FILES);
777: break;
778: }
779: }
780:
781: subcmds = sbcmds;
782: filelist = files;
783:
784: lastmod = stb.st_mtime;
785: if (!nflag && !IS_ON(options, DO_VERIFY))
786: /*
787: * Set atime and mtime to current time
788: */
789: (void) setfiletime(stamp, (time_t) 0, (time_t) 0);
790:
791: for (f = files; f != NULL; f = f->n_next) {
792: if (filev) {
793: for (cpp = filev; *cpp; cpp++)
794: if (strcmp(f->n_name, *cpp) == 0)
795: goto found;
1.1 deraadt 796: continue;
797: }
1.2 dm 798: found:
799: ptarget = NULL;
800: cmptime(f->n_name, sbcmds, &env);
801: }
802:
803: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
804: if (sc->sc_type == NOTIFY)
1.7 millert 805: notify(NULL, sc->sc_args, (time_t)lastmod);
1.2 dm 806: else if (sc->sc_type == CMDSPECIAL && env) {
807: char *p;
808: int len = strlen(env);
809:
810: env = xrealloc(env,
811: len + strlen(sc->sc_name) + 2);
812: env[len] = CNULL;
813: if (*(p = &env[len - 1]) == ':')
814: *p = CNULL;
815: (void) strcat(env, "';");
816: (void) strcat(env, sc->sc_name);
817: message(MT_CHANGE, "cmdspecial \"%s\"", env);
818: if (!nflag && IS_OFF(options, DO_VERIFY))
819: runcommand(env);
820: (void) free(env);
821: env = NULL; /* so cmdspecial is only called once */
822: }
1.1 deraadt 823: }
1.2 dm 824: if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
825: (void) unlink(cp);
1.1 deraadt 826: }
827:
828: /*
1.2 dm 829: * Return TRUE if file is in the exception list.
1.1 deraadt 830: */
1.2 dm 831: extern int except(file)
832: char *file;
1.1 deraadt 833: {
1.2 dm 834: register struct subcmd *sc;
835: register struct namelist *nl;
836:
837: debugmsg(DM_CALL, "except(%s)", file);
1.1 deraadt 838:
1.2 dm 839: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
840: if (sc->sc_type == EXCEPT) {
841: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
842: if (strcmp(file, nl->n_name) == 0)
843: return(1);
844: continue;
845: }
846: if (sc->sc_type == PATTERN) {
847: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
848: char *cp, *re_comp();
849:
850: if ((cp = re_comp(nl->n_name)) != NULL) {
851: error("Regex error \"%s\" for \"%s\".",
852: cp, nl->n_name);
853: return(0);
854: }
855: if (re_exec(file) > 0)
856: return(1);
857: }
858: }
1.1 deraadt 859: }
1.2 dm 860: return(0);
861: }
862:
863: /*
864: * Do a specific command for a specific host
865: */
866: static void docmdhost(cmd, filev)
867: struct cmd *cmd;
868: char **filev;
869: {
870: checkcmd(cmd);
1.1 deraadt 871:
872: /*
1.2 dm 873: * If we're multi-threaded and we're the parent, spawn a
874: * new child process.
1.1 deraadt 875: */
1.2 dm 876: if (do_fork && !amchild) {
877: int pid;
878:
879: /*
880: * If we're at maxchildren, wait for number of active
881: * children to fall below max number of children.
882: */
883: while (activechildren >= maxchildren)
884: waitup();
885:
886: pid = spawn(cmd, cmds);
887: if (pid == 0)
888: /* Child */
889: amchild = 1;
890: else
891: /* Parent */
892: return;
1.1 deraadt 893: }
1.2 dm 894:
1.1 deraadt 895: /*
1.2 dm 896: * Disable NFS checks
1.1 deraadt 897: */
1.2 dm 898: if (cmd->c_flags & CMD_NOCHKNFS)
899: FLAG_OFF(options, DO_CHKNFS);
900:
901: if (!nflag) {
902: currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
1.7 millert 903: #if defined(SETARGS) || defined(HAVE_SETPROCTITLE)
1.2 dm 904: setproctitle("update %s", currenthost);
1.7 millert 905: #endif /* SETARGS || HAVE_SETPROCTITLE */
1.1 deraadt 906: }
907:
1.2 dm 908: switch (cmd->c_type) {
909: case ARROW:
910: doarrow(cmd, filev);
911: break;
912: case DCOLON:
913: dodcolon(cmd, filev);
914: break;
915: default:
916: fatalerr("illegal command type %d", cmd->c_type);
917: }
1.1 deraadt 918: }
919:
920: /*
1.2 dm 921: * Do a specific command (cmd)
1.1 deraadt 922: */
1.2 dm 923: static void docmd(cmd, argc, argv)
924: struct cmd *cmd;
925: int argc;
926: char **argv;
1.1 deraadt 927: {
1.2 dm 928: register struct namelist *f;
929: register int i;
1.1 deraadt 930:
1.2 dm 931: if (argc) {
932: for (i = 0; i < argc; i++) {
933: if (cmd->c_label != NULL &&
934: strcmp(cmd->c_label, argv[i]) == 0) {
1.7 millert 935: docmdhost(cmd, NULL);
1.2 dm 936: return;
937: }
938: for (f = cmd->c_files; f != NULL; f = f->n_next)
939: if (strcmp(f->n_name, argv[i]) == 0) {
940: docmdhost(cmd, &argv[i]);
941: return;
942: }
943: }
944: } else
1.7 millert 945: docmdhost(cmd, NULL);
1.1 deraadt 946: }
947:
948: /*
1.2 dm 949: *
950: * Multiple hosts are updated at once via a "ring" of at most
951: * maxchildren rdist processes. The parent rdist fork()'s a child
952: * for a given host. That child will update the given target files
953: * and then continue scanning through the remaining targets looking
954: * for more work for a given host. Meanwhile, the parent gets the
955: * next target command and makes sure that it hasn't encountered
956: * that host yet since the children are responsible for everything
957: * for that host. If no children have done this host, then check
958: * to see if the number of active proc's is less than maxchildren.
959: * If so, then spawn a new child for that host. Otherwise, wait
960: * for a child to finish.
961: *
962: */
963:
964: /*
965: * Do the commands in cmds (initialized by yyparse).
1.1 deraadt 966: */
1.2 dm 967: extern void docmds(hostlist, argc, argv)
968: struct namelist *hostlist;
969: int argc;
970: char **argv;
1.1 deraadt 971: {
1.2 dm 972: register struct cmd *c;
973: register char *cp;
974: register int i;
975:
976: (void) signal(SIGHUP, sighandler);
977: (void) signal(SIGINT, sighandler);
978: (void) signal(SIGQUIT, sighandler);
979: (void) signal(SIGTERM, sighandler);
980:
981: if (!nflag)
982: mysetlinebuf(stdout); /* Make output (mostly) clean */
983:
984: #if defined(USE_STATDB)
985: if (!nflag && (dostatdb || juststatdb)) {
986: extern long reccount;
987: message(MT_INFO, "Making stat database [%s] ... \n",
988: gettimestr());
989: if (mkstatdb() < 0)
990: error("Warning: Make stat database failed.");
991: message(MT_INFO,
992: "Stat database created: %d files stored [%s].\n",
993: reccount, gettimestr());
994: if (juststatdb)
995: return;
996: }
997: #endif /* USE_STATDB */
998:
999: /*
1000: * Print errors for any command line targets we didn't find.
1001: * If any errors are found, return to main() which will then exit.
1002: */
1003: for (i = 0; i < argc; i++) {
1004: int found;
1.1 deraadt 1005:
1.2 dm 1006: for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
1007: if (c->c_label && argv[i] &&
1008: strcmp(c->c_label, argv[i]) == 0) {
1009: found = TRUE;
1010: break;
1011: }
1012: }
1013: if (!found)
1014: error("Label \"%s\" is not defined in the distfile.",
1015: argv[i]);
1016: }
1017: if (nerrs)
1018: return;
1.1 deraadt 1019:
1.2 dm 1020: /*
1021: * Main command loop. Loop through all the commands.
1022: */
1023: for (c = cmds; c != NULL; c = c->c_next) {
1024: checkcmd(c);
1025: if (do_fork) {
1026: /*
1027: * Let the children take care of their assigned host
1028: */
1029: if (amchild) {
1030: if (strcmp(c->c_name, currenthost) != 0)
1031: continue;
1032: } else if (c->c_flags & CMD_ASSIGNED) {
1033: /* This cmd has been previously assigned */
1034: debugmsg(DM_MISC, "prev assigned: %s\n",
1035: c->c_name);
1.1 deraadt 1036: continue;
1037: }
1038: }
1.2 dm 1039:
1040: if (hostlist) {
1041: /* Do specific hosts as specified on command line */
1042: register struct namelist *nlptr;
1043:
1044: for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
1045: /*
1046: * Try an exact match and then a match
1047: * without '@' (if present).
1048: */
1049: if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
1050: ((cp = strchr(c->c_name, '@')) &&
1051: strcmp(++cp, nlptr->n_name) == 0))
1052: docmd(c, argc, argv);
1053: continue;
1054: } else
1055: /* Do all of the command */
1056: docmd(c, argc, argv);
1.1 deraadt 1057: }
1058:
1.2 dm 1059: if (do_fork) {
1060: /*
1061: * We're multi-threaded, so do appropriate shutdown
1062: * actions based on whether we're the parent or a child.
1063: */
1064: if (amchild) {
1065: if (!IS_ON(options, DO_QUIET))
1066: message(MT_VERBOSE, "updating of %s finished",
1067: currenthost);
1068: closeconn();
1069: cleanup();
1070: exit(nerrs);
1071: }
1.1 deraadt 1072:
1.2 dm 1073: /*
1074: * Wait for all remaining active children to finish
1075: */
1076: while (activechildren > 0) {
1077: debugmsg(DM_MISC,
1078: "Waiting for %d children to finish.\n",
1079: activechildren);
1080: waitup();
1081: }
1082: } else if (!nflag) {
1083: /*
1084: * We're single-threaded so close down current connection
1085: */
1086: closeconn();
1087: cleanup();
1.1 deraadt 1088: }
1089: }