diff options
Diffstat (limited to 'usr.bin/rdist/docmd.c')
-rw-r--r-- | usr.bin/rdist/docmd.c | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/usr.bin/rdist/docmd.c b/usr.bin/rdist/docmd.c new file mode 100644 index 0000000..0422a37 --- /dev/null +++ b/usr.bin/rdist/docmd.c @@ -0,0 +1,629 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)docmd.c 8.1 (Berkeley) 6/9/93"; +#endif /* not lint */ + +#include "defs.h" +#include <setjmp.h> +#include <netdb.h> + +FILE *lfp; /* log file for recording files updated */ +struct subcmd *subcmds; /* list of sub-commands for current cmd */ +jmp_buf env; + +static int makeconn __P((char *)); +static int okname __P((char *)); +static void closeconn __P((void)); +static void cmptime __P((char *)); +static void doarrow __P((char **, + struct namelist *, char *, struct subcmd *)); +static void dodcolon __P((char **, + struct namelist *, char *, struct subcmd *)); +static void notify __P((char *, char *, struct namelist *, time_t)); +static void rcmptime __P((struct stat *)); + +/* + * Do the commands in cmds (initialized by yyparse). + */ +void +docmds(dhosts, argc, argv) + char **dhosts; + int argc; + char **argv; +{ + register struct cmd *c; + register struct namelist *f; + register char **cpp; + extern struct cmd *cmds; + + signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + + for (c = cmds; c != NULL; c = c->c_next) { + if (dhosts != NULL && *dhosts != NULL) { + for (cpp = dhosts; *cpp; cpp++) + if (strcmp(c->c_name, *cpp) == 0) + goto fndhost; + continue; + } + fndhost: + if (argc) { + for (cpp = argv; *cpp; cpp++) { + if (c->c_label != NULL && + strcmp(c->c_label, *cpp) == 0) { + cpp = NULL; + goto found; + } + for (f = c->c_files; f != NULL; f = f->n_next) + if (strcmp(f->n_name, *cpp) == 0) + goto found; + } + continue; + } else + cpp = NULL; + found: + switch (c->c_type) { + case ARROW: + doarrow(cpp, c->c_files, c->c_name, c->c_cmds); + break; + case DCOLON: + dodcolon(cpp, c->c_files, c->c_name, c->c_cmds); + break; + default: + fatal("illegal command type %d\n", c->c_type); + } + } + closeconn(); +} + +/* + * Process commands for sending files to other machines. + */ +static void +doarrow(filev, files, rhost, cmds) + char **filev; + struct namelist *files; + char *rhost; + struct subcmd *cmds; +{ + register struct namelist *f; + register struct subcmd *sc; + register char **cpp; + int n, ddir, opts = options; + + if (debug) + printf("doarrow(%x, %s, %x)\n", files, rhost, cmds); + + if (files == NULL) { + error("no files to be updated\n"); + return; + } + + subcmds = cmds; + ddir = files->n_next != NULL; /* destination is a directory */ + if (nflag) + printf("updating host %s\n", rhost); + else { + if (setjmp(env)) + goto done; + signal(SIGPIPE, lostconn); + if (!makeconn(rhost)) + return; + if ((lfp = fopen(tempfile, "w")) == NULL) { + fatal("cannot open %s\n", tempfile); + exit(1); + } + } + for (f = files; f != NULL; f = f->n_next) { + if (filev) { + for (cpp = filev; *cpp; cpp++) + if (strcmp(f->n_name, *cpp) == 0) + goto found; + if (!nflag) + (void) fclose(lfp); + continue; + } + found: + n = 0; + for (sc = cmds; sc != NULL; sc = sc->sc_next) { + if (sc->sc_type != INSTALL) + continue; + n++; + install(f->n_name, sc->sc_name, + sc->sc_name == NULL ? 0 : ddir, sc->sc_options); + opts = sc->sc_options; + } + if (n == 0) + install(f->n_name, NULL, 0, options); + } +done: + if (!nflag) { + (void) signal(SIGPIPE, cleanup); + (void) fclose(lfp); + lfp = NULL; + } + for (sc = cmds; sc != NULL; sc = sc->sc_next) + if (sc->sc_type == NOTIFY) + notify(tempfile, rhost, sc->sc_args, 0); + if (!nflag) { + (void) unlink(tempfile); + for (; ihead != NULL; ihead = ihead->nextp) { + free(ihead); + if ((opts & IGNLNKS) || ihead->count == 0) + continue; + log(lfp, "%s: Warning: missing links\n", + ihead->pathname); + } + } +} + +/* + * Create a connection to the rdist server on the machine rhost. + */ +static int +makeconn(rhost) + char *rhost; +{ + register char *ruser, *cp; + static char *cur_host = NULL; + static int port = -1; + char tuser[20]; + int n; + extern char user[]; + extern int userid; + + if (debug) + printf("makeconn(%s)\n", rhost); + + if (cur_host != NULL && rem >= 0) { + if (strcmp(cur_host, rhost) == 0) + return(1); + closeconn(); + } + cur_host = rhost; + cp = index(rhost, '@'); + if (cp != NULL) { + char c = *cp; + + *cp = '\0'; + strncpy(tuser, rhost, sizeof(tuser)-1); + *cp = c; + rhost = cp + 1; + ruser = tuser; + if (*ruser == '\0') + ruser = user; + else if (!okname(ruser)) + return(0); + } else + ruser = user; + if (!qflag) + printf("updating host %s\n", rhost); + (void) sprintf(buf, "%s -Server%s", _PATH_RDIST, qflag ? " -q" : ""); + if (port < 0) { + struct servent *sp; + + if ((sp = getservbyname("shell", "tcp")) == NULL) + fatal("shell/tcp: unknown service"); + port = sp->s_port; + } + + if (debug) { + printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser); + printf("buf = %s\n", buf); + } + + fflush(stdout); + seteuid(0); + rem = rcmd(&rhost, port, user, ruser, buf, 0); + seteuid(userid); + if (rem < 0) + return(0); + cp = buf; + if (read(rem, cp, 1) != 1) + lostconn(0); + if (*cp == 'V') { + do { + if (read(rem, cp, 1) != 1) + lostconn(0); + } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); + *--cp = '\0'; + cp = buf; + n = 0; + while (*cp >= '0' && *cp <= '9') + n = (n * 10) + (*cp++ - '0'); + if (*cp == '\0' && n == VERSION) + return(1); + error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n); + } else { + error("connection failed: version numbers don't match\n"); + error("got unexpected input:"); + do { + error("%c", *cp); + } while (*cp != '\n' && read(rem, cp, 1) == 1); + } + closeconn(); + return(0); +} + +/* + * Signal end of previous connection. + */ +static void +closeconn() +{ + if (debug) + printf("closeconn()\n"); + + if (rem >= 0) { + (void) write(rem, "\2\n", 2); + (void) close(rem); + rem = -1; + } +} + +void +lostconn(signo) + int signo; +{ + if (iamremote) + cleanup(0); + log(lfp, "rdist: lost connection\n"); + longjmp(env, 1); +} + +static int +okname(name) + register char *name; +{ + register char *cp = name; + register int c; + + do { + c = *cp; + if (c & 0200) + goto bad; + if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') + goto bad; + cp++; + } while (*cp); + return(1); +bad: + error("invalid user name %s\n", name); + return(0); +} + +time_t lastmod; +FILE *tfp; +extern char target[], *tp; + +/* + * Process commands for comparing files to time stamp files. + */ +static void +dodcolon(filev, files, stamp, cmds) + char **filev; + struct namelist *files; + char *stamp; + struct subcmd *cmds; +{ + register struct subcmd *sc; + register struct namelist *f; + register char **cpp; + struct timeval tv[2]; + struct timezone tz; + struct stat stb; + + if (debug) + printf("dodcolon()\n"); + + if (files == NULL) { + error("no files to be updated\n"); + return; + } + if (stat(stamp, &stb) < 0) { + error("%s: %s\n", stamp, strerror(errno)); + return; + } + if (debug) + printf("%s: %ld\n", stamp, stb.st_mtime); + + subcmds = cmds; + lastmod = stb.st_mtime; + if (nflag || (options & VERIFY)) + tfp = NULL; + else { + if ((tfp = fopen(tempfile, "w")) == NULL) { + error("%s: %s\n", stamp, strerror(errno)); + return; + } + (void) gettimeofday(&tv[0], &tz); + tv[1] = tv[0]; + (void) utimes(stamp, tv); + } + + for (f = files; f != NULL; f = f->n_next) { + if (filev) { + for (cpp = filev; *cpp; cpp++) + if (strcmp(f->n_name, *cpp) == 0) + goto found; + continue; + } + found: + tp = NULL; + cmptime(f->n_name); + } + + if (tfp != NULL) + (void) fclose(tfp); + for (sc = cmds; sc != NULL; sc = sc->sc_next) + if (sc->sc_type == NOTIFY) + notify(tempfile, NULL, sc->sc_args, lastmod); + if (!nflag && !(options & VERIFY)) + (void) unlink(tempfile); +} + +/* + * Compare the mtime of file to the list of time stamps. + */ +static void +cmptime(name) + char *name; +{ + struct stat stb; + + if (debug) + printf("cmptime(%s)\n", name); + + if (except(name)) + return; + + if (nflag) { + printf("comparing dates: %s\n", name); + return; + } + + /* + * first time cmptime() is called? + */ + if (tp == NULL) { + if (exptilde(target, name) == NULL) + return; + tp = name = target; + while (*tp) + tp++; + } + if (access(name, 4) < 0 || stat(name, &stb) < 0) { + error("%s: %s\n", name, strerror(errno)); + return; + } + + switch (stb.st_mode & S_IFMT) { + case S_IFREG: + break; + + case S_IFDIR: + rcmptime(&stb); + return; + + default: + error("%s: not a plain file\n", name); + return; + } + + if (stb.st_mtime > lastmod) + log(tfp, "new: %s\n", name); +} + +static void +rcmptime(st) + struct stat *st; +{ + register DIR *d; + register struct direct *dp; + register char *cp; + char *otp; + int len; + + if (debug) + printf("rcmptime(%x)\n", st); + + if ((d = opendir(target)) == NULL) { + error("%s: %s\n", target, strerror(errno)); + return; + } + otp = tp; + len = tp - target; + while (dp = readdir(d)) { + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { + error("%s/%s: Name too long\n", target, dp->d_name); + continue; + } + tp = otp; + *tp++ = '/'; + cp = dp->d_name; + while (*tp++ = *cp++) + ; + tp--; + cmptime(target); + } + closedir(d); + tp = otp; + *tp = '\0'; +} + +/* + * Notify the list of people the changes that were made. + * rhost == NULL if we are mailing a list of changes compared to at time + * stamp file. + */ +static void +notify(file, rhost, to, lmod) + char *file, *rhost; + register struct namelist *to; + time_t lmod; +{ + register int fd, len; + struct stat stb; + FILE *pf; + + if ((options & VERIFY) || to == NULL) + return; + if (!qflag) { + printf("notify "); + if (rhost) + printf("@%s ", rhost); + prnames(to); + } + if (nflag) + return; + + if ((fd = open(file, 0)) < 0) { + error("%s: %s\n", file, strerror(errno)); + return; + } + if (fstat(fd, &stb) < 0) { + error("%s: %s\n", file, strerror(errno)); + (void) close(fd); + return; + } + if (stb.st_size == 0) { + (void) close(fd); + return; + } + /* + * Create a pipe to mailling program. + */ + (void)sprintf(buf, "%s -oi -t", _PATH_SENDMAIL); + pf = popen(buf, "w"); + if (pf == NULL) { + error("notify: \"%s\" failed\n", _PATH_SENDMAIL); + (void) close(fd); + return; + } + /* + * Output the proper header information. + */ + fprintf(pf, "From: rdist (Remote distribution program)\n"); + fprintf(pf, "To:"); + if (!any('@', to->n_name) && rhost != NULL) + fprintf(pf, " %s@%s", to->n_name, rhost); + else + fprintf(pf, " %s", to->n_name); + to = to->n_next; + while (to != NULL) { + if (!any('@', to->n_name) && rhost != NULL) + fprintf(pf, ", %s@%s", to->n_name, rhost); + else + fprintf(pf, ", %s", to->n_name); + to = to->n_next; + } + putc('\n', pf); + if (rhost != NULL) + fprintf(pf, "Subject: files updated by rdist from %s to %s\n", + host, rhost); + else + fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod)); + putc('\n', pf); + + while ((len = read(fd, buf, BUFSIZ)) > 0) + (void) fwrite(buf, 1, len, pf); + (void) close(fd); + (void) pclose(pf); +} + +/* + * Return true if name is in the list. + */ +int +inlist(list, file) + struct namelist *list; + char *file; +{ + register struct namelist *nl; + + for (nl = list; nl != NULL; nl = nl->n_next) + if (!strcmp(file, nl->n_name)) + return(1); + return(0); +} + +/* + * Return TRUE if file is in the exception list. + */ +int +except(file) + char *file; +{ + register struct subcmd *sc; + register struct namelist *nl; + + if (debug) + printf("except(%s)\n", file); + + for (sc = subcmds; sc != NULL; sc = sc->sc_next) { + if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN) + continue; + for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) { + if (sc->sc_type == EXCEPT) { + if (!strcmp(file, nl->n_name)) + return(1); + continue; + } + re_comp(nl->n_name); + if (re_exec(file) > 0) + return(1); + } + } + return(0); +} + +char * +colon(cp) + register char *cp; +{ + + while (*cp) { + if (*cp == ':') + return(cp); + if (*cp == '/') + return(0); + cp++; + } + return(0); +} |