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