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