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