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