+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
#ifndef lint
-static char *sccsid = "@(#)docmd.c 4.1 (Berkeley) 83/09/07";
-#endif
+static char sccsid[] = "@(#)docmd.c 5.4 (Berkeley) %G%";
+#endif /* not lint */
#include "defs.h"
+#include <setjmp.h>
+#include <netdb.h>
-FILE *lfp; /* log file for recording files updated */
+FILE *lfp; /* log file for recording files updated */
+struct subcmd *subcmds; /* list of sub-commands for current cmd */
+jmp_buf env;
+
+int cleanup();
+int lostconn();
/*
- * Routines to process commands.
+ * Do the commands in cmds (initialized by yyparse).
*/
-docmd(files, hosts, cmds)
- struct block *files, *hosts, *cmds;
+docmds(dhosts, argc, argv)
+ char **dhosts;
+ int argc;
+ char **argv;
{
- register struct block *h, *f, *c;
- register char *cp, **cpp;
- int n;
+ register struct cmd *c;
+ register struct namelist *f;
+ register char **cpp;
+ extern struct cmd *cmds;
- if (debug)
- printf("docmd()\n");
-
- files = expand(files);
- hosts = expand(hosts);
- if (files == NULL)
- fatal("no files to be updated\n");
- if (hosts == NULL)
- fatal("empty list of hosts to be updated\n");
- except = cmds;
-
- for (h = hosts; h != NULL; h = h->b_next) {
- if (!nflag) {
- if ((lfp = fopen(tmpfile, "w")) == NULL) {
- fatal("cannot open %s\n", tmpfile);
- exit(1);
- }
- if (!makeconn(h->b_name))
- continue;
+ 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;
}
- for (f = files; f != NULL; f = f->b_next) {
- if (filec) {
- for (cpp = filev; *cpp; cpp++)
- if (!strcmp(f->b_name, *cpp))
+ 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;
}
- found:
- n = 0;
- for (c = cmds; c != NULL; c = c->b_next)
- if (c->b_type == INSTALL) {
- install(f->b_name, c->b_name, 0);
- n++;
- } else if (c->b_type == VERIFY) {
- install(f->b_name, c->b_name, 1);
- n++;
- }
- if (n == 0)
- install(f->b_name, f->b_name, 0);
+ 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);
}
- if (!nflag) {
- (void) fclose(lfp);
- (void) close(rem);
+ }
+ closeconn();
+}
+
+/*
+ * Process commands for sending files to other machines.
+ */
+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(tmpfile, "w")) == NULL) {
+ fatal("cannot open %s\n", tmpfile);
+ 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;
}
- for (c = cmds; c != NULL; c = c->b_next)
- if (c->b_type == NOTIFY)
- notify(h->b_name, c->b_args);
+ if (n == 0)
+ install(f->n_name, NULL, 0, options);
}
- if (!nflag)
+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(tmpfile, rhost, sc->sc_args, 0);
+ if (!nflag) {
(void) unlink(tmpfile);
+ for (; ihead != NULL; ihead = ihead->nextp) {
+ free(ihead);
+ if ((opts & IGNLNKS) || ihead->count == 0)
+ continue;
+ log(lfp, "%s: Warning: missing links\n",
+ ihead->pathname);
+ }
+ }
}
/*
makeconn(rhost)
char *rhost;
{
- register char *ruser;
+ register char *ruser, *cp;
+ static char *cur_host = NULL;
+ static int port = -1;
+ char tuser[20];
+ int n;
extern char user[];
+ extern int userid;
- (void) sprintf(buf, "/usr/local/rdist -Server%s%s%s%s%s",
- vflag ? " -v" : "", qflag ? " -q" : "", nflag ? " -n" : "",
- yflag ? " -y" : "", debug ? " -d" : "");
+ if (debug)
+ printf("makeconn(%s)\n", rhost);
- ruser = rindex(rhost, '.');
- if (ruser != NULL) {
- *ruser++ = '\0';
- if (!okname(ruser))
+ 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("makeconn(%s)\n", rhost);
- printf("luser = %s, ruser = %s\n", user, ruser);
+ printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
printf("buf = %s\n", buf);
}
- rem = rcmd(&rhost, IPPORT_CMDSERVER, user, ruser, buf, 0);
+ fflush(stdout);
+ setreuid(userid, 0);
+ rem = rcmd(&rhost, port, user, ruser, buf, 0);
+ setreuid(0, userid);
if (rem < 0)
return(0);
- if (response() < 0)
- return(0);
+ cp = buf;
+ if (read(rem, cp, 1) != 1)
+ lostconn();
+ if (*cp == 'V') {
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn();
+ } 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");
+ closeconn();
+ return(0);
+}
+
+/*
+ * Signal end of previous connection.
+ */
+closeconn()
+{
+ if (debug)
+ printf("closeconn()\n");
+
+ if (rem >= 0) {
+ (void) write(rem, "\2\n", 2);
+ (void) close(rem);
+ rem = -1;
+ }
+}
+
+lostconn()
+{
+ if (iamremote)
+ cleanup();
+ log(lfp, "rdist: lost connection\n");
+ longjmp(env, 1);
+}
+
+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;
+
/*
- * Update the file(s) if they are different.
+ * Process commands for comparing files to time stamp files.
*/
-install(src, dest, verify)
- char *src, *dest;
- int verify;
+dodcolon(filev, files, stamp, cmds)
+ char **filev;
+ struct namelist *files;
+ char *stamp;
+ struct subcmd *cmds;
{
- register char *cp;
- extern char *tp;
- char lbuf[BUFSIZ];
+ register struct subcmd *sc;
+ register struct namelist *f;
+ register char **cpp;
+ struct timeval tv[2];
+ struct timezone tz;
+ struct stat stb;
- if (!qflag)
- printf("%s %s %s\n", verify ? "verify" : "install", src, dest);
- if (nflag)
+ 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, sys_errlist[errno]);
+ return;
+ }
+ if (debug)
+ printf("%s: %d\n", stamp, stb.st_mtime);
+
+ subcmds = cmds;
+ lastmod = stb.st_mtime;
+ if (nflag || (options & VERIFY))
+ tfp = NULL;
+ else {
+ if ((tfp = fopen(tmpfile, "w")) == NULL) {
+ error("%s: %s\n", stamp, sys_errlist[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(tmpfile, NULL, sc->sc_args, lastmod);
+ if (!nflag && !(options & VERIFY))
+ (void) unlink(tmpfile);
+}
+
+/*
+ * Compare the mtime of file to the list of time stamps.
+ */
+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;
+ }
+
/*
- * Pass the destination file/directory name to remote.
+ * first time cmptime() is called?
*/
- (void) sprintf(buf, "T%s\n", dest);
+ 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, sys_errlist[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);
+}
+
+rcmptime(st)
+ struct stat *st;
+{
+ register DIR *d;
+ register struct direct *dp;
+ register char *cp;
+ char *otp;
+ int len;
+
if (debug)
- printf("buf = %s", buf);
- (void) write(rem, buf, strlen(buf));
- tp = NULL;
- shexpand(lbuf, src);
- sendf(lbuf, verify);
+ printf("rcmptime(%x)\n", st);
+
+ if ((d = opendir(target)) == NULL) {
+ error("%s: %s\n", target, sys_errlist[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.
*/
-notify(host, to)
- char *host;
- register struct block *to;
+notify(file, rhost, to, lmod)
+ char *file, *rhost;
+ register struct namelist *to;
+ time_t lmod;
{
register int fd, len;
FILE *pf, *popen();
struct stat stb;
- if (vflag)
+ if ((options & VERIFY) || to == NULL)
return;
if (!qflag) {
- printf("notify @%s ", host);
+ printf("notify ");
+ if (rhost)
+ printf("@%s ", rhost);
prnames(to);
}
if (nflag)
return;
- if ((fd = open(tmpfile, 0)) < 0) {
- error("%s: %s\n", tmpfile, sys_errlist[errno]);
+ if ((fd = open(file, 0)) < 0) {
+ error("%s: %s\n", file, sys_errlist[errno]);
+ return;
+ }
+ if (fstat(fd, &stb) < 0) {
+ error("%s: %s\n", file, sys_errlist[errno]);
+ (void) close(fd);
+ return;
+ }
+ if (stb.st_size == 0) {
+ (void) close(fd);
return;
}
/*
* Create a pipe to mailling program.
*/
- pf = popen(MAILCMD, "w");
- if (pf == NULL)
- fatal("notify: \"%s\" failed\n", MAILCMD);
+ (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) {
- fprintf(pf, " %s@%s", to->b_name, host);
- to = to->b_next;
+ 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);
- fprintf(pf, "Subject: files updated by rdist\n");
+ 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) pclose(pf);
}
-struct block *except; /* list of files to exclude */
-
/*
- * Return true if name is in list.
+ * Return true if name is in the list.
*/
-exclude(file)
+inlist(list, file)
+ struct namelist *list;
char *file;
{
- register struct block *b, *c;
+ register struct namelist *nl;
- for (c = except; c != NULL; c = c->b_next) {
- if (c->b_type != EXCEPT)
- continue;
- for (b = c->b_args; b != NULL; b = b->b_next)
- if (!strcmp(file, b->b_name))
- return(1);
- }
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ if (!strcmp(file, nl->n_name))
+ return(1);
return(0);
}
-okname(name)
- register char *name;
+/*
+ * Return TRUE if file is in the exception list.
+ */
+except(file)
+ char *file;
{
- register char *cp = name;
- register int c;
+ register struct subcmd *sc;
+ register struct namelist *nl;
- 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);
+ 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);
}