Annotation of src/usr.bin/oldrdist/docmd.c, Revision 1.5
1.5 ! millert 1: /* $OpenBSD: docmd.c,v 1.4 1996/07/19 21:57:31 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.5 ! millert 38: static char *rcsid = "$OpenBSD: docmd.c,v 1.4 1996/07/19 21:57:31 millert Exp $";
1.1 dm 39: #endif /* not lint */
40:
41: #include "defs.h"
42: #include <setjmp.h>
43: #include <netdb.h>
44:
45: FILE *lfp; /* log file for recording files updated */
46: struct subcmd *subcmds; /* list of sub-commands for current cmd */
47: jmp_buf env;
48:
49: static int makeconn __P((char *));
50: static int okname __P((char *));
51: static void closeconn __P((void));
52: static void cmptime __P((char *));
53: static void doarrow __P((char **,
54: struct namelist *, char *, struct subcmd *));
55: static void dodcolon __P((char **,
56: struct namelist *, char *, struct subcmd *));
57: static void notify __P((char *, char *, struct namelist *, time_t));
58: static void rcmptime __P((struct stat *));
59:
60: /*
61: * Do the commands in cmds (initialized by yyparse).
62: */
63: void
64: docmds(dhosts, argc, argv)
65: char **dhosts;
66: int argc;
67: char **argv;
68: {
69: register struct cmd *c;
70: register struct namelist *f;
71: register char **cpp;
72: extern struct cmd *cmds;
73:
74: signal(SIGHUP, cleanup);
75: signal(SIGINT, cleanup);
76: signal(SIGQUIT, cleanup);
77: signal(SIGTERM, cleanup);
78:
79: for (c = cmds; c != NULL; c = c->c_next) {
80: if (dhosts != NULL && *dhosts != NULL) {
81: for (cpp = dhosts; *cpp; cpp++)
82: if (strcmp(c->c_name, *cpp) == 0)
83: goto fndhost;
84: continue;
85: }
86: fndhost:
87: if (argc) {
88: for (cpp = argv; *cpp; cpp++) {
89: if (c->c_label != NULL &&
90: strcmp(c->c_label, *cpp) == 0) {
91: cpp = NULL;
92: goto found;
93: }
94: for (f = c->c_files; f != NULL; f = f->n_next)
95: if (strcmp(f->n_name, *cpp) == 0)
96: goto found;
97: }
98: continue;
99: } else
100: cpp = NULL;
101: found:
102: switch (c->c_type) {
103: case ARROW:
104: doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
105: break;
106: case DCOLON:
107: dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
108: break;
109: default:
110: fatal("illegal command type %d\n", c->c_type);
111: }
112: }
113: closeconn();
114: }
115:
116: /*
117: * Process commands for sending files to other machines.
118: */
119: static void
120: doarrow(filev, files, rhost, cmds)
121: char **filev;
122: struct namelist *files;
123: char *rhost;
124: struct subcmd *cmds;
125: {
126: register struct namelist *f;
127: register struct subcmd *sc;
128: register char **cpp;
129: int n, ddir, opts = options;
130:
131: if (debug)
132: printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
133:
134: if (files == NULL) {
135: error("no files to be updated\n");
136: return;
137: }
138:
139: subcmds = cmds;
140: ddir = files->n_next != NULL; /* destination is a directory */
141: if (nflag)
142: printf("updating host %s\n", rhost);
143: else {
1.2 deraadt 144: int fd;
145:
1.1 dm 146: if (setjmp(env))
147: goto done;
148: signal(SIGPIPE, lostconn);
149: if (!makeconn(rhost))
150: return;
1.2 deraadt 151: if ((fd = open(tempfile, O_RDWR|O_EXCL|O_CREAT, 0666)) == -1 ||
152: (lfp = fdopen(fd, "w")) == NULL) {
153: if (fd != -1)
154: close(fd);
1.1 dm 155: fatal("cannot open %s\n", tempfile);
156: exit(1);
157: }
158: }
159: for (f = files; f != NULL; f = f->n_next) {
160: if (filev) {
161: for (cpp = filev; *cpp; cpp++)
162: if (strcmp(f->n_name, *cpp) == 0)
163: goto found;
1.4 millert 164: if (!nflag && lfp) {
1.1 dm 165: (void) fclose(lfp);
1.4 millert 166: lfp = NULL;
167: }
1.1 dm 168: continue;
169: }
170: found:
171: n = 0;
172: for (sc = cmds; sc != NULL; sc = sc->sc_next) {
173: if (sc->sc_type != INSTALL)
174: continue;
175: n++;
176: install(f->n_name, sc->sc_name,
177: sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
178: opts = sc->sc_options;
179: }
180: if (n == 0)
181: install(f->n_name, NULL, 0, options);
182: }
183: done:
184: if (!nflag) {
185: (void) signal(SIGPIPE, cleanup);
1.4 millert 186: if (lfp)
187: (void) fclose(lfp);
1.1 dm 188: lfp = NULL;
189: }
190: for (sc = cmds; sc != NULL; sc = sc->sc_next)
191: if (sc->sc_type == NOTIFY)
192: notify(tempfile, rhost, sc->sc_args, 0);
193: if (!nflag) {
194: (void) unlink(tempfile);
195: for (; ihead != NULL; ihead = ihead->nextp) {
196: free(ihead);
197: if ((opts & IGNLNKS) || ihead->count == 0)
198: continue;
199: log(lfp, "%s: Warning: missing links\n",
200: ihead->pathname);
201: }
202: }
203: }
204:
205: /*
206: * Create a connection to the rdist server on the machine rhost.
207: */
208: static int
209: makeconn(rhost)
210: char *rhost;
211: {
212: register char *ruser, *cp;
213: static char *cur_host = NULL;
1.4 millert 214: #if defined(DIRECT_RCMD)
1.1 dm 215: static int port = -1;
1.4 millert 216: #endif /* DIRECT_RCMD */
1.1 dm 217: char tuser[20];
218: int n;
219: extern char user[];
220: extern int userid;
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.5 ! millert 248: (void) snprintf(buf, BUFSIZ, "%s -Server%s", _PATH_RDIST,
! 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 */
275: rem = rshrcmd(&rhost, -1, user, ruser, buf, 0);
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)
341: register char *name;
342: {
343: register char *cp = name;
344: register int c;
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;
362: extern char target[], *tp;
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: {
374: register struct subcmd *sc;
375: register struct namelist *f;
376: register char **cpp;
377: struct timeval tv[2];
378: struct timezone tz;
379: struct stat stb;
380:
381: if (debug)
382: printf("dodcolon()\n");
383:
384: if (files == NULL) {
385: error("no files to be updated\n");
386: return;
387: }
388: if (stat(stamp, &stb) < 0) {
389: error("%s: %s\n", stamp, strerror(errno));
390: return;
391: }
392: if (debug)
393: printf("%s: %ld\n", stamp, stb.st_mtime);
394:
395: subcmds = cmds;
396: lastmod = stb.st_mtime;
397: if (nflag || (options & VERIFY))
398: tfp = NULL;
399: else {
1.2 deraadt 400: int fd;
401:
402: if ((fd = open(tempfile, O_RDWR|O_EXCL|O_CREAT, 0666)) == -1 ||
403: (tfp = fdopen(fd, "w")) == NULL) {
404: if (fd != -1)
405: close(fd);
1.1 dm 406: error("%s: %s\n", stamp, strerror(errno));
407: return;
408: }
409: (void) gettimeofday(&tv[0], &tz);
410: tv[1] = tv[0];
411: (void) utimes(stamp, tv);
412: }
413:
414: for (f = files; f != NULL; f = f->n_next) {
415: if (filev) {
416: for (cpp = filev; *cpp; cpp++)
417: if (strcmp(f->n_name, *cpp) == 0)
418: goto found;
419: continue;
420: }
421: found:
422: tp = NULL;
423: cmptime(f->n_name);
424: }
425:
426: if (tfp != NULL)
427: (void) fclose(tfp);
428: for (sc = cmds; sc != NULL; sc = sc->sc_next)
429: if (sc->sc_type == NOTIFY)
430: notify(tempfile, NULL, sc->sc_args, lastmod);
431: if (!nflag && !(options & VERIFY))
432: (void) unlink(tempfile);
433: }
434:
435: /*
436: * Compare the mtime of file to the list of time stamps.
437: */
438: static void
439: cmptime(name)
440: char *name;
441: {
442: struct stat stb;
443:
444: if (debug)
445: printf("cmptime(%s)\n", name);
446:
447: if (except(name))
448: return;
449:
450: if (nflag) {
451: printf("comparing dates: %s\n", name);
452: return;
453: }
454:
455: /*
456: * first time cmptime() is called?
457: */
458: if (tp == NULL) {
459: if (exptilde(target, name) == NULL)
460: return;
461: tp = name = target;
462: while (*tp)
463: tp++;
464: }
465: if (access(name, 4) < 0 || stat(name, &stb) < 0) {
466: error("%s: %s\n", name, strerror(errno));
467: return;
468: }
469:
470: switch (stb.st_mode & S_IFMT) {
471: case S_IFREG:
472: break;
473:
474: case S_IFDIR:
475: rcmptime(&stb);
476: return;
477:
478: default:
479: error("%s: not a plain file\n", name);
480: return;
481: }
482:
483: if (stb.st_mtime > lastmod)
484: log(tfp, "new: %s\n", name);
485: }
486:
487: static void
488: rcmptime(st)
489: struct stat *st;
490: {
491: register DIR *d;
492: register struct direct *dp;
493: register char *cp;
494: char *otp;
495: int len;
496:
497: if (debug)
498: printf("rcmptime(%x)\n", st);
499:
500: if ((d = opendir(target)) == NULL) {
501: error("%s: %s\n", target, strerror(errno));
502: return;
503: }
504: otp = tp;
505: len = tp - target;
506: while (dp = readdir(d)) {
507: if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
508: continue;
509: if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
510: error("%s/%s: Name too long\n", target, dp->d_name);
511: continue;
512: }
513: tp = otp;
514: *tp++ = '/';
515: cp = dp->d_name;
516: while (*tp++ = *cp++)
517: ;
518: tp--;
519: cmptime(target);
520: }
521: closedir(d);
522: tp = otp;
523: *tp = '\0';
524: }
525:
526: /*
527: * Notify the list of people the changes that were made.
528: * rhost == NULL if we are mailing a list of changes compared to at time
529: * stamp file.
530: */
531: static void
532: notify(file, rhost, to, lmod)
533: char *file, *rhost;
534: register struct namelist *to;
535: time_t lmod;
536: {
537: register int fd, len;
538: struct stat stb;
539: FILE *pf;
540:
541: if ((options & VERIFY) || to == NULL)
542: return;
543: if (!qflag) {
544: printf("notify ");
545: if (rhost)
546: printf("@%s ", rhost);
547: prnames(to);
548: }
549: if (nflag)
550: return;
551:
552: if ((fd = open(file, 0)) < 0) {
553: error("%s: %s\n", file, strerror(errno));
554: return;
555: }
556: if (fstat(fd, &stb) < 0) {
557: error("%s: %s\n", file, strerror(errno));
558: (void) close(fd);
559: return;
560: }
561: if (stb.st_size == 0) {
562: (void) close(fd);
563: return;
564: }
565: /*
566: * Create a pipe to mailling program.
567: */
1.5 ! millert 568: (void) snprintf(buf, BUFSIZ, "%s -oi -t", _PATH_SENDMAIL);
1.1 dm 569: pf = popen(buf, "w");
570: if (pf == NULL) {
571: error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
572: (void) close(fd);
573: return;
574: }
575: /*
576: * Output the proper header information.
577: */
578: fprintf(pf, "From: rdist (Remote distribution program)\n");
579: fprintf(pf, "To:");
580: if (!any('@', to->n_name) && rhost != NULL)
581: fprintf(pf, " %s@%s", to->n_name, rhost);
582: else
583: fprintf(pf, " %s", to->n_name);
584: to = to->n_next;
585: while (to != NULL) {
586: if (!any('@', to->n_name) && rhost != NULL)
587: fprintf(pf, ", %s@%s", to->n_name, rhost);
588: else
589: fprintf(pf, ", %s", to->n_name);
590: to = to->n_next;
591: }
592: putc('\n', pf);
593: if (rhost != NULL)
594: fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
595: host, rhost);
596: else
597: fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
598: putc('\n', pf);
599:
600: while ((len = read(fd, buf, BUFSIZ)) > 0)
601: (void) fwrite(buf, 1, len, pf);
602: (void) close(fd);
603: (void) pclose(pf);
604: }
605:
606: /*
607: * Return true if name is in the list.
608: */
609: int
610: inlist(list, file)
611: struct namelist *list;
612: char *file;
613: {
614: register struct namelist *nl;
615:
616: for (nl = list; nl != NULL; nl = nl->n_next)
617: if (!strcmp(file, nl->n_name))
618: return(1);
619: return(0);
620: }
621:
622: /*
623: * Return TRUE if file is in the exception list.
624: */
625: int
626: except(file)
627: char *file;
628: {
629: register struct subcmd *sc;
630: register struct namelist *nl;
631:
632: if (debug)
633: printf("except(%s)\n", file);
634:
635: for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
636: if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
637: continue;
638: for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
639: if (sc->sc_type == EXCEPT) {
640: if (!strcmp(file, nl->n_name))
641: return(1);
642: continue;
643: }
644: re_comp(nl->n_name);
645: if (re_exec(file) > 0)
646: return(1);
647: }
648: }
649: return(0);
650: }
651:
652: char *
653: colon(cp)
654: register char *cp;
655: {
656:
657: while (*cp) {
658: if (*cp == ':')
659: return(cp);
660: if (*cp == '/')
661: return(0);
662: cp++;
663: }
664: return(0);
665: }