Annotation of src/usr.bin/rdist/docmd.c, Revision 1.30
1.30 ! guenther 1: /* $OpenBSD: docmd.c,v 1.29 2014/07/05 07:22:18 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:
261: debugmsg(DM_MISC, "local user = %s remote user = %s\n", luser, ruser);
262: debugmsg(DM_MISC, "Remote command = '%s'\n", cmd);
263:
264: (void) fflush(stdout);
265: (void) fflush(stderr);
266: (void) signal(SIGALRM, sighandler);
267: (void) alarm(RTIMEOUT);
268:
1.6 millert 269: debugmsg(DM_MISC, "Remote shell command = '%s'\n",
270: path_remsh ? path_remsh : "default");
1.2 dm 271: (void) signal(SIGPIPE, SIG_IGN);
1.6 millert 272: desc = rcmdsh(&rhost, -1, luser, ruser, cmd, path_remsh);
1.2 dm 273: if (desc > 0)
274: (void) signal(SIGPIPE, sighandler);
275:
276: (void) alarm(0);
277:
278: return(desc);
1.1 deraadt 279: }
280:
281: /*
282: * Create a connection to the rdist server on the machine rhost.
1.2 dm 283: * Return 0 if the connection fails or 1 if it succeeds.
1.1 deraadt 284: */
1.16 millert 285: static int
286: makeconn(char *rhost)
1.1 deraadt 287: {
1.10 mpech 288: char *ruser, *cp;
1.1 deraadt 289: static char *cur_host = NULL;
1.2 dm 290: extern char *locuser;
1.22 krw 291: extern int64_t min_freefiles, min_freespace;
1.2 dm 292: extern char *remotemsglist;
293: char tuser[BUFSIZ], buf[BUFSIZ];
1.16 millert 294: u_char respbuff[BUFSIZ];
1.1 deraadt 295: int n;
296:
1.2 dm 297: debugmsg(DM_CALL, "makeconn(%s)", rhost);
1.1 deraadt 298:
1.2 dm 299: /*
300: * See if we're already connected to this host
301: */
302: if (cur_host != NULL && rem_w >= 0) {
1.1 deraadt 303: if (strcmp(cur_host, rhost) == 0)
304: return(1);
305: closeconn();
306: }
1.2 dm 307:
308: /*
309: * Determine remote user and current host names
310: */
1.1 deraadt 311: cur_host = rhost;
1.2 dm 312: cp = strchr(rhost, '@');
313:
1.1 deraadt 314: if (cp != NULL) {
315: char c = *cp;
316:
1.2 dm 317: *cp = CNULL;
1.16 millert 318: (void) strlcpy((char *)tuser, rhost, sizeof(tuser));
1.1 deraadt 319: *cp = c;
320: rhost = cp + 1;
321: ruser = tuser;
1.2 dm 322: if (*ruser == CNULL)
323: ruser = locuser;
1.1 deraadt 324: else if (!okname(ruser))
325: return(0);
326: } else
1.2 dm 327: ruser = locuser;
328:
329: if (!IS_ON(options, DO_QUIET))
330: message(MT_VERBOSE, "updating host %s", rhost);
1.1 deraadt 331:
1.16 millert 332: (void) snprintf(buf, sizeof(buf), "%.*s -S",
333: (int)(sizeof(buf)-5), path_rdistd);
1.2 dm 334:
335: if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0)
336: return(0);
1.1 deraadt 337:
1.2 dm 338: /*
339: * First thing received should be S_VERSION
340: */
1.16 millert 341: respbuff[0] = '\0';
1.2 dm 342: n = remline(respbuff, sizeof(respbuff), TRUE);
343: if (n <= 0 || respbuff[0] != S_VERSION) {
1.16 millert 344: if (n > 0)
345: error("Unexpected input from server: \"%s\".", respbuff);
346: else
347: error("No input from server.");
1.2 dm 348: closeconn();
349: return(0);
1.1 deraadt 350: }
351:
1.2 dm 352: /*
353: * For future compatibility we check to see if the server
354: * sent it's version number to us. If it did, we use it,
355: * otherwise, we send our version number to the server and let
356: * it decide if it can handle our protocol version.
357: */
358: if (respbuff[1] == CNULL) {
359: /*
360: * The server wants us to send it our version number
361: */
362: (void) sendcmd(S_VERSION, "%d", VERSION);
363: if (response() < 0)
364: return(0);
1.1 deraadt 365: } else {
1.2 dm 366: /*
367: * The server sent it's version number to us
368: */
369: proto_version = atoi(&respbuff[1]);
370: if (proto_version != VERSION) {
371: fatalerr(
372: "Server version (%d) is not the same as local version (%d).",
373: proto_version, VERSION);
374: return(0);
375: }
1.1 deraadt 376: }
377:
1.2 dm 378: /*
379: * Send config commands
380: */
381: if (host[0]) {
382: (void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host);
383: if (response() < 0)
384: return(0);
385: }
386: if (min_freespace) {
1.24 krw 387: (void) sendcmd(C_SETCONFIG, "%c%lld", SC_FREESPACE,
1.2 dm 388: min_freespace);
389: if (response() < 0)
390: return(0);
391: }
392: if (min_freefiles) {
1.24 krw 393: (void) sendcmd(C_SETCONFIG, "%c%lld", SC_FREEFILES,
1.2 dm 394: min_freefiles);
395: if (response() < 0)
396: return(0);
397: }
398: if (remotemsglist) {
399: (void) sendcmd(C_SETCONFIG, "%c%s", SC_LOGGING, remotemsglist);
400: if (response() < 0)
401: return(0);
1.1 deraadt 402: }
1.16 millert 403: if (strcmp(defowner, "bin") != 0) {
404: (void) sendcmd(C_SETCONFIG, "%c%s", SC_DEFOWNER, defowner);
405: if (response() < 0)
406: return(0);
407: }
408: if (strcmp(defgroup, "bin") != 0) {
409: (void) sendcmd(C_SETCONFIG, "%c%s", SC_DEFGROUP, defgroup);
410: if (response() < 0)
411: return(0);
412: }
1.1 deraadt 413:
414: return(1);
415: }
416:
417: /*
1.2 dm 418: * Process commands for sending files to other machines.
1.1 deraadt 419: */
1.16 millert 420: static void
421: doarrow(struct cmd *cmd, char **filev)
1.1 deraadt 422: {
1.10 mpech 423: struct namelist *f;
424: struct subcmd *sc;
425: char **cpp;
1.9 millert 426: int n, ddir, destdir;
1.16 millert 427: volatile opt_t opts = options;
1.2 dm 428: struct namelist *files;
429: struct subcmd *sbcmds;
430: char *rhost;
1.9 millert 431: volatile int didupdate = 0;
1.7 millert 432:
1.3 dm 433: if (setjmp_ok) {
434: error("reentrant call to doarrow");
435: abort();
436: }
437:
1.2 dm 438: if (!cmd) {
439: debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
440: return;
441: }
1.1 deraadt 442:
1.2 dm 443: files = cmd->c_files;
444: sbcmds = cmd->c_cmds;
445: rhost = cmd->c_name;
1.1 deraadt 446:
447: if (files == NULL) {
1.2 dm 448: error("No files to be updated on %s for target \"%s\"",
449: rhost, cmd->c_label);
1.1 deraadt 450: return;
451: }
452:
1.25 guenther 453: debugmsg(DM_CALL, "doarrow(%p, %s, %p) start",
1.2 dm 454: files, A(rhost), sbcmds);
455:
456: if (nflag)
457: (void) printf("updating host %s\n", rhost);
1.1 deraadt 458: else {
1.2 dm 459: if (cmd->c_flags & CMD_CONNFAILED) {
460: debugmsg(DM_MISC,
461: "makeconn %s failed before; skipping\n",
462: rhost);
463: return;
464: }
465:
466: if (setjmp(finish_jmpbuf)) {
1.3 dm 467: setjmp_ok = FALSE;
1.2 dm 468: debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
469: markfailed(cmd, cmds);
470: return;
471: }
1.3 dm 472: setjmp_ok = TRUE;
1.2 dm 473:
474: if (!makeconn(rhost)) {
1.3 dm 475: setjmp_ok = FALSE;
1.2 dm 476: markfailed(cmd, cmds);
1.1 deraadt 477: return;
478: }
479: }
480:
1.2 dm 481: subcmds = sbcmds;
482: filelist = files;
483:
484: n = 0;
485: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
486: if (sc->sc_type != INSTALL)
487: continue;
488: n++;
489: /*
490: * destination is a directory if one of the following is true:
491: * a) more than one name specified on left side of -> directive
492: * b) basename of destination in "install" directive is "."
493: * (e.g. install /tmp/.;)
494: * c) name on left side of -> directive is a directory on local system.
495: *
496: * We need 2 destdir flags (destdir and ddir) because single directory
497: * source is handled differently. In this case, ddir is 0 (which
498: * tells install() not to send DIRTARGET directive to remote rdistd)
499: * and destdir is 1 (which tells remfilename() how to build the FILE
500: * variables correctly). In every other case, destdir and ddir will
501: * have the same value.
502: */
503: ddir = files->n_next != NULL; /* destination is a directory */
504: if (!ddir) {
505: struct stat s;
506: int isadir = 0;
507:
508: if (lstat(files->n_name, &s) == 0)
509: isadir = S_ISDIR(s.st_mode);
510: if (!isadir && sc->sc_name && *sc->sc_name)
511: ddir = !strcmp(xbasename(sc->sc_name),".");
512: destdir = isadir | ddir;
513: } else
514: destdir = ddir;
515:
516: debugmsg(DM_MISC,
1.25 guenther 517: "Debug files->n_next= %p, destdir=%d, ddir=%d",
1.2 dm 518: files->n_next, destdir, ddir);
519:
520: if (!sc->sc_name || !*sc->sc_name) {
521: destdir = 0;
522: ddir = 0;
523: }
524:
525: debugmsg(DM_MISC,
1.25 guenther 526: "Debug sc->sc_name=%p, destdir=%d, ddir=%d",
1.2 dm 527: sc->sc_name, destdir, ddir);
528:
1.1 deraadt 529: for (f = files; f != NULL; f = f->n_next) {
530: if (filev) {
531: for (cpp = filev; *cpp; cpp++)
532: if (strcmp(f->n_name, *cpp) == 0)
533: goto found;
534: continue;
535: }
536: found:
1.2 dm 537: if (install(f->n_name, sc->sc_name, ddir, destdir,
538: sc->sc_options) > 0)
539: ++didupdate;
540: opts = sc->sc_options;
1.1 deraadt 541: }
542:
1.2 dm 543: } /* end loop for each INSTALL command */
544:
545: /* if no INSTALL commands present, do default install */
546: if (!n) {
547: for (f = files; f != NULL; f = f->n_next) {
548: if (filev) {
549: for (cpp = filev; *cpp; cpp++)
550: if (strcmp(f->n_name, *cpp) == 0)
551: goto found2;
552: continue;
553: }
554: found2:
555: /* ddir & destdir set to zero for default install */
556: if (install(f->n_name, NULL, 0, 0, options) > 0)
557: ++didupdate;
558: }
559: }
560:
561: /*
562: * Run any commands for the entire cmd
563: */
564: if (didupdate > 0) {
1.16 millert 565: runcmdspecial(cmd, opts);
1.2 dm 566: didupdate = 0;
567: }
568:
569: if (!nflag)
570: (void) signal(SIGPIPE, cleanup);
571:
572: for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
1.1 deraadt 573: if (sc->sc_type == NOTIFY)
1.2 dm 574: notify(rhost, sc->sc_args, (time_t) 0);
575:
576: if (!nflag) {
1.10 mpech 577: struct linkbuf *nextl, *l;
1.2 dm 578:
1.8 millert 579: for (l = ihead; l != NULL; freelinkinfo(l), l = nextl) {
1.2 dm 580: nextl = l->nextp;
581: if (contimedout || IS_ON(opts, DO_IGNLNKS) ||
582: l->count == 0)
583: continue;
584: message(MT_WARNING, "%s: Warning: %d %s link%s",
585: l->pathname, abs(l->count),
586: (l->count > 0) ? "missing" : "extra",
587: (l->count == 1) ? "" : "s");
588: }
589: ihead = NULL;
590: }
1.3 dm 591: setjmp_ok = FALSE;
1.2 dm 592: }
593:
1.7 millert 594: int
1.16 millert 595: okname(char *name)
1.2 dm 596: {
1.10 mpech 597: char *cp = name;
598: int c, isbad;
1.2 dm 599:
600: for (isbad = FALSE; *cp && !isbad; ++cp) {
601: c = *cp;
602: if (c & 0200)
603: isbad = TRUE;
604: if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
605: isbad = TRUE;
606: }
607:
608: if (isbad) {
609: error("Invalid user name \"%s\"\n", name);
610: return(0);
611: }
612: return(1);
613: }
614:
1.16 millert 615: static void
616: rcmptime(struct stat *st, struct subcmd *sbcmds, char **env)
1.2 dm 617: {
1.10 mpech 618: DIR *d;
1.28 guenther 619: struct dirent *dp;
1.10 mpech 620: char *cp;
1.2 dm 621: char *optarget;
622: int len;
623:
1.25 guenther 624: debugmsg(DM_CALL, "rcmptime(%p) start", st);
1.2 dm 625:
626: if ((d = opendir((char *) target)) == NULL) {
627: error("%s: open directory failed: %s", target, SYSERR);
628: return;
629: }
630: optarget = ptarget;
631: len = ptarget - target;
1.16 millert 632: while ((dp = readdir(d)) != NULL) {
1.2 dm 633: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
634: continue;
635: if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) {
636: error("%s/%s: Name too long\n", target, dp->d_name);
637: continue;
638: }
639: ptarget = optarget;
640: *ptarget++ = '/';
641: cp = dp->d_name;
1.16 millert 642: while ((*ptarget++ = *cp++) != '\0')
1.2 dm 643: ;
644: ptarget--;
645: cmptime(target, sbcmds, env);
646: }
647: (void) closedir((DIR *) d);
648: ptarget = optarget;
649: *ptarget = '\0';
1.1 deraadt 650: }
651:
652: /*
653: * Compare the mtime of file to the list of time stamps.
654: */
1.16 millert 655: static void
656: cmptime(char *name, struct subcmd *sbcmds, char **env)
1.1 deraadt 657: {
1.2 dm 658: struct subcmd *sc;
1.1 deraadt 659: struct stat stb;
660:
1.2 dm 661: debugmsg(DM_CALL, "cmptime(%s)", name);
1.1 deraadt 662:
663: if (except(name))
664: return;
665:
666: if (nflag) {
1.2 dm 667: (void) printf("comparing dates: %s\n", name);
1.1 deraadt 668: return;
669: }
670:
671: /*
672: * first time cmptime() is called?
673: */
1.2 dm 674: if (ptarget == NULL) {
1.13 millert 675: if (exptilde(target, name, sizeof(target)) == NULL)
1.1 deraadt 676: return;
1.2 dm 677: ptarget = name = target;
678: while (*ptarget)
679: ptarget++;
1.1 deraadt 680: }
1.2 dm 681: if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
682: error("%s: cannot access file: %s", name, SYSERR);
1.1 deraadt 683: return;
684: }
685:
1.2 dm 686: if (S_ISDIR(stb.st_mode)) {
687: rcmptime(&stb, sbcmds, env);
1.1 deraadt 688: return;
1.2 dm 689: } else if (!S_ISREG(stb.st_mode)) {
690: error("%s: not a plain file", name);
1.1 deraadt 691: return;
692: }
693:
1.2 dm 694: if (stb.st_mtime > lastmod) {
695: message(MT_INFO, "%s: file is newer", name);
696: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
697: char buf[BUFSIZ];
698: if (sc->sc_type != SPECIAL)
699: continue;
700: if (sc->sc_args != NULL && !inlist(sc->sc_args, name))
701: continue;
1.16 millert 702: (void) snprintf(buf, sizeof(buf), "%s=%s;%s",
703: E_LOCFILE, name, sc->sc_name);
1.2 dm 704: message(MT_CHANGE, "special \"%s\"", buf);
705: if (*env) {
1.13 millert 706: size_t len = strlen(*env) + strlen(name) + 2;
1.26 guenther 707: *env = xrealloc(*env, len);
1.13 millert 708: (void) strlcat(*env, name, len);
709: (void) strlcat(*env, ":", len);
1.2 dm 710: }
711: if (IS_ON(options, DO_VERIFY))
712: continue;
713:
714: runcommand(buf);
715: }
716: }
1.1 deraadt 717: }
718:
1.2 dm 719: /*
720: * Process commands for comparing files to time stamp files.
721: */
1.16 millert 722: static void
723: dodcolon(struct cmd *cmd, char **filev)
1.1 deraadt 724: {
1.10 mpech 725: struct subcmd *sc;
726: struct namelist *f;
727: char *cp, **cpp;
1.2 dm 728: struct stat stb;
729: struct namelist *files = cmd->c_files;
730: struct subcmd *sbcmds = cmd->c_cmds;
731: char *env, *stamp = cmd->c_name;
1.1 deraadt 732:
1.2 dm 733: debugmsg(DM_CALL, "dodcolon()");
1.1 deraadt 734:
1.2 dm 735: if (files == NULL) {
736: error("No files to be updated for target \"%s\"",
737: cmd->c_label);
1.1 deraadt 738: return;
739: }
1.2 dm 740: if (stat(stamp, &stb) < 0) {
741: error("%s: stat failed: %s", stamp, SYSERR);
742: return;
743: }
744:
1.25 guenther 745: debugmsg(DM_MISC, "%s: mtime %lld\n", stamp, (long long)stb.st_mtime);
1.2 dm 746:
747: env = NULL;
748: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
749: if (sc->sc_type == CMDSPECIAL) {
1.26 guenther 750: env = xmalloc(sizeof(E_FILES) + 3);
1.16 millert 751: (void) snprintf(env, sizeof(E_FILES) + 3,
752: "%s='", E_FILES);
1.2 dm 753: break;
754: }
755: }
756:
757: subcmds = sbcmds;
758: filelist = files;
759:
760: lastmod = stb.st_mtime;
761: if (!nflag && !IS_ON(options, DO_VERIFY))
762: /*
763: * Set atime and mtime to current time
764: */
765: (void) setfiletime(stamp, (time_t) 0, (time_t) 0);
766:
767: for (f = files; f != NULL; f = f->n_next) {
768: if (filev) {
769: for (cpp = filev; *cpp; cpp++)
770: if (strcmp(f->n_name, *cpp) == 0)
771: goto found;
1.1 deraadt 772: continue;
773: }
1.2 dm 774: found:
775: ptarget = NULL;
776: cmptime(f->n_name, sbcmds, &env);
777: }
778:
779: for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
780: if (sc->sc_type == NOTIFY)
1.7 millert 781: notify(NULL, sc->sc_args, (time_t)lastmod);
1.2 dm 782: else if (sc->sc_type == CMDSPECIAL && env) {
1.13 millert 783: size_t len = strlen(env);
784: if (env[len - 1] == ':')
785: env[--len] = CNULL;
786: len += 2 + strlen(sc->sc_name) + 1;
787: env = xrealloc(env, len);
788: (void) strlcat(env, "';", len);
789: (void) strlcat(env, sc->sc_name, len);
1.2 dm 790: message(MT_CHANGE, "cmdspecial \"%s\"", env);
791: if (!nflag && IS_OFF(options, DO_VERIFY))
792: runcommand(env);
793: (void) free(env);
794: env = NULL; /* so cmdspecial is only called once */
795: }
1.1 deraadt 796: }
1.2 dm 797: if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
798: (void) unlink(cp);
1.1 deraadt 799: }
800:
801: /*
1.2 dm 802: * Return TRUE if file is in the exception list.
1.1 deraadt 803: */
1.16 millert 804: int
805: except(char *file)
1.1 deraadt 806: {
1.10 mpech 807: struct subcmd *sc;
808: struct namelist *nl;
1.2 dm 809:
810: debugmsg(DM_CALL, "except(%s)", file);
1.1 deraadt 811:
1.2 dm 812: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
813: if (sc->sc_type == EXCEPT) {
814: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
815: if (strcmp(file, nl->n_name) == 0)
816: return(1);
817: continue;
818: }
819: if (sc->sc_type == PATTERN) {
820: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
1.14 millert 821: char ebuf[BUFSIZ];
822: int ecode = 0;
1.2 dm 823:
1.14 millert 824: /* allocate and compile n_regex as needed */
825: if (nl->n_regex == NULL) {
1.26 guenther 826: nl->n_regex = xmalloc(sizeof(regex_t));
1.14 millert 827: ecode = regcomp(nl->n_regex, nl->n_name,
828: REG_NOSUB);
829: }
830: if (ecode == 0) {
831: ecode = regexec(nl->n_regex, file, 0,
832: NULL, 0);
833: }
834: switch (ecode) {
835: case REG_NOMATCH:
836: break;
837: case 0:
838: return(1); /* match! */
839: default:
840: regerror(ecode, nl->n_regex, ebuf,
841: sizeof(ebuf));
1.2 dm 842: error("Regex error \"%s\" for \"%s\".",
1.14 millert 843: ebuf, nl->n_name);
1.15 millert 844: return(0);
1.2 dm 845: }
846: }
847: }
1.1 deraadt 848: }
1.2 dm 849: return(0);
850: }
851:
852: /*
853: * Do a specific command for a specific host
854: */
1.16 millert 855: static void
856: docmdhost(struct cmd *cmd, char **filev)
1.2 dm 857: {
858: checkcmd(cmd);
1.1 deraadt 859:
860: /*
1.2 dm 861: * If we're multi-threaded and we're the parent, spawn a
862: * new child process.
1.1 deraadt 863: */
1.2 dm 864: if (do_fork && !amchild) {
1.11 mpech 865: pid_t pid;
1.2 dm 866:
867: /*
868: * If we're at maxchildren, wait for number of active
869: * children to fall below max number of children.
870: */
871: while (activechildren >= maxchildren)
872: waitup();
873:
874: pid = spawn(cmd, cmds);
875: if (pid == 0)
876: /* Child */
877: amchild = 1;
878: else
879: /* Parent */
880: return;
1.1 deraadt 881: }
1.2 dm 882:
1.1 deraadt 883: /*
1.2 dm 884: * Disable NFS checks
1.1 deraadt 885: */
1.2 dm 886: if (cmd->c_flags & CMD_NOCHKNFS)
887: FLAG_OFF(options, DO_CHKNFS);
888:
889: if (!nflag) {
890: currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
891: setproctitle("update %s", currenthost);
1.1 deraadt 892: }
893:
1.2 dm 894: switch (cmd->c_type) {
895: case ARROW:
896: doarrow(cmd, filev);
897: break;
898: case DCOLON:
899: dodcolon(cmd, filev);
900: break;
901: default:
902: fatalerr("illegal command type %d", cmd->c_type);
903: }
1.1 deraadt 904: }
905:
906: /*
1.2 dm 907: * Do a specific command (cmd)
1.1 deraadt 908: */
1.16 millert 909: static void
910: docmd(struct cmd *cmd, int argc, char **argv)
1.1 deraadt 911: {
1.10 mpech 912: struct namelist *f;
913: int i;
1.1 deraadt 914:
1.2 dm 915: if (argc) {
916: for (i = 0; i < argc; i++) {
917: if (cmd->c_label != NULL &&
918: strcmp(cmd->c_label, argv[i]) == 0) {
1.7 millert 919: docmdhost(cmd, NULL);
1.2 dm 920: return;
921: }
922: for (f = cmd->c_files; f != NULL; f = f->n_next)
923: if (strcmp(f->n_name, argv[i]) == 0) {
924: docmdhost(cmd, &argv[i]);
925: return;
926: }
927: }
928: } else
1.7 millert 929: docmdhost(cmd, NULL);
1.1 deraadt 930: }
931:
932: /*
1.2 dm 933: *
934: * Multiple hosts are updated at once via a "ring" of at most
935: * maxchildren rdist processes. The parent rdist fork()'s a child
936: * for a given host. That child will update the given target files
937: * and then continue scanning through the remaining targets looking
938: * for more work for a given host. Meanwhile, the parent gets the
939: * next target command and makes sure that it hasn't encountered
940: * that host yet since the children are responsible for everything
941: * for that host. If no children have done this host, then check
942: * to see if the number of active proc's is less than maxchildren.
943: * If so, then spawn a new child for that host. Otherwise, wait
944: * for a child to finish.
945: *
946: */
947:
948: /*
949: * Do the commands in cmds (initialized by yyparse).
1.1 deraadt 950: */
1.16 millert 951: void
952: docmds(struct namelist *hostlist, int argc, char **argv)
1.1 deraadt 953: {
1.10 mpech 954: struct cmd *c;
955: char *cp;
956: int i;
1.2 dm 957:
958: (void) signal(SIGHUP, sighandler);
959: (void) signal(SIGINT, sighandler);
960: (void) signal(SIGQUIT, sighandler);
961: (void) signal(SIGTERM, sighandler);
962:
963: if (!nflag)
1.27 guenther 964: setvbuf(stdout, NULL, _IOLBF, 0);
1.2 dm 965:
966: #if defined(USE_STATDB)
967: if (!nflag && (dostatdb || juststatdb)) {
968: extern long reccount;
969: message(MT_INFO, "Making stat database [%s] ... \n",
970: gettimestr());
971: if (mkstatdb() < 0)
972: error("Warning: Make stat database failed.");
973: message(MT_INFO,
974: "Stat database created: %d files stored [%s].\n",
975: reccount, gettimestr());
976: if (juststatdb)
977: return;
978: }
979: #endif /* USE_STATDB */
980:
981: /*
982: * Print errors for any command line targets we didn't find.
983: * If any errors are found, return to main() which will then exit.
984: */
985: for (i = 0; i < argc; i++) {
986: int found;
1.1 deraadt 987:
1.2 dm 988: for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
989: if (c->c_label && argv[i] &&
990: strcmp(c->c_label, argv[i]) == 0) {
991: found = TRUE;
992: break;
993: }
994: }
995: if (!found)
996: error("Label \"%s\" is not defined in the distfile.",
997: argv[i]);
998: }
999: if (nerrs)
1000: return;
1.1 deraadt 1001:
1.2 dm 1002: /*
1003: * Main command loop. Loop through all the commands.
1004: */
1005: for (c = cmds; c != NULL; c = c->c_next) {
1006: checkcmd(c);
1007: if (do_fork) {
1008: /*
1009: * Let the children take care of their assigned host
1010: */
1011: if (amchild) {
1012: if (strcmp(c->c_name, currenthost) != 0)
1013: continue;
1014: } else if (c->c_flags & CMD_ASSIGNED) {
1015: /* This cmd has been previously assigned */
1016: debugmsg(DM_MISC, "prev assigned: %s\n",
1017: c->c_name);
1.1 deraadt 1018: continue;
1019: }
1020: }
1.2 dm 1021:
1022: if (hostlist) {
1023: /* Do specific hosts as specified on command line */
1.10 mpech 1024: struct namelist *nlptr;
1.2 dm 1025:
1026: for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
1027: /*
1028: * Try an exact match and then a match
1029: * without '@' (if present).
1030: */
1031: if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
1032: ((cp = strchr(c->c_name, '@')) &&
1033: strcmp(++cp, nlptr->n_name) == 0))
1034: docmd(c, argc, argv);
1035: continue;
1036: } else
1037: /* Do all of the command */
1038: docmd(c, argc, argv);
1.1 deraadt 1039: }
1040:
1.2 dm 1041: if (do_fork) {
1042: /*
1043: * We're multi-threaded, so do appropriate shutdown
1044: * actions based on whether we're the parent or a child.
1045: */
1046: if (amchild) {
1047: if (!IS_ON(options, DO_QUIET))
1048: message(MT_VERBOSE, "updating of %s finished",
1049: currenthost);
1050: closeconn();
1.16 millert 1051: cleanup(0);
1.2 dm 1052: exit(nerrs);
1053: }
1.1 deraadt 1054:
1.2 dm 1055: /*
1056: * Wait for all remaining active children to finish
1057: */
1058: while (activechildren > 0) {
1059: debugmsg(DM_MISC,
1060: "Waiting for %d children to finish.\n",
1061: activechildren);
1062: waitup();
1063: }
1064: } else if (!nflag) {
1065: /*
1066: * We're single-threaded so close down current connection
1067: */
1068: closeconn();
1.16 millert 1069: cleanup(0);
1.1 deraadt 1070: }
1071: }