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