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