Annotation of src/usr.bin/oldrdist/docmd.c, Revision 1.23
1.23 ! guenther 1: /* $OpenBSD: docmd.c,v 1.22 2009/10/27 23:59:41 deraadt Exp $ */
1.3 deraadt 2:
1.1 dm 3: /*
4: * Copyright (c) 1983, 1993
5: * The Regents of the University of California. All rights reserved.
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.18 millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 dm 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:
32: #include "defs.h"
33: #include <setjmp.h>
34: #include <netdb.h>
1.7 millert 35: #include <regex.h>
1.1 dm 36:
37: FILE *lfp; /* log file for recording files updated */
38: struct subcmd *subcmds; /* list of sub-commands for current cmd */
39: jmp_buf env;
40:
1.15 millert 41: static int makeconn(char *);
42: static int okname(char *);
43: static void closeconn(void);
44: static void cmptime(char *);
1.16 millert 45: static void doarrow(char **, struct namelist *, char *, struct subcmd *);
46: static void dodcolon(char **, struct namelist *, char *, struct subcmd *);
1.15 millert 47: static void notify(char *, char *, struct namelist *, time_t);
48: static void rcmptime(struct stat *);
1.1 dm 49:
50: /*
51: * Do the commands in cmds (initialized by yyparse).
52: */
53: void
54: docmds(dhosts, argc, argv)
55: char **dhosts;
56: int argc;
57: char **argv;
58: {
1.14 mpech 59: struct cmd *c;
60: struct namelist *f;
61: char **cpp;
1.1 dm 62: extern struct cmd *cmds;
63:
64: signal(SIGHUP, cleanup);
65: signal(SIGINT, cleanup);
66: signal(SIGQUIT, cleanup);
67: signal(SIGTERM, cleanup);
68:
69: for (c = cmds; c != NULL; c = c->c_next) {
70: if (dhosts != NULL && *dhosts != NULL) {
71: for (cpp = dhosts; *cpp; cpp++)
72: if (strcmp(c->c_name, *cpp) == 0)
73: goto fndhost;
74: continue;
75: }
76: fndhost:
77: if (argc) {
78: for (cpp = argv; *cpp; cpp++) {
79: if (c->c_label != NULL &&
80: strcmp(c->c_label, *cpp) == 0) {
81: cpp = NULL;
82: goto found;
83: }
84: for (f = c->c_files; f != NULL; f = f->n_next)
85: if (strcmp(f->n_name, *cpp) == 0)
86: goto found;
87: }
88: continue;
89: } else
90: cpp = NULL;
91: found:
92: switch (c->c_type) {
93: case ARROW:
94: doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
95: break;
96: case DCOLON:
97: dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
98: break;
99: default:
100: fatal("illegal command type %d\n", c->c_type);
101: }
102: }
103: closeconn();
104: }
105:
106: /*
107: * Process commands for sending files to other machines.
108: */
109: static void
110: doarrow(filev, files, rhost, cmds)
111: char **filev;
112: struct namelist *files;
113: char *rhost;
114: struct subcmd *cmds;
115: {
1.14 mpech 116: struct namelist *f;
117: struct subcmd *sc;
118: char **cpp;
1.1 dm 119: int n, ddir, opts = options;
120:
121: if (debug)
1.13 pvalchev 122: printf("doarrow(%lx, %s, %lx)\n", (long)files, rhost, (long)cmds);
1.1 dm 123:
124: if (files == NULL) {
125: error("no files to be updated\n");
126: return;
127: }
128:
129: subcmds = cmds;
130: ddir = files->n_next != NULL; /* destination is a directory */
131: if (nflag)
132: printf("updating host %s\n", rhost);
133: else {
1.2 deraadt 134: int fd;
135:
1.1 dm 136: if (setjmp(env))
137: goto done;
138: signal(SIGPIPE, lostconn);
139: if (!makeconn(rhost))
140: return;
1.7 millert 141: if ((fd = open(tempfile, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0 ||
1.2 deraadt 142: (lfp = fdopen(fd, "w")) == NULL) {
1.7 millert 143: if (fd >= 0)
144: (void) close(fd);
1.1 dm 145: fatal("cannot open %s\n", tempfile);
146: exit(1);
147: }
148: }
149: for (f = files; f != NULL; f = f->n_next) {
150: if (filev) {
151: for (cpp = filev; *cpp; cpp++)
152: if (strcmp(f->n_name, *cpp) == 0)
153: goto found;
1.4 millert 154: if (!nflag && lfp) {
1.1 dm 155: (void) fclose(lfp);
1.4 millert 156: lfp = NULL;
157: }
1.1 dm 158: continue;
159: }
160: found:
161: n = 0;
162: for (sc = cmds; sc != NULL; sc = sc->sc_next) {
163: if (sc->sc_type != INSTALL)
164: continue;
165: n++;
166: install(f->n_name, sc->sc_name,
167: sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
168: opts = sc->sc_options;
169: }
170: if (n == 0)
171: install(f->n_name, NULL, 0, options);
172: }
173: done:
174: if (!nflag) {
175: (void) signal(SIGPIPE, cleanup);
1.4 millert 176: if (lfp)
177: (void) fclose(lfp);
1.1 dm 178: lfp = NULL;
179: }
180: for (sc = cmds; sc != NULL; sc = sc->sc_next)
181: if (sc->sc_type == NOTIFY)
182: notify(tempfile, rhost, sc->sc_args, 0);
183: if (!nflag) {
1.11 deraadt 184: struct linkbuf *nextihead;
185:
1.1 dm 186: (void) unlink(tempfile);
1.11 deraadt 187: for (; ihead != NULL; ihead = nextihead) {
188: nextihead = ihead->nextp;
1.1 dm 189: if ((opts & IGNLNKS) || ihead->count == 0)
190: continue;
1.19 espie 191: logit(lfp, "%s: Warning: missing links\n",
1.1 dm 192: ihead->pathname);
1.11 deraadt 193: free(ihead);
1.1 dm 194: }
195: }
196: }
197:
198: /*
199: * Create a connection to the rdist server on the machine rhost.
200: */
201: static int
202: makeconn(rhost)
203: char *rhost;
204: {
1.14 mpech 205: char *ruser, *cp;
1.1 dm 206: static char *cur_host = NULL;
1.4 millert 207: #if defined(DIRECT_RCMD)
1.1 dm 208: static int port = -1;
1.4 millert 209: #endif /* DIRECT_RCMD */
1.1 dm 210: char tuser[20];
211: int n;
212: extern char user[];
1.13 pvalchev 213: #if defined(DIRECT_RCMD)
1.17 mpech 214: extern uid_t userid;
1.13 pvalchev 215: #endif
1.1 dm 216:
217: if (debug)
218: printf("makeconn(%s)\n", rhost);
219:
220: if (cur_host != NULL && rem >= 0) {
221: if (strcmp(cur_host, rhost) == 0)
222: return(1);
223: closeconn();
224: }
225: cur_host = rhost;
1.4 millert 226: cp = strchr(rhost, '@');
1.1 dm 227: if (cp != NULL) {
228: char c = *cp;
229:
230: *cp = '\0';
231: strncpy(tuser, rhost, sizeof(tuser)-1);
232: *cp = c;
233: rhost = cp + 1;
234: ruser = tuser;
235: if (*ruser == '\0')
236: ruser = user;
237: else if (!okname(ruser))
238: return(0);
239: } else
240: ruser = user;
241: if (!qflag)
242: printf("updating host %s\n", rhost);
1.7 millert 243: (void) snprintf(buf, sizeof(buf), "%s -Server%s", _PATH_RDIST,
1.5 millert 244: qflag ? " -q" : "");
1.4 millert 245: #if defined(DIRECT_RCMD)
1.1 dm 246: if (port < 0) {
247: struct servent *sp;
248:
249: if ((sp = getservbyname("shell", "tcp")) == NULL)
250: fatal("shell/tcp: unknown service");
251: port = sp->s_port;
252: }
1.4 millert 253: #endif /* !DIRECT_RCMD */
1.1 dm 254:
255: if (debug) {
1.4 millert 256: #if defined(DIRECT_RCMD)
1.1 dm 257: printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
1.4 millert 258: #else /* !DIRECT_RCMD */
259: printf("luser = %s, ruser = %s\n", user, ruser);
260: #endif /* !DIRECT_RCMD */
1.1 dm 261: printf("buf = %s\n", buf);
262: }
263:
264: fflush(stdout);
1.4 millert 265: #if defined(DIRECT_RCMD)
1.1 dm 266: seteuid(0);
267: rem = rcmd(&rhost, port, user, ruser, buf, 0);
268: seteuid(userid);
1.4 millert 269: #else /* !DIRECT_RCMD */
1.7 millert 270: rem = rcmdsh(&rhost, -1, user, ruser, buf, NULL);
1.4 millert 271: #endif /* !DIRECT_RCMD */
1.1 dm 272: if (rem < 0)
273: return(0);
274: cp = buf;
275: if (read(rem, cp, 1) != 1)
276: lostconn(0);
277: if (*cp == 'V') {
278: do {
279: if (read(rem, cp, 1) != 1)
280: lostconn(0);
281: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
282: *--cp = '\0';
283: cp = buf;
284: n = 0;
285: while (*cp >= '0' && *cp <= '9')
286: n = (n * 10) + (*cp++ - '0');
287: if (*cp == '\0' && n == VERSION)
288: return(1);
289: error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
290: } else {
291: error("connection failed: version numbers don't match\n");
292: error("got unexpected input:");
293: do {
294: error("%c", *cp);
295: } while (*cp != '\n' && read(rem, cp, 1) == 1);
296: }
297: closeconn();
298: return(0);
299: }
300:
301: /*
302: * Signal end of previous connection.
303: */
304: static void
305: closeconn()
306: {
307: if (debug)
308: printf("closeconn()\n");
309:
310: if (rem >= 0) {
1.4 millert 311: void (*osig)();
312: osig = signal(SIGPIPE, SIG_IGN);
1.1 dm 313: (void) write(rem, "\2\n", 2);
1.4 millert 314: (void) signal(SIGPIPE, osig);
1.1 dm 315: (void) close(rem);
316: rem = -1;
317: }
318: }
319:
320: void
321: lostconn(signo)
322: int signo;
323: {
324: if (iamremote)
325: cleanup(0);
1.19 espie 326: logit(lfp, "rdist: lost connection\n");
1.4 millert 327: if (rem >= 0) {
328: (void) close(rem);
329: rem = -1;
330: }
1.1 dm 331: longjmp(env, 1);
332: }
333:
334: static int
335: okname(name)
1.14 mpech 336: char *name;
1.1 dm 337: {
1.14 mpech 338: char *cp = name;
339: int c;
1.1 dm 340:
341: do {
342: c = *cp;
343: if (c & 0200)
344: goto bad;
345: if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
346: goto bad;
347: cp++;
348: } while (*cp);
349: return(1);
350: bad:
351: error("invalid user name %s\n", name);
352: return(0);
353: }
354:
355: time_t lastmod;
356: FILE *tfp;
1.8 deraadt 357: extern char *tp;
1.1 dm 358:
359: /*
360: * Process commands for comparing files to time stamp files.
361: */
362: static void
363: dodcolon(filev, files, stamp, cmds)
364: char **filev;
365: struct namelist *files;
366: char *stamp;
367: struct subcmd *cmds;
368: {
1.14 mpech 369: struct subcmd *sc;
370: struct namelist *f;
371: char **cpp;
1.1 dm 372: struct timeval tv[2];
373: struct stat stb;
374:
375: if (debug)
376: printf("dodcolon()\n");
377:
378: if (files == NULL) {
379: error("no files to be updated\n");
380: return;
381: }
382: if (stat(stamp, &stb) < 0) {
383: error("%s: %s\n", stamp, strerror(errno));
384: return;
385: }
386: if (debug)
1.13 pvalchev 387: printf("%s: %lld\n", stamp, (long long)stb.st_mtime);
1.1 dm 388:
389: subcmds = cmds;
390: lastmod = stb.st_mtime;
391: if (nflag || (options & VERIFY))
392: tfp = NULL;
393: else {
1.2 deraadt 394: int fd;
395:
1.7 millert 396: if ((fd = open(tempfile, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0 ||
1.2 deraadt 397: (tfp = fdopen(fd, "w")) == NULL) {
1.10 deraadt 398: error("%s: %s\n", tempfile, strerror(errno));
1.7 millert 399: if (fd >= 0)
400: (void) close(fd);
1.1 dm 401: return;
402: }
1.9 deraadt 403: (void) gettimeofday(&tv[0], NULL);
1.1 dm 404: tv[1] = tv[0];
405: (void) utimes(stamp, tv);
406: }
407:
408: for (f = files; f != NULL; f = f->n_next) {
409: if (filev) {
410: for (cpp = filev; *cpp; cpp++)
411: if (strcmp(f->n_name, *cpp) == 0)
412: goto found;
413: continue;
414: }
415: found:
416: tp = NULL;
417: cmptime(f->n_name);
418: }
419:
420: if (tfp != NULL)
421: (void) fclose(tfp);
422: for (sc = cmds; sc != NULL; sc = sc->sc_next)
423: if (sc->sc_type == NOTIFY)
424: notify(tempfile, NULL, sc->sc_args, lastmod);
425: if (!nflag && !(options & VERIFY))
426: (void) unlink(tempfile);
427: }
428:
429: /*
430: * Compare the mtime of file to the list of time stamps.
431: */
432: static void
433: cmptime(name)
434: char *name;
435: {
436: struct stat stb;
437:
438: if (debug)
439: printf("cmptime(%s)\n", name);
440:
441: if (except(name))
442: return;
443:
444: if (nflag) {
445: printf("comparing dates: %s\n", name);
446: return;
447: }
448:
449: /*
450: * first time cmptime() is called?
451: */
452: if (tp == NULL) {
1.8 deraadt 453: if (exptilde(target, name, sizeof (target)) == NULL)
1.1 dm 454: return;
455: tp = name = target;
456: while (*tp)
457: tp++;
458: }
459: if (access(name, 4) < 0 || stat(name, &stb) < 0) {
460: error("%s: %s\n", name, strerror(errno));
461: return;
462: }
463:
464: switch (stb.st_mode & S_IFMT) {
465: case S_IFREG:
466: break;
467:
468: case S_IFDIR:
469: rcmptime(&stb);
470: return;
471:
472: default:
473: error("%s: not a plain file\n", name);
474: return;
475: }
476:
477: if (stb.st_mtime > lastmod)
1.19 espie 478: logit(tfp, "new: %s\n", name);
1.1 dm 479: }
480:
481: static void
482: rcmptime(st)
483: struct stat *st;
484: {
1.14 mpech 485: DIR *d;
1.23 ! guenther 486: struct dirent *dp;
1.14 mpech 487: char *cp;
1.1 dm 488: char *otp;
489: int len;
490:
491: if (debug)
1.13 pvalchev 492: printf("rcmptime(%lx)\n", (long)st);
1.1 dm 493:
494: if ((d = opendir(target)) == NULL) {
495: error("%s: %s\n", target, strerror(errno));
496: return;
497: }
498: otp = tp;
499: len = tp - target;
500: while (dp = readdir(d)) {
501: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
502: continue;
503: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
504: error("%s/%s: Name too long\n", target, dp->d_name);
505: continue;
506: }
507: tp = otp;
508: *tp++ = '/';
509: cp = dp->d_name;
510: while (*tp++ = *cp++)
511: ;
512: tp--;
513: cmptime(target);
514: }
515: closedir(d);
516: tp = otp;
517: *tp = '\0';
518: }
519:
520: /*
521: * Notify the list of people the changes that were made.
522: * rhost == NULL if we are mailing a list of changes compared to at time
523: * stamp file.
524: */
525: static void
526: notify(file, rhost, to, lmod)
527: char *file, *rhost;
1.14 mpech 528: struct namelist *to;
1.1 dm 529: time_t lmod;
530: {
1.14 mpech 531: int fd, len;
1.1 dm 532: struct stat stb;
533: FILE *pf;
534:
535: if ((options & VERIFY) || to == NULL)
536: return;
537: if (!qflag) {
538: printf("notify ");
539: if (rhost)
540: printf("@%s ", rhost);
541: prnames(to);
542: }
543: if (nflag)
544: return;
545:
1.12 millert 546: if ((fd = open(file, O_RDONLY)) < 0) {
1.1 dm 547: error("%s: %s\n", file, strerror(errno));
548: return;
549: }
550: if (fstat(fd, &stb) < 0) {
551: error("%s: %s\n", file, strerror(errno));
552: (void) close(fd);
553: return;
554: }
555: if (stb.st_size == 0) {
556: (void) close(fd);
557: return;
558: }
559: /*
1.21 sobrado 560: * Create a pipe to a mail program.
1.1 dm 561: */
1.7 millert 562: (void) snprintf(buf, sizeof(buf), "%s -oi -t", _PATH_SENDMAIL);
1.1 dm 563: pf = popen(buf, "w");
564: if (pf == NULL) {
565: error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
566: (void) close(fd);
567: return;
568: }
569: /*
570: * Output the proper header information.
571: */
1.20 deraadt 572: fprintf(pf, "Auto-Submitted: auto-generated\n");
1.1 dm 573: fprintf(pf, "From: rdist (Remote distribution program)\n");
574: fprintf(pf, "To:");
575: if (!any('@', to->n_name) && rhost != NULL)
576: fprintf(pf, " %s@%s", to->n_name, rhost);
577: else
578: fprintf(pf, " %s", to->n_name);
579: to = to->n_next;
580: while (to != NULL) {
581: if (!any('@', to->n_name) && rhost != NULL)
582: fprintf(pf, ", %s@%s", to->n_name, rhost);
583: else
584: fprintf(pf, ", %s", to->n_name);
585: to = to->n_next;
586: }
587: putc('\n', pf);
588: if (rhost != NULL)
589: fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
590: host, rhost);
591: else
592: fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
593: putc('\n', pf);
594:
595: while ((len = read(fd, buf, BUFSIZ)) > 0)
596: (void) fwrite(buf, 1, len, pf);
597: (void) close(fd);
598: (void) pclose(pf);
599: }
600:
601: /*
602: * Return true if name is in the list.
603: */
604: int
605: inlist(list, file)
606: struct namelist *list;
607: char *file;
608: {
1.14 mpech 609: struct namelist *nl;
1.1 dm 610:
611: for (nl = list; nl != NULL; nl = nl->n_next)
612: if (!strcmp(file, nl->n_name))
613: return(1);
614: return(0);
615: }
616:
617: /*
618: * Return TRUE if file is in the exception list.
619: */
620: int
621: except(file)
622: char *file;
623: {
1.14 mpech 624: struct subcmd *sc;
625: struct namelist *nl;
1.7 millert 626: regex_t s;
627: int err;
1.1 dm 628:
629: if (debug)
630: printf("except(%s)\n", file);
631:
632: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
633: if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
634: continue;
635: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
636: if (sc->sc_type == EXCEPT) {
637: if (!strcmp(file, nl->n_name))
638: return(1);
639: continue;
640: }
1.7 millert 641: if ((err = regcomp(&s, nl->n_name, 0)) != 0) {
642: (void) regerror(err, &s, buf, sizeof(buf));
643: error("%s: %s\n", nl->n_name, buf);
644: }
645: if (regexec(&s, file, 0, NULL, 0) == 0) {
646: regfree(&s);
1.1 dm 647: return(1);
1.7 millert 648: }
649: regfree(&s);
1.1 dm 650: }
651: }
652: return(0);
653: }
654:
655: char *
656: colon(cp)
1.14 mpech 657: char *cp;
1.1 dm 658: {
659:
660: while (*cp) {
661: if (*cp == ':')
662: return(cp);
663: if (*cp == '/')
664: return(0);
665: cp++;
666: }
667: return(0);
668: }