Annotation of src/usr.bin/oldrdist/docmd.c, Revision 1.7
1.7 ! millert 1: /* $OpenBSD: docmd.c,v 1.6 1996/07/30 20:34:54 millert 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.7 ! millert 38: static char *rcsid = "$OpenBSD: docmd.c,v 1.6 1996/07/30 20:34:54 millert 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:
50: static int makeconn __P((char *));
51: static int okname __P((char *));
52: static void closeconn __P((void));
53: static void cmptime __P((char *));
54: static void doarrow __P((char **,
55: struct namelist *, char *, struct subcmd *));
56: static void dodcolon __P((char **,
57: struct namelist *, char *, struct subcmd *));
58: static void notify __P((char *, char *, struct namelist *, time_t));
59: static void rcmptime __P((struct stat *));
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: {
70: register struct cmd *c;
71: register struct namelist *f;
72: register char **cpp;
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: {
127: register struct namelist *f;
128: register struct subcmd *sc;
129: register char **cpp;
130: int n, ddir, opts = options;
131:
132: if (debug)
133: printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
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) {
195: (void) unlink(tempfile);
196: for (; ihead != NULL; ihead = ihead->nextp) {
197: free(ihead);
198: if ((opts & IGNLNKS) || ihead->count == 0)
199: continue;
200: log(lfp, "%s: Warning: missing links\n",
201: ihead->pathname);
202: }
203: }
204: }
205:
206: /*
207: * Create a connection to the rdist server on the machine rhost.
208: */
209: static int
210: makeconn(rhost)
211: char *rhost;
212: {
213: register char *ruser, *cp;
214: static char *cur_host = NULL;
1.4 millert 215: #if defined(DIRECT_RCMD)
1.1 dm 216: static int port = -1;
1.4 millert 217: #endif /* DIRECT_RCMD */
1.1 dm 218: char tuser[20];
219: int n;
220: extern char user[];
221: extern int userid;
222:
223: if (debug)
224: printf("makeconn(%s)\n", rhost);
225:
226: if (cur_host != NULL && rem >= 0) {
227: if (strcmp(cur_host, rhost) == 0)
228: return(1);
229: closeconn();
230: }
231: cur_host = rhost;
1.4 millert 232: cp = strchr(rhost, '@');
1.1 dm 233: if (cp != NULL) {
234: char c = *cp;
235:
236: *cp = '\0';
237: strncpy(tuser, rhost, sizeof(tuser)-1);
238: *cp = c;
239: rhost = cp + 1;
240: ruser = tuser;
241: if (*ruser == '\0')
242: ruser = user;
243: else if (!okname(ruser))
244: return(0);
245: } else
246: ruser = user;
247: if (!qflag)
248: printf("updating host %s\n", rhost);
1.7 ! millert 249: (void) snprintf(buf, sizeof(buf), "%s -Server%s", _PATH_RDIST,
1.5 millert 250: qflag ? " -q" : "");
1.4 millert 251: #if defined(DIRECT_RCMD)
1.1 dm 252: if (port < 0) {
253: struct servent *sp;
254:
255: if ((sp = getservbyname("shell", "tcp")) == NULL)
256: fatal("shell/tcp: unknown service");
257: port = sp->s_port;
258: }
1.4 millert 259: #endif /* !DIRECT_RCMD */
1.1 dm 260:
261: if (debug) {
1.4 millert 262: #if defined(DIRECT_RCMD)
1.1 dm 263: printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
1.4 millert 264: #else /* !DIRECT_RCMD */
265: printf("luser = %s, ruser = %s\n", user, ruser);
266: #endif /* !DIRECT_RCMD */
1.1 dm 267: printf("buf = %s\n", buf);
268: }
269:
270: fflush(stdout);
1.4 millert 271: #if defined(DIRECT_RCMD)
1.1 dm 272: seteuid(0);
273: rem = rcmd(&rhost, port, user, ruser, buf, 0);
274: seteuid(userid);
1.4 millert 275: #else /* !DIRECT_RCMD */
1.7 ! millert 276: rem = rcmdsh(&rhost, -1, user, ruser, buf, NULL);
1.4 millert 277: #endif /* !DIRECT_RCMD */
1.1 dm 278: if (rem < 0)
279: return(0);
280: cp = buf;
281: if (read(rem, cp, 1) != 1)
282: lostconn(0);
283: if (*cp == 'V') {
284: do {
285: if (read(rem, cp, 1) != 1)
286: lostconn(0);
287: } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
288: *--cp = '\0';
289: cp = buf;
290: n = 0;
291: while (*cp >= '0' && *cp <= '9')
292: n = (n * 10) + (*cp++ - '0');
293: if (*cp == '\0' && n == VERSION)
294: return(1);
295: error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
296: } else {
297: error("connection failed: version numbers don't match\n");
298: error("got unexpected input:");
299: do {
300: error("%c", *cp);
301: } while (*cp != '\n' && read(rem, cp, 1) == 1);
302: }
303: closeconn();
304: return(0);
305: }
306:
307: /*
308: * Signal end of previous connection.
309: */
310: static void
311: closeconn()
312: {
313: if (debug)
314: printf("closeconn()\n");
315:
316: if (rem >= 0) {
1.4 millert 317: void (*osig)();
318: osig = signal(SIGPIPE, SIG_IGN);
1.1 dm 319: (void) write(rem, "\2\n", 2);
1.4 millert 320: (void) signal(SIGPIPE, osig);
1.1 dm 321: (void) close(rem);
322: rem = -1;
323: }
324: }
325:
326: void
327: lostconn(signo)
328: int signo;
329: {
330: if (iamremote)
331: cleanup(0);
332: log(lfp, "rdist: lost connection\n");
1.4 millert 333: if (rem >= 0) {
334: (void) close(rem);
335: rem = -1;
336: }
1.1 dm 337: longjmp(env, 1);
338: }
339:
340: static int
341: okname(name)
342: register char *name;
343: {
344: register char *cp = name;
345: register int c;
346:
347: do {
348: c = *cp;
349: if (c & 0200)
350: goto bad;
351: if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
352: goto bad;
353: cp++;
354: } while (*cp);
355: return(1);
356: bad:
357: error("invalid user name %s\n", name);
358: return(0);
359: }
360:
361: time_t lastmod;
362: FILE *tfp;
363: extern char target[], *tp;
364:
365: /*
366: * Process commands for comparing files to time stamp files.
367: */
368: static void
369: dodcolon(filev, files, stamp, cmds)
370: char **filev;
371: struct namelist *files;
372: char *stamp;
373: struct subcmd *cmds;
374: {
375: register struct subcmd *sc;
376: register struct namelist *f;
377: register char **cpp;
378: struct timeval tv[2];
379: struct timezone tz;
380: struct stat stb;
381:
382: if (debug)
383: printf("dodcolon()\n");
384:
385: if (files == NULL) {
386: error("no files to be updated\n");
387: return;
388: }
389: if (stat(stamp, &stb) < 0) {
390: error("%s: %s\n", stamp, strerror(errno));
391: return;
392: }
393: if (debug)
394: printf("%s: %ld\n", stamp, stb.st_mtime);
395:
396: subcmds = cmds;
397: lastmod = stb.st_mtime;
398: if (nflag || (options & VERIFY))
399: tfp = NULL;
400: else {
1.2 deraadt 401: int fd;
402:
1.7 ! millert 403: if ((fd = open(tempfile, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0 ||
1.2 deraadt 404: (tfp = fdopen(fd, "w")) == NULL) {
1.1 dm 405: error("%s: %s\n", stamp, strerror(errno));
1.7 ! millert 406: if (fd >= 0)
! 407: (void) close(fd);
1.1 dm 408: return;
409: }
410: (void) gettimeofday(&tv[0], &tz);
411: tv[1] = tv[0];
412: (void) utimes(stamp, tv);
413: }
414:
415: for (f = files; f != NULL; f = f->n_next) {
416: if (filev) {
417: for (cpp = filev; *cpp; cpp++)
418: if (strcmp(f->n_name, *cpp) == 0)
419: goto found;
420: continue;
421: }
422: found:
423: tp = NULL;
424: cmptime(f->n_name);
425: }
426:
427: if (tfp != NULL)
428: (void) fclose(tfp);
429: for (sc = cmds; sc != NULL; sc = sc->sc_next)
430: if (sc->sc_type == NOTIFY)
431: notify(tempfile, NULL, sc->sc_args, lastmod);
432: if (!nflag && !(options & VERIFY))
433: (void) unlink(tempfile);
434: }
435:
436: /*
437: * Compare the mtime of file to the list of time stamps.
438: */
439: static void
440: cmptime(name)
441: char *name;
442: {
443: struct stat stb;
444:
445: if (debug)
446: printf("cmptime(%s)\n", name);
447:
448: if (except(name))
449: return;
450:
451: if (nflag) {
452: printf("comparing dates: %s\n", name);
453: return;
454: }
455:
456: /*
457: * first time cmptime() is called?
458: */
459: if (tp == NULL) {
460: if (exptilde(target, name) == NULL)
461: return;
462: tp = name = target;
463: while (*tp)
464: tp++;
465: }
466: if (access(name, 4) < 0 || stat(name, &stb) < 0) {
467: error("%s: %s\n", name, strerror(errno));
468: return;
469: }
470:
471: switch (stb.st_mode & S_IFMT) {
472: case S_IFREG:
473: break;
474:
475: case S_IFDIR:
476: rcmptime(&stb);
477: return;
478:
479: default:
480: error("%s: not a plain file\n", name);
481: return;
482: }
483:
484: if (stb.st_mtime > lastmod)
485: log(tfp, "new: %s\n", name);
486: }
487:
488: static void
489: rcmptime(st)
490: struct stat *st;
491: {
492: register DIR *d;
493: register struct direct *dp;
494: register char *cp;
495: char *otp;
496: int len;
497:
498: if (debug)
499: printf("rcmptime(%x)\n", st);
500:
501: if ((d = opendir(target)) == NULL) {
502: error("%s: %s\n", target, strerror(errno));
503: return;
504: }
505: otp = tp;
506: len = tp - target;
507: while (dp = readdir(d)) {
508: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
509: continue;
510: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
511: error("%s/%s: Name too long\n", target, dp->d_name);
512: continue;
513: }
514: tp = otp;
515: *tp++ = '/';
516: cp = dp->d_name;
517: while (*tp++ = *cp++)
518: ;
519: tp--;
520: cmptime(target);
521: }
522: closedir(d);
523: tp = otp;
524: *tp = '\0';
525: }
526:
527: /*
528: * Notify the list of people the changes that were made.
529: * rhost == NULL if we are mailing a list of changes compared to at time
530: * stamp file.
531: */
532: static void
533: notify(file, rhost, to, lmod)
534: char *file, *rhost;
535: register struct namelist *to;
536: time_t lmod;
537: {
538: register int fd, len;
539: struct stat stb;
540: FILE *pf;
541:
542: if ((options & VERIFY) || to == NULL)
543: return;
544: if (!qflag) {
545: printf("notify ");
546: if (rhost)
547: printf("@%s ", rhost);
548: prnames(to);
549: }
550: if (nflag)
551: return;
552:
553: if ((fd = open(file, 0)) < 0) {
554: error("%s: %s\n", file, strerror(errno));
555: return;
556: }
557: if (fstat(fd, &stb) < 0) {
558: error("%s: %s\n", file, strerror(errno));
559: (void) close(fd);
560: return;
561: }
562: if (stb.st_size == 0) {
563: (void) close(fd);
564: return;
565: }
566: /*
567: * Create a pipe to mailling program.
568: */
1.7 ! millert 569: (void) snprintf(buf, sizeof(buf), "%s -oi -t", _PATH_SENDMAIL);
1.1 dm 570: pf = popen(buf, "w");
571: if (pf == NULL) {
572: error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
573: (void) close(fd);
574: return;
575: }
576: /*
577: * Output the proper header information.
578: */
579: fprintf(pf, "From: rdist (Remote distribution program)\n");
580: fprintf(pf, "To:");
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: while (to != NULL) {
587: if (!any('@', to->n_name) && rhost != NULL)
588: fprintf(pf, ", %s@%s", to->n_name, rhost);
589: else
590: fprintf(pf, ", %s", to->n_name);
591: to = to->n_next;
592: }
593: putc('\n', pf);
594: if (rhost != NULL)
595: fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
596: host, rhost);
597: else
598: fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
599: putc('\n', pf);
600:
601: while ((len = read(fd, buf, BUFSIZ)) > 0)
602: (void) fwrite(buf, 1, len, pf);
603: (void) close(fd);
604: (void) pclose(pf);
605: }
606:
607: /*
608: * Return true if name is in the list.
609: */
610: int
611: inlist(list, file)
612: struct namelist *list;
613: char *file;
614: {
615: register struct namelist *nl;
616:
617: for (nl = list; nl != NULL; nl = nl->n_next)
618: if (!strcmp(file, nl->n_name))
619: return(1);
620: return(0);
621: }
622:
623: /*
624: * Return TRUE if file is in the exception list.
625: */
626: int
627: except(file)
628: char *file;
629: {
630: register struct subcmd *sc;
631: register struct namelist *nl;
1.7 ! millert 632: regex_t s;
! 633: int err;
1.1 dm 634:
635: if (debug)
636: printf("except(%s)\n", file);
637:
638: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
639: if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
640: continue;
641: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
642: if (sc->sc_type == EXCEPT) {
643: if (!strcmp(file, nl->n_name))
644: return(1);
645: continue;
646: }
1.7 ! millert 647: if ((err = regcomp(&s, nl->n_name, 0)) != 0) {
! 648: (void) regerror(err, &s, buf, sizeof(buf));
! 649: error("%s: %s\n", nl->n_name, buf);
! 650: }
! 651: if (regexec(&s, file, 0, NULL, 0) == 0) {
! 652: regfree(&s);
1.1 dm 653: return(1);
1.7 ! millert 654: }
! 655: regfree(&s);
1.1 dm 656: }
657: }
658: return(0);
659: }
660:
661: char *
662: colon(cp)
663: register char *cp;
664: {
665:
666: while (*cp) {
667: if (*cp == ':')
668: return(cp);
669: if (*cp == '/')
670: return(0);
671: cp++;
672: }
673: return(0);
674: }