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