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