summaryrefslogtreecommitdiff
path: root/usr.bin/rdist
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/rdist')
-rw-r--r--usr.bin/rdist/Makefile15
-rw-r--r--usr.bin/rdist/cron.entry1
-rw-r--r--usr.bin/rdist/defs.h181
-rw-r--r--usr.bin/rdist/docmd.c630
-rw-r--r--usr.bin/rdist/expand.c667
-rw-r--r--usr.bin/rdist/gram.y509
-rw-r--r--usr.bin/rdist/lookup.c167
-rw-r--r--usr.bin/rdist/main.c328
-rw-r--r--usr.bin/rdist/pathnames.h39
-rw-r--r--usr.bin/rdist/rdist.1413
-rw-r--r--usr.bin/rdist/server.c1586
11 files changed, 4536 insertions, 0 deletions
diff --git a/usr.bin/rdist/Makefile b/usr.bin/rdist/Makefile
new file mode 100644
index 00000000000..f3f5de8532f
--- /dev/null
+++ b/usr.bin/rdist/Makefile
@@ -0,0 +1,15 @@
+# from: @(#)Makefile 5.11 (Berkeley) 3/12/91
+# $Id: Makefile,v 1.1 1995/10/18 08:45:58 deraadt Exp $
+
+PROG= rdist
+CFLAGS+=-I${.CURDIR}
+SRCS= docmd.c expand.c lookup.c main.c server.c
+OBJS+= gram.o
+BINOWN= root
+BINMODE=4555
+CLEANFILES=y.tab.h
+
+LDADD= -lcompat
+DPADD= ${LIBCOMPAT}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rdist/cron.entry b/usr.bin/rdist/cron.entry
new file mode 100644
index 00000000000..90968cf3faa
--- /dev/null
+++ b/usr.bin/rdist/cron.entry
@@ -0,0 +1 @@
+30 3 * * * /usr/bin/rdist -f /etc/Distfile >& /var/log/rdist/rdist.err 2>&1
diff --git a/usr.bin/rdist/defs.h b/usr.bin/rdist/defs.h
new file mode 100644
index 00000000000..731f43427b4
--- /dev/null
+++ b/usr.bin/rdist/defs.h
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ *
+ * from: @(#)defs.h 8.1 (Berkeley) 6/9/93
+ * $Id: defs.h,v 1.1 1995/10/18 08:45:58 deraadt Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "pathnames.h"
+
+/*
+ * The version number should be changed whenever the protocol changes.
+ */
+#define VERSION 3
+
+ /* defines for yacc */
+#define EQUAL 1
+#define LP 2
+#define RP 3
+#define SM 4
+#define ARROW 5
+#define COLON 6
+#define DCOLON 7
+#define NAME 8
+#define STRING 9
+#define INSTALL 10
+#define NOTIFY 11
+#define EXCEPT 12
+#define PATTERN 13
+#define SPECIAL 14
+#define OPTION 15
+
+ /* lexical definitions */
+#define QUOTE 0200 /* used internally for quoted characters */
+#define TRIM 0177 /* Mask to strip quote bit */
+
+ /* table sizes */
+#define HASHSIZE 1021
+#define INMAX 3500
+
+ /* option flags */
+#define VERIFY 0x1
+#define WHOLE 0x2
+#define YOUNGER 0x4
+#define COMPARE 0x8
+#define REMOVE 0x10
+#define FOLLOW 0x20
+#define IGNLNKS 0x40
+
+ /* expand type definitions */
+#define E_VARS 0x1
+#define E_SHELL 0x2
+#define E_TILDE 0x4
+#define E_ALL 0x7
+
+ /* actions for lookup() */
+#define LOOKUP 0
+#define INSERT 1
+#define REPLACE 2
+
+#define ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+
+#define ALLOC(x) (struct x *) malloc(sizeof(struct x))
+
+struct namelist { /* for making lists of strings */
+ char *n_name;
+ struct namelist *n_next;
+};
+
+struct subcmd {
+ short sc_type; /* type - INSTALL,NOTIFY,EXCEPT,SPECIAL */
+ short sc_options;
+ char *sc_name;
+ struct namelist *sc_args;
+ struct subcmd *sc_next;
+};
+
+struct cmd {
+ int c_type; /* type - ARROW,DCOLON */
+ char *c_name; /* hostname or time stamp file name */
+ char *c_label; /* label for partial update */
+ struct namelist *c_files;
+ struct subcmd *c_cmds;
+ struct cmd *c_next;
+};
+
+struct linkbuf {
+ ino_t inum;
+ dev_t devnum;
+ int count;
+ char pathname[BUFSIZ];
+ char target[BUFSIZ];
+ struct linkbuf *nextp;
+};
+
+extern int debug; /* debugging flag */
+extern int nflag; /* NOP flag, don't execute commands */
+extern int qflag; /* Quiet. don't print messages */
+extern int options; /* global options */
+
+extern int nerrs; /* number of errors seen */
+extern int rem; /* remote file descriptor */
+extern int iamremote; /* acting as remote server */
+extern char tempfile[]; /* file name for logging changes */
+extern struct linkbuf *ihead; /* list of files with more than one link */
+extern struct passwd *pw; /* pointer to static area used by getpwent */
+extern struct group *gr; /* pointer to static area used by getgrent */
+extern char host[]; /* host name of master copy */
+extern char buf[]; /* general purpose buffer */
+
+int any __P((int, char *));
+char *colon __P((char *));
+void cleanup __P((int));
+void define __P((char *));
+void docmds __P((char **, int, char **));
+void error __P((const char *, ...));
+int except __P((char *));
+struct namelist *
+ expand __P((struct namelist *, int));
+char *exptilde __P((char [], char *));
+void fatal __P((const char *, ...));
+int inlist __P((struct namelist *, char *));
+void insert __P((char *,
+ struct namelist *, struct namelist *, struct subcmd *));
+void install __P((char *, char *, int, int));
+void log __P((FILE *, const char *, ...));
+struct namelist *
+ lookup __P((char *, int, struct namelist *));
+void lostconn __P((int));
+struct namelist *
+ makenl __P((char *));
+struct subcmd *
+ makesubcmd __P((int));
+void prnames __P((struct namelist *));
+void server __P((void));
+void yyerror __P((char *));
+int yyparse __P((void));
diff --git a/usr.bin/rdist/docmd.c b/usr.bin/rdist/docmd.c
new file mode 100644
index 00000000000..4f71389e5ad
--- /dev/null
+++ b/usr.bin/rdist/docmd.c
@@ -0,0 +1,630 @@
+/*
+ * 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
+/* from: static char sccsid[] = "@(#)docmd.c 8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: docmd.c,v 1.1 1995/10/18 08:45:58 deraadt Exp $";
+#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);
+}
diff --git a/usr.bin/rdist/expand.c b/usr.bin/rdist/expand.c
new file mode 100644
index 00000000000..6b917331e3c
--- /dev/null
+++ b/usr.bin/rdist/expand.c
@@ -0,0 +1,667 @@
+/*
+ * 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
+/* from: static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: expand.c,v 1.1 1995/10/18 08:45:59 deraadt Exp $";
+#endif /* not lint */
+
+#include "defs.h"
+
+#define GAVSIZ NCARGS / 6
+#define LC '{'
+#define RC '}'
+
+static char shchars[] = "${[*?";
+
+int which; /* bit mask of types to expand */
+int eargc; /* expanded arg count */
+char **eargv; /* expanded arg vectors */
+char *path;
+char *pathp;
+char *lastpathp;
+char *tilde; /* "~user" if not expanding tilde, else "" */
+char *tpathp;
+int nleft;
+
+int expany; /* any expansions done? */
+char *entp;
+char **sortbase;
+
+#define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
+ sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
+
+static void Cat __P((char *, char *));
+static void addpath __P((int));
+static int amatch __P((char *, char *));
+static int argcmp __P((const void *, const void *));
+static int execbrc __P((char *, char *));
+static void expsh __P((char *));
+static void expstr __P((char *));
+static int match __P((char *, char *));
+static void matchdir __P((char *));
+static int smatch __P((char *, char *));
+
+/*
+ * Take a list of names and expand any macros, etc.
+ * wh = E_VARS if expanding variables.
+ * wh = E_SHELL if expanding shell characters.
+ * wh = E_TILDE if expanding `~'.
+ * or any of these or'ed together.
+ *
+ * Major portions of this were snarfed from csh/sh.glob.c.
+ */
+struct namelist *
+expand(list, wh)
+ struct namelist *list;
+ int wh;
+{
+ register struct namelist *nl, *prev;
+ register int n;
+ char pathbuf[BUFSIZ];
+ char *argvbuf[GAVSIZ];
+
+ if (debug) {
+ printf("expand(%x, %d)\nlist = ", list, wh);
+ prnames(list);
+ }
+
+ if (wh == 0) {
+ register char *cp;
+
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ for (cp = nl->n_name; *cp; cp++)
+ *cp = *cp & TRIM;
+ return(list);
+ }
+
+ which = wh;
+ path = tpathp = pathp = pathbuf;
+ *pathp = '\0';
+ lastpathp = &path[sizeof pathbuf - 2];
+ tilde = "";
+ eargc = 0;
+ eargv = sortbase = argvbuf;
+ *eargv = 0;
+ nleft = NCARGS - 4;
+ /*
+ * Walk the name list and expand names into eargv[];
+ */
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ expstr(nl->n_name);
+ /*
+ * Take expanded list of names from eargv[] and build a new list.
+ */
+ list = prev = NULL;
+ for (n = 0; n < eargc; n++) {
+ nl = makenl(NULL);
+ nl->n_name = eargv[n];
+ if (prev == NULL)
+ list = prev = nl;
+ else {
+ prev->n_next = nl;
+ prev = nl;
+ }
+ }
+ if (debug) {
+ printf("expanded list = ");
+ prnames(list);
+ }
+ return(list);
+}
+
+static void
+expstr(s)
+ char *s;
+{
+ register char *cp, *cp1;
+ register struct namelist *tp;
+ char *tail;
+ char buf[BUFSIZ];
+ int savec, oeargc;
+ extern char homedir[];
+
+ if (s == NULL || *s == '\0')
+ return;
+
+ if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
+ *cp++ = '\0';
+ if (*cp == '\0') {
+ yyerror("no variable name after '$'");
+ return;
+ }
+ if (*cp == LC) {
+ cp++;
+ if ((tail = index(cp, RC)) == NULL) {
+ yyerror("unmatched '{'");
+ return;
+ }
+ *tail++ = savec = '\0';
+ if (*cp == '\0') {
+ yyerror("no variable name after '$'");
+ return;
+ }
+ } else {
+ tail = cp + 1;
+ savec = *tail;
+ *tail = '\0';
+ }
+ tp = lookup(cp, NULL, 0);
+ if (savec != '\0')
+ *tail = savec;
+ if (tp != NULL) {
+ for (; tp != NULL; tp = tp->n_next) {
+ sprintf(buf, "%s%s%s", s, tp->n_name, tail);
+ expstr(buf);
+ }
+ return;
+ }
+ sprintf(buf, "%s%s", s, tail);
+ expstr(buf);
+ return;
+ }
+ if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
+ Cat(s, "");
+ sort();
+ return;
+ }
+ if (*s == '~') {
+ cp = ++s;
+ if (*cp == '\0' || *cp == '/') {
+ tilde = "~";
+ cp1 = homedir;
+ } else {
+ tilde = cp1 = buf;
+ *cp1++ = '~';
+ do
+ *cp1++ = *cp++;
+ while (*cp && *cp != '/');
+ *cp1 = '\0';
+ if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
+ if ((pw = getpwnam(buf+1)) == NULL) {
+ strcat(buf, ": unknown user name");
+ yyerror(buf+1);
+ return;
+ }
+ }
+ cp1 = pw->pw_dir;
+ s = cp;
+ }
+ for (cp = path; *cp++ = *cp1++; )
+ ;
+ tpathp = pathp = cp - 1;
+ } else {
+ tpathp = pathp = path;
+ tilde = "";
+ }
+ *pathp = '\0';
+ if (!(which & E_SHELL)) {
+ if (which & E_TILDE)
+ Cat(path, s);
+ else
+ Cat(tilde, s);
+ sort();
+ return;
+ }
+ oeargc = eargc;
+ expany = 0;
+ expsh(s);
+ if (eargc == oeargc)
+ Cat(s, ""); /* "nonomatch" is set */
+ sort();
+}
+
+static int
+argcmp(a1, a2)
+ const void *a1, *a2;
+{
+
+ return (strcmp(*(char **)a1, *(char **)a2));
+}
+
+/*
+ * If there are any Shell meta characters in the name,
+ * expand into a list, after searching directory
+ */
+static void
+expsh(s)
+ char *s;
+{
+ register char *cp;
+ register char *spathp, *oldcp;
+ struct stat stb;
+
+ spathp = pathp;
+ cp = s;
+ while (!any(*cp, shchars)) {
+ if (*cp == '\0') {
+ if (!expany || stat(path, &stb) >= 0) {
+ if (which & E_TILDE)
+ Cat(path, "");
+ else
+ Cat(tilde, tpathp);
+ }
+ goto endit;
+ }
+ addpath(*cp++);
+ }
+ oldcp = cp;
+ while (cp > s && *cp != '/')
+ cp--, pathp--;
+ if (*cp == '/')
+ cp++, pathp++;
+ *pathp = '\0';
+ if (*oldcp == '{') {
+ execbrc(cp, NULL);
+ return;
+ }
+ matchdir(cp);
+endit:
+ pathp = spathp;
+ *pathp = '\0';
+}
+
+static void
+matchdir(pattern)
+ char *pattern;
+{
+ struct stat stb;
+ register struct direct *dp;
+ DIR *dirp;
+
+ dirp = opendir(path);
+ if (dirp == NULL) {
+ if (expany)
+ return;
+ goto patherr2;
+ }
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ goto patherr1;
+ if (!ISDIR(stb.st_mode)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+ while ((dp = readdir(dirp)) != NULL)
+ if (match(dp->d_name, pattern)) {
+ if (which & E_TILDE)
+ Cat(path, dp->d_name);
+ else {
+ strcpy(pathp, dp->d_name);
+ Cat(tilde, tpathp);
+ *pathp = '\0';
+ }
+ }
+ closedir(dirp);
+ return;
+
+patherr1:
+ closedir(dirp);
+patherr2:
+ strcat(path, ": ");
+ strcat(path, strerror(errno));
+ yyerror(path);
+}
+
+static int
+execbrc(p, s)
+ char *p, *s;
+{
+ char restbuf[BUFSIZ + 2];
+ register char *pe, *pm, *pl;
+ int brclev = 0;
+ char *lm, savec, *spathp;
+
+ for (lm = restbuf; *p != '{'; *lm++ = *p++)
+ continue;
+ for (pe = ++p; *pe; pe++)
+ switch (*pe) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe++)
+ continue;
+ if (!*pe)
+ yyerror("Missing ']'");
+ continue;
+ }
+pend:
+ if (brclev || !*pe) {
+ yyerror("Missing '}'");
+ return (0);
+ }
+ for (pl = pm = p; pm <= pe; pm++)
+ switch (*pm & (QUOTE|TRIM)) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ',':
+ if (brclev)
+ continue;
+doit:
+ savec = *pm;
+ *pm = 0;
+ strcpy(lm, pl);
+ strcat(restbuf, pe + 1);
+ *pm = savec;
+ if (s == 0) {
+ spathp = pathp;
+ expsh(restbuf);
+ pathp = spathp;
+ *pathp = 0;
+ } else if (amatch(s, restbuf))
+ return (1);
+ sort();
+ pl = pm + 1;
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm++)
+ continue;
+ if (!*pm)
+ yyerror("Missing ']'");
+ continue;
+ }
+ return (0);
+}
+
+static int
+match(s, p)
+ char *s, *p;
+{
+ register int c;
+ register char *sentp;
+ char sexpany = expany;
+
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p);
+ entp = sentp;
+ expany = sexpany;
+ return (c);
+}
+
+static int
+amatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ char *spathp;
+ struct stat stb;
+ int c, cc;
+
+ expany = 1;
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '{':
+ return (execbrc(p - 1, s - 1));
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0) {
+ yyerror("Missing ']'");
+ return (0);
+ }
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ }
+ for (s--; *s; s++)
+ if (amatch(s, p))
+ return (1);
+ return (0);
+
+ case '\0':
+ return (scc == '\0');
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == '\0')
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+slash:
+ s = entp;
+ spathp = pathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
+ if (*p == '\0') {
+ if (which & E_TILDE)
+ Cat(path, "");
+ else
+ Cat(tilde, tpathp);
+ } else
+ expsh(p);
+ pathp = spathp;
+ *pathp = '\0';
+ return (0);
+ }
+ }
+}
+
+static int
+smatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ int c, cc;
+
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0) {
+ yyerror("Missing ']'");
+ return (0);
+ }
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ for (s--; *s; s++)
+ if (smatch(s, p))
+ return (1);
+ return (0);
+
+ case '\0':
+ return (scc == '\0');
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ }
+ }
+}
+
+static void
+Cat(s1, s2)
+ register char *s1, *s2;
+{
+ int len = strlen(s1) + strlen(s2) + 1;
+ register char *s;
+
+ nleft -= len;
+ if (nleft <= 0 || ++eargc >= GAVSIZ)
+ yyerror("Arguments too long");
+ eargv[eargc] = 0;
+ eargv[eargc - 1] = s = malloc(len);
+ if (s == NULL)
+ fatal("ran out of memory\n");
+ while (*s++ = *s1++ & TRIM)
+ ;
+ s--;
+ while (*s++ = *s2++ & TRIM)
+ ;
+}
+
+static void
+addpath(c)
+ int c;
+{
+
+ if (pathp >= lastpathp)
+ yyerror("Pathname too long");
+ else {
+ *pathp++ = c & TRIM;
+ *pathp = '\0';
+ }
+}
+
+/*
+ * Expand file names beginning with `~' into the
+ * user's home directory path name. Return a pointer in buf to the
+ * part corresponding to `file'.
+ */
+char *
+exptilde(buf, file)
+ char buf[];
+ register char *file;
+{
+ register char *s1, *s2, *s3;
+ extern char homedir[];
+
+ if (*file != '~') {
+ strcpy(buf, file);
+ return(buf);
+ }
+ if (*++file == '\0') {
+ s2 = homedir;
+ s3 = NULL;
+ } else if (*file == '/') {
+ s2 = homedir;
+ s3 = file;
+ } else {
+ s3 = file;
+ while (*s3 && *s3 != '/')
+ s3++;
+ if (*s3 == '/')
+ *s3 = '\0';
+ else
+ s3 = NULL;
+ if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
+ if ((pw = getpwnam(file)) == NULL) {
+ error("%s: unknown user name\n", file);
+ if (s3 != NULL)
+ *s3 = '/';
+ return(NULL);
+ }
+ }
+ if (s3 != NULL)
+ *s3 = '/';
+ s2 = pw->pw_dir;
+ }
+ for (s1 = buf; *s1++ = *s2++; )
+ ;
+ s2 = --s1;
+ if (s3 != NULL) {
+ s2++;
+ while (*s1++ = *s3++)
+ ;
+ }
+ return(s2);
+}
diff --git a/usr.bin/rdist/gram.y b/usr.bin/rdist/gram.y
new file mode 100644
index 00000000000..1eb190f6a4b
--- /dev/null
+++ b/usr.bin/rdist/gram.y
@@ -0,0 +1,509 @@
+%{
+/*
+ * 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
+/* from: static char sccsid[] = "@(#)gram.y 8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: gram.y,v 1.1 1995/10/18 08:45:59 deraadt Exp $";
+#endif /* not lint */
+
+#include "defs.h"
+
+struct cmd *cmds = NULL;
+struct cmd *last_cmd;
+struct namelist *last_n;
+struct subcmd *last_sc;
+
+static char *makestr __P((char *));
+
+%}
+
+%term EQUAL 1
+%term LP 2
+%term RP 3
+%term SM 4
+%term ARROW 5
+%term COLON 6
+%term DCOLON 7
+%term NAME 8
+%term STRING 9
+%term INSTALL 10
+%term NOTIFY 11
+%term EXCEPT 12
+%term PATTERN 13
+%term SPECIAL 14
+%term OPTION 15
+
+%union {
+ int intval;
+ char *string;
+ struct subcmd *subcmd;
+ struct namelist *namel;
+}
+
+%type <intval> OPTION, options
+%type <string> NAME, STRING
+%type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
+%type <namel> namelist, names, opt_namelist
+
+%%
+
+file: /* VOID */
+ | file command
+ ;
+
+command: NAME EQUAL namelist = {
+ (void) lookup($1, INSERT, $3);
+ }
+ | namelist ARROW namelist cmdlist = {
+ insert(NULL, $1, $3, $4);
+ }
+ | NAME COLON namelist ARROW namelist cmdlist = {
+ insert($1, $3, $5, $6);
+ }
+ | namelist DCOLON NAME cmdlist = {
+ append(NULL, $1, $3, $4);
+ }
+ | NAME COLON namelist DCOLON NAME cmdlist = {
+ append($1, $3, $5, $6);
+ }
+ | error
+ ;
+
+namelist: NAME = {
+ $$ = makenl($1);
+ }
+ | LP names RP = {
+ $$ = $2;
+ }
+ ;
+
+names: /* VOID */ {
+ $$ = last_n = NULL;
+ }
+ | names NAME = {
+ if (last_n == NULL)
+ $$ = last_n = makenl($2);
+ else {
+ last_n->n_next = makenl($2);
+ last_n = last_n->n_next;
+ $$ = $1;
+ }
+ }
+ ;
+
+cmdlist: /* VOID */ {
+ $$ = last_sc = NULL;
+ }
+ | cmdlist cmd = {
+ if (last_sc == NULL)
+ $$ = last_sc = $2;
+ else {
+ last_sc->sc_next = $2;
+ last_sc = $2;
+ $$ = $1;
+ }
+ }
+ ;
+
+cmd: INSTALL options opt_namelist SM = {
+ register struct namelist *nl;
+
+ $1->sc_options = $2 | options;
+ if ($3 != NULL) {
+ nl = expand($3, E_VARS);
+ if (nl) {
+ if (nl->n_next != NULL)
+ yyerror("only one name allowed\n");
+ $1->sc_name = nl->n_name;
+ free(nl);
+ } else
+ $1->sc_name = NULL;
+ }
+ $$ = $1;
+ }
+ | NOTIFY namelist SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_VARS);
+ $$ = $1;
+ }
+ | EXCEPT namelist SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_ALL);
+ $$ = $1;
+ }
+ | PATTERN namelist SM = {
+ struct namelist *nl;
+ char *cp, *re_comp();
+
+ for (nl = $2; nl != NULL; nl = nl->n_next)
+ if ((cp = re_comp(nl->n_name)) != NULL)
+ yyerror(cp);
+ $1->sc_args = expand($2, E_VARS);
+ $$ = $1;
+ }
+ | SPECIAL opt_namelist STRING SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_ALL);
+ $1->sc_name = $3;
+ $$ = $1;
+ }
+ ;
+
+options: /* VOID */ = {
+ $$ = 0;
+ }
+ | options OPTION = {
+ $$ |= $2;
+ }
+ ;
+
+opt_namelist: /* VOID */ = {
+ $$ = NULL;
+ }
+ | namelist = {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+int yylineno = 1;
+extern FILE *fin;
+
+int
+yylex()
+{
+ static char yytext[INMAX];
+ register int c;
+ register char *cp1, *cp2;
+ static char quotechars[] = "[]{}*?$";
+
+again:
+ switch (c = getc(fin)) {
+ case EOF: /* end of file */
+ return(0);
+
+ case '#': /* start of comment */
+ while ((c = getc(fin)) != EOF && c != '\n')
+ ;
+ if (c == EOF)
+ return(0);
+ case '\n':
+ yylineno++;
+ case ' ':
+ case '\t': /* skip blanks */
+ goto again;
+
+ case '=': /* EQUAL */
+ return(EQUAL);
+
+ case '(': /* LP */
+ return(LP);
+
+ case ')': /* RP */
+ return(RP);
+
+ case ';': /* SM */
+ return(SM);
+
+ case '-': /* -> */
+ if ((c = getc(fin)) == '>')
+ return(ARROW);
+ ungetc(c, fin);
+ c = '-';
+ break;
+
+ case '"': /* STRING */
+ cp1 = yytext;
+ cp2 = &yytext[INMAX - 1];
+ for (;;) {
+ if (cp1 >= cp2) {
+ yyerror("command string too long\n");
+ break;
+ }
+ c = getc(fin);
+ if (c == EOF || c == '"')
+ break;
+ if (c == '\\') {
+ if ((c = getc(fin)) == EOF) {
+ *cp1++ = '\\';
+ break;
+ }
+ }
+ if (c == '\n') {
+ yylineno++;
+ c = ' '; /* can't send '\n' */
+ }
+ *cp1++ = c;
+ }
+ if (c != '"')
+ yyerror("missing closing '\"'\n");
+ *cp1 = '\0';
+ yylval.string = makestr(yytext);
+ return(STRING);
+
+ case ':': /* : or :: */
+ if ((c = getc(fin)) == ':')
+ return(DCOLON);
+ ungetc(c, fin);
+ return(COLON);
+ }
+ cp1 = yytext;
+ cp2 = &yytext[INMAX - 1];
+ for (;;) {
+ if (cp1 >= cp2) {
+ yyerror("input line too long\n");
+ break;
+ }
+ if (c == '\\') {
+ if ((c = getc(fin)) != EOF) {
+ if (any(c, quotechars))
+ c |= QUOTE;
+ } else {
+ *cp1++ = '\\';
+ break;
+ }
+ }
+ *cp1++ = c;
+ c = getc(fin);
+ if (c == EOF || any(c, " \"'\t()=;:\n")) {
+ ungetc(c, fin);
+ break;
+ }
+ }
+ *cp1 = '\0';
+ if (yytext[0] == '-' && yytext[2] == '\0') {
+ switch (yytext[1]) {
+ case 'b':
+ yylval.intval = COMPARE;
+ return(OPTION);
+
+ case 'R':
+ yylval.intval = REMOVE;
+ return(OPTION);
+
+ case 'v':
+ yylval.intval = VERIFY;
+ return(OPTION);
+
+ case 'w':
+ yylval.intval = WHOLE;
+ return(OPTION);
+
+ case 'y':
+ yylval.intval = YOUNGER;
+ return(OPTION);
+
+ case 'h':
+ yylval.intval = FOLLOW;
+ return(OPTION);
+
+ case 'i':
+ yylval.intval = IGNLNKS;
+ return(OPTION);
+ }
+ }
+ if (!strcmp(yytext, "install"))
+ c = INSTALL;
+ else if (!strcmp(yytext, "notify"))
+ c = NOTIFY;
+ else if (!strcmp(yytext, "except"))
+ c = EXCEPT;
+ else if (!strcmp(yytext, "except_pat"))
+ c = PATTERN;
+ else if (!strcmp(yytext, "special"))
+ c = SPECIAL;
+ else {
+ yylval.string = makestr(yytext);
+ return(NAME);
+ }
+ yylval.subcmd = makesubcmd(c);
+ return(c);
+}
+
+int
+any(c, str)
+ register int c;
+ register char *str;
+{
+ while (*str)
+ if (c == *str++)
+ return(1);
+ return(0);
+}
+
+/*
+ * Insert or append ARROW command to list of hosts to be updated.
+ */
+void
+insert(label, files, hosts, subcmds)
+ char *label;
+ struct namelist *files, *hosts;
+ struct subcmd *subcmds;
+{
+ register struct cmd *c, *prev, *nc;
+ register struct namelist *h;
+
+ files = expand(files, E_VARS|E_SHELL);
+ hosts = expand(hosts, E_ALL);
+ for (h = hosts; h != NULL; free(h), h = h->n_next) {
+ /*
+ * Search command list for an update to the same host.
+ */
+ for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
+ if (strcmp(c->c_name, h->n_name) == 0) {
+ do {
+ prev = c;
+ c = c->c_next;
+ } while (c != NULL &&
+ strcmp(c->c_name, h->n_name) == 0);
+ break;
+ }
+ }
+ /*
+ * Insert new command to update host.
+ */
+ nc = ALLOC(cmd);
+ if (nc == NULL)
+ fatal("ran out of memory\n");
+ nc->c_type = ARROW;
+ nc->c_name = h->n_name;
+ nc->c_label = label;
+ nc->c_files = files;
+ nc->c_cmds = subcmds;
+ nc->c_next = c;
+ if (prev == NULL)
+ cmds = nc;
+ else
+ prev->c_next = nc;
+ /* update last_cmd if appending nc to cmds */
+ if (c == NULL)
+ last_cmd = nc;
+ }
+}
+
+/*
+ * Append DCOLON command to the end of the command list since these are always
+ * executed in the order they appear in the distfile.
+ */
+void
+append(label, files, stamp, subcmds)
+ char *label;
+ struct namelist *files;
+ char *stamp;
+ struct subcmd *subcmds;
+{
+ register struct cmd *c;
+
+ c = ALLOC(cmd);
+ if (c == NULL)
+ fatal("ran out of memory\n");
+ c->c_type = DCOLON;
+ c->c_name = stamp;
+ c->c_label = label;
+ c->c_files = expand(files, E_ALL);
+ c->c_cmds = subcmds;
+ c->c_next = NULL;
+ if (cmds == NULL)
+ cmds = last_cmd = c;
+ else {
+ last_cmd->c_next = c;
+ last_cmd = c;
+ }
+}
+
+/*
+ * Error printing routine in parser.
+ */
+void
+yyerror(s)
+ char *s;
+{
+ ++nerrs;
+ fflush(stdout);
+ fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
+}
+
+/*
+ * Return a copy of the string.
+ */
+static char *
+makestr(str)
+ char *str;
+{
+ register char *cp, *s;
+
+ str = cp = malloc(strlen(s = str) + 1);
+ if (cp == NULL)
+ fatal("ran out of memory\n");
+ while (*cp++ = *s++)
+ ;
+ return(str);
+}
+
+/*
+ * Allocate a namelist structure.
+ */
+struct namelist *
+makenl(name)
+ char *name;
+{
+ register struct namelist *nl;
+
+ nl = ALLOC(namelist);
+ if (nl == NULL)
+ fatal("ran out of memory\n");
+ nl->n_name = name;
+ nl->n_next = NULL;
+ return(nl);
+}
+
+/*
+ * Make a sub command for lists of variables, commands, etc.
+ */
+struct subcmd *
+makesubcmd(type)
+ int type;
+{
+ register struct subcmd *sc;
+
+ sc = ALLOC(subcmd);
+ if (sc == NULL)
+ fatal("ran out of memory\n");
+ sc->sc_type = type;
+ sc->sc_args = NULL;
+ sc->sc_next = NULL;
+ sc->sc_name = NULL;
+ return(sc);
+}
diff --git a/usr.bin/rdist/lookup.c b/usr.bin/rdist/lookup.c
new file mode 100644
index 00000000000..69e15dd6304
--- /dev/null
+++ b/usr.bin/rdist/lookup.c
@@ -0,0 +1,167 @@
+/*
+ * 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
+/* from: static char sccsid[] = "@(#)lookup.c 8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: lookup.c,v 1.1 1995/10/18 08:45:59 deraadt Exp $";
+#endif /* not lint */
+
+#include "defs.h"
+
+ /* symbol types */
+#define VAR 1
+#define CONST 2
+
+struct syment {
+ int s_type;
+ char *s_name;
+ struct namelist *s_value;
+ struct syment *s_next;
+};
+
+static struct syment *hashtab[HASHSIZE];
+
+/*
+ * Define a variable from a command line argument.
+ */
+void
+define(name)
+ char *name;
+{
+ register char *cp, *s;
+ register struct namelist *nl;
+ struct namelist *value;
+
+ if (debug)
+ printf("define(%s)\n", name);
+
+ cp = index(name, '=');
+ if (cp == NULL)
+ value = NULL;
+ else if (cp[1] == '\0') {
+ *cp = '\0';
+ value = NULL;
+ } else if (cp[1] != '(') {
+ *cp++ = '\0';
+ value = makenl(cp);
+ } else {
+ nl = NULL;
+ *cp++ = '\0';
+ do
+ cp++;
+ while (*cp == ' ' || *cp == '\t');
+ for (s = cp; ; s++) {
+ switch (*s) {
+ case ')':
+ *s = '\0';
+ case '\0':
+ break;
+ case ' ':
+ case '\t':
+ *s++ = '\0';
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == ')')
+ *s = '\0';
+ break;
+ default:
+ continue;
+ }
+ if (nl == NULL)
+ value = nl = makenl(cp);
+ else {
+ nl->n_next = makenl(cp);
+ nl = nl->n_next;
+ }
+ if (*s == '\0')
+ break;
+ cp = s;
+ }
+ }
+ (void) lookup(name, REPLACE, value);
+}
+
+/*
+ * Lookup name in the table and return a pointer to it.
+ * LOOKUP - just do lookup, return NULL if not found.
+ * INSERT - insert name with value, error if already defined.
+ * REPLACE - insert or replace name with value.
+ */
+
+struct namelist *
+lookup(name, action, value)
+ char *name;
+ int action;
+ struct namelist *value;
+{
+ register unsigned n;
+ register char *cp;
+ register struct syment *s;
+ char buf[256];
+
+ if (debug)
+ printf("lookup(%s, %d, %x)\n", name, action, value);
+
+ n = 0;
+ for (cp = name; *cp; )
+ n += *cp++;
+ n %= HASHSIZE;
+
+ for (s = hashtab[n]; s != NULL; s = s->s_next) {
+ if (strcmp(name, s->s_name))
+ continue;
+ if (action != LOOKUP) {
+ if (action != INSERT || s->s_type != CONST) {
+ (void)sprintf(buf, "%s redefined", name);
+ yyerror(buf);
+ }
+ }
+ return(s->s_value);
+ }
+
+ if (action == LOOKUP) {
+ (void)sprintf(buf, "%s undefined", name);
+ yyerror(buf);
+ return(NULL);
+ }
+
+ s = ALLOC(syment);
+ if (s == NULL)
+ fatal("ran out of memory\n");
+ s->s_next = hashtab[n];
+ hashtab[n] = s;
+ s->s_type = action == INSERT ? VAR : CONST;
+ s->s_name = name;
+ s->s_value = value;
+ return(value);
+}
diff --git a/usr.bin/rdist/main.c b/usr.bin/rdist/main.c
new file mode 100644
index 00000000000..037836000bd
--- /dev/null
+++ b/usr.bin/rdist/main.c
@@ -0,0 +1,328 @@
+/*
+ * 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 copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/* from: static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: main.c,v 1.1 1995/10/18 08:45:59 deraadt Exp $";
+#endif /* not lint */
+
+#include "defs.h"
+
+#define NHOSTS 100
+
+/*
+ * Remote distribution program.
+ */
+
+char *distfile = NULL;
+#define _RDIST_TMP "/rdistXXXXXX"
+char tempfile[sizeof _PATH_TMP + sizeof _RDIST_TMP + 1];
+char *tempname;
+
+int debug; /* debugging flag */
+int nflag; /* NOP flag, just print commands without executing */
+int qflag; /* Quiet. Don't print messages */
+int options; /* global options */
+int iamremote; /* act as remote server for transfering files */
+
+FILE *fin = NULL; /* input file pointer */
+int rem = -1; /* file descriptor to remote source/sink process */
+char host[32]; /* host name */
+int nerrs; /* number of errors while sending/receiving */
+char user[10]; /* user's name */
+char homedir[128]; /* user's home directory */
+int userid; /* user's user ID */
+int groupid; /* user's group ID */
+
+struct passwd *pw; /* pointer to static area used by getpwent */
+struct group *gr; /* pointer to static area used by getgrent */
+
+static void usage __P((void));
+static void docmdargs __P((int, char *[]));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *arg;
+ int cmdargs = 0;
+ char *dhosts[NHOSTS], **hp = dhosts;
+
+ pw = getpwuid(userid = getuid());
+ if (pw == NULL) {
+ fprintf(stderr, "%s: Who are you?\n", argv[0]);
+ exit(1);
+ }
+ strcpy(user, pw->pw_name);
+ strcpy(homedir, pw->pw_dir);
+ groupid = pw->pw_gid;
+ gethostname(host, sizeof(host));
+ strcpy(tempfile, _PATH_TMP);
+ strcat(tempfile, _RDIST_TMP);
+ if ((tempname = rindex(tempfile, '/')) != 0)
+ tempname++;
+ else
+ tempname = tempfile;
+
+ while (--argc > 0) {
+ if ((arg = *++argv)[0] != '-')
+ break;
+ if (!strcmp(arg, "-Server"))
+ iamremote++;
+ else while (*++arg)
+ switch (*arg) {
+ case 'f':
+ if (--argc <= 0)
+ usage();
+ distfile = *++argv;
+ if (distfile[0] == '-' && distfile[1] == '\0')
+ fin = stdin;
+ break;
+
+ case 'm':
+ if (--argc <= 0)
+ usage();
+ if (hp >= &dhosts[NHOSTS-2]) {
+ fprintf(stderr, "rdist: too many destination hosts\n");
+ exit(1);
+ }
+ *hp++ = *++argv;
+ break;
+
+ case 'd':
+ if (--argc <= 0)
+ usage();
+ define(*++argv);
+ break;
+
+ case 'D':
+ debug++;
+ break;
+
+ case 'c':
+ cmdargs++;
+ break;
+
+ case 'n':
+ if (options & VERIFY) {
+ printf("rdist: -n overrides -v\n");
+ options &= ~VERIFY;
+ }
+ nflag++;
+ break;
+
+ case 'q':
+ qflag++;
+ break;
+
+ case 'b':
+ options |= COMPARE;
+ break;
+
+ case 'R':
+ options |= REMOVE;
+ break;
+
+ case 'v':
+ if (nflag) {
+ printf("rdist: -n overrides -v\n");
+ break;
+ }
+ options |= VERIFY;
+ break;
+
+ case 'w':
+ options |= WHOLE;
+ break;
+
+ case 'y':
+ options |= YOUNGER;
+ break;
+
+ case 'h':
+ options |= FOLLOW;
+ break;
+
+ case 'i':
+ options |= IGNLNKS;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ *hp = NULL;
+
+ seteuid(userid);
+ mktemp(tempfile);
+
+ if (iamremote) {
+ server();
+ exit(nerrs != 0);
+ }
+
+ if (cmdargs)
+ docmdargs(argc, argv);
+ else {
+ if (fin == NULL) {
+ if(distfile == NULL) {
+ if((fin = fopen("distfile","r")) == NULL)
+ fin = fopen("Distfile", "r");
+ } else
+ fin = fopen(distfile, "r");
+ if(fin == NULL) {
+ perror(distfile ? distfile : "distfile");
+ exit(1);
+ }
+ }
+ yyparse();
+ if (nerrs == 0)
+ docmds(dhosts, argc, argv);
+ }
+
+ exit(nerrs != 0);
+}
+
+static void
+usage()
+{
+ printf("Usage: rdist [-nqbhirvwyD] [-f distfile] [-d var=value] [-m host] [file ...]\n");
+ printf("or: rdist [-nqbhirvwyD] -c source [...] machine[:dest]\n");
+ exit(1);
+}
+
+/*
+ * rcp like interface for distributing files.
+ */
+static void
+docmdargs(nargs, args)
+ int nargs;
+ char *args[];
+{
+ register struct namelist *nl, *prev;
+ register char *cp;
+ struct namelist *files, *hosts;
+ struct subcmd *cmds;
+ char *dest;
+ static struct namelist tnl = { NULL, NULL };
+ int i;
+
+ if (nargs < 2)
+ usage();
+
+ prev = NULL;
+ for (i = 0; i < nargs - 1; i++) {
+ nl = makenl(args[i]);
+ if (prev == NULL)
+ files = prev = nl;
+ else {
+ prev->n_next = nl;
+ prev = nl;
+ }
+ }
+
+ cp = args[i];
+ if ((dest = index(cp, ':')) != NULL)
+ *dest++ = '\0';
+ tnl.n_name = cp;
+ hosts = expand(&tnl, E_ALL);
+ if (nerrs)
+ exit(1);
+
+ if (dest == NULL || *dest == '\0')
+ cmds = NULL;
+ else {
+ cmds = makesubcmd(INSTALL);
+ cmds->sc_options = options;
+ cmds->sc_name = dest;
+ }
+
+ if (debug) {
+ printf("docmdargs()\nfiles = ");
+ prnames(files);
+ printf("hosts = ");
+ prnames(hosts);
+ }
+ insert(NULL, files, hosts, cmds);
+ docmds(NULL, 0, NULL);
+}
+
+/*
+ * Print a list of NAME blocks (mostly for debugging).
+ */
+void
+prnames(nl)
+ register struct namelist *nl;
+{
+ printf("( ");
+ while (nl != NULL) {
+ printf("%s ", nl->n_name);
+ nl = nl->n_next;
+ }
+ printf(")\n");
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ extern int yylineno;
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "rdist: line %d: Warning: ", yylineno);
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+ va_end(ap);
+}
diff --git a/usr.bin/rdist/pathnames.h b/usr.bin/rdist/pathnames.h
new file mode 100644
index 00000000000..c81b7d5fa62
--- /dev/null
+++ b/usr.bin/rdist/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 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.
+ *
+ * from: @(#)pathnames.h 8.1 (Berkeley) 6/9/93
+ * $Id: pathnames.h,v 1.1 1995/10/18 08:45:59 deraadt Exp $
+ */
+
+#include <paths.h>
+
+#define _PATH_RDIST "rdist"
diff --git a/usr.bin/rdist/rdist.1 b/usr.bin/rdist/rdist.1
new file mode 100644
index 00000000000..3a0287ddeee
--- /dev/null
+++ b/usr.bin/rdist/rdist.1
@@ -0,0 +1,413 @@
+.\" Copyright (c) 1985, 1990, 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.
+.\"
+.\" from: @(#)rdist.1 8.2 (Berkeley) 12/30/93
+.\" $Id: rdist.1,v 1.1 1995/10/18 08:45:59 deraadt Exp $
+.\"
+.Dd December 30, 1993
+.Dt RDIST 1
+.Os BSD 4.3
+.Sh NAME
+.Nm rdist
+.Nd remote file distribution program
+.Sh SYNOPSIS
+.Nm rdist
+.Op Fl nqbRhivwy
+.Op Fl f Ar distfile
+.Op Fl d Ar var=value
+.Op Fl m Ar host
+.Op Ar name ...
+.Nm rdist
+.Op Fl nqbRhivwy
+.Fl c
+.Ar name ...
+.Oo login@ Oc Ns Ar host Ns Op :dest
+.Sh DESCRIPTION
+.Nm Rdist
+is a program to maintain identical copies of files over multiple hosts.
+It preserves the owner, group, mode, and mtime of files if possible and
+can update programs that are executing.
+.Nm Rdist
+reads commands from
+.Ar distfile
+to direct the updating of files and/or directories.
+.Pp
+Options specific to the first SYNOPSIS form:
+.Pp
+.Bl -tag -width indent
+.It Fl
+If
+.Ar distfile
+is
+.Sq Fl ,
+the standard input is used.
+.It Fl f Ar distfile
+Use the specified
+.Ar distfile.
+.El
+.Pp
+If either the
+.Fl f
+or
+.Sq Fl
+option is not specified, the program looks first for
+.Dq Pa distfile ,
+then
+.Dq Pa Distfile
+to use as the input.
+If no names are specified on the command line,
+.Nm rdist
+will update all of the files and directories listed in
+.Ar distfile .
+Otherwise, the argument is taken to be the name of a file to be updated
+or the label of a command to execute. If label and file names conflict,
+it is assumed to be a label.
+These may be used together to update specific files
+using specific commands.
+.Pp
+Options specific to the second SYNOPSIS form:
+.Pp
+.Bl -tag -width Fl c
+.It Fl c
+Forces
+.Nm rdist
+to interpret the remaining arguments as a small
+.Ar distfile .
+.Pp
+The equivalent distfile is as follows.
+.Pp
+.Bd -filled -offset indent -compact
+.Pq Ar name ...
+.Li ->
+.Op Ar login@
+.Ar host
+.Bd -filled -offset indent -compact
+.Li install
+.Op Ar dest ;
+.Ed
+.Ed
+.El
+.Pp
+Options common to both forms:
+.Pp
+.Bl -tag -width Ic
+.It Fl b
+Binary comparison. Perform a binary comparison and update files if they differ
+rather than comparing dates and sizes.
+.It Fl d Ar var=value
+Define
+.Ar var
+to have
+.Ar value .
+The
+.Fl d
+option is used to define or override variable definitions in the
+.Ar distfile .
+.Ar Value
+can be the empty string, one name, or a list of names surrounded by
+parentheses and separated by tabs and/or spaces.
+.It Fl h
+Follow symbolic links. Copy the file that the link points to rather than the
+link itself.
+.It Fl i
+Ignore unresolved links.
+.Nm Rdist
+will normally try to maintain the link structure of files being transferred
+and warn the user if all the links cannot be found.
+.It Fl m Ar host
+Limit which machines are to be updated. Multiple
+.Fl m
+arguments can be given to limit updates to a subset of the hosts listed in the
+.Ar distfile .
+.It Fl n
+Print the commands without executing them. This option is
+useful for debugging
+.Ar distfile .
+.It Fl q
+Quiet mode. Files that are being modified are normally
+printed on standard output. The
+.Fl q
+option suppresses this.
+.It Fl R
+Remove extraneous files. If a directory is being updated, any files that exist
+on the remote host that do not exist in the master directory are removed.
+This is useful for maintaining truly identical copies of directories.
+.It Fl v
+Verify that the files are up to date on all the hosts. Any files
+that are out of date will be displayed but no files will be changed
+nor any mail sent.
+.It Fl w
+Whole mode. The whole file name is appended to the destination directory
+name. Normally, only the last component of a name is used when renaming files.
+This will preserve the directory structure of the files being
+copied instead of flattening the directory structure. For example,
+renaming a list of files such as ( dir1/f1 dir2/f2 ) to dir3 would create
+files dir3/dir1/f1 and dir3/dir2/f2 instead of dir3/f1 and dir3/f2.
+.It Fl y
+Younger mode. Files are normally updated if their
+.Ar mtime
+and
+.Ar size
+(see
+.Xr stat 2 )
+disagree. The
+.Fl y
+option causes
+.Nm rdist
+not to update files that are younger than the master copy.
+This can be used
+to prevent newer copies on other hosts from being replaced.
+A warning message is printed for files which are newer than the master copy.
+.El
+.Pp
+.Ar Distfile
+contains a sequence of entries that specify the files
+to be copied, the destination hosts, and what operations to perform
+to do the updating. Each entry has one of the following formats.
+.Pp
+.Bd -literal -offset indent -compact
+<variable name> `=' <name list>
+[label:]<source list> `\->' <destination list> <command list>
+[label:]<source list> `::' <time_stamp file> <command list>
+.Ed
+.Pp
+The first format is used for defining variables.
+The second format is used for distributing files to other hosts.
+The third format is used for making lists of files that have been changed
+since some given date.
+The
+.Ar source list
+specifies a
+list of files and/or directories on the local host which are to be used
+as the master copy for distribution.
+The
+.Ar destination list
+is the list of hosts to which these files are to be
+copied. Each file in the source list is added to a list of changes
+if the file is out of date on the host which is being updated (second format) or
+the file is newer than the time stamp file (third format).
+.Pp
+Labels are optional. They are used to identify a command for partial updates.
+.Pp
+Newlines, tabs, and blanks are only used as separators and are
+otherwise ignored. Comments begin with `#' and end with a newline.
+.Pp
+Variables to be expanded begin with `$' followed by one character or
+a name enclosed in curly braces (see the examples at the end).
+.Pp
+The source and destination lists have the following format:
+.Bd -literal -offset indent
+<name>
+.Ed
+or
+.Bd -literal -offset indent -compact
+`(' <zero or more names separated by white-space> `)'
+.Ed
+.Pp
+The shell meta-characters `[', `]', `{', `}', `*', and `?'
+are recognized and expanded (on the local host only) in the same way as
+.Xr csh 1 .
+They can be escaped with a backslash.
+The `~' character is also expanded in the same way as
+.Xr csh 1
+but is expanded separately on the local and destination hosts.
+When the
+.Fl w
+option is used with a file name that begins with `~', everything except the
+home directory is appended to the destination name.
+File names which do not begin with `/' or `~' use the destination user's
+home directory as the root directory for the rest of the file name.
+.Pp
+The command list consists of zero or more commands of the following
+format.
+.Bd -ragged -offset indent -compact
+.Bl -column except_patx pattern\ listx
+.It `install' <options> opt_dest_name `;'
+.It `notify' <name list> `;'
+.It `except' <name list> `;'
+.It `except_pat' <pattern list> `;'
+.It `special' <name list> string `;'
+.El
+.Ed
+.Pp
+The
+.Ic install
+command is used to copy out of date files and/or directories.
+Each source file is copied to each host in the destination list.
+Directories are recursively copied in the same way.
+.Ar Opt_dest_name
+is an optional parameter to rename files.
+If no
+.Ic install
+command appears in the command list or
+the destination name is not specified,
+the source file name is used.
+Directories in the path name will be created if they
+do not exist on the remote host.
+To help prevent disasters, a non-empty directory on a target host will
+never be replaced with a regular file or a symbolic link.
+However, under the `\-R' option a non-empty directory will be removed
+if the corresponding filename is completely absent on the master host.
+The
+.Ar options
+are `\-R', `\-h', `\-i', `\-v', `\-w', `\-y', and `\-b'
+and have the same semantics as
+options on the command line except they only apply to the files
+in the source list.
+The login name used on the destination host is the same as the local host
+unless the destination name is of the format ``login@host".
+.Pp
+The
+.Ic notify
+command is used to mail the list of files updated (and any errors
+that may have occurred) to the listed names.
+If no `@' appears in the name, the destination host is appended to
+the name
+(e.g., name1@host, name2@host, ...).
+.Pp
+The
+.Ic except
+command is used to update all of the files in the source list
+.Ic except
+for the files listed in
+.Ar name list .
+This is usually used to copy everything in a directory except certain files.
+.Pp
+The
+.Ic except_pat
+command is like the
+.Ic except
+command except that
+.Ar pattern list
+is a list of regular expressions
+(see
+.Xr ed 1
+for details).
+If one of the patterns matches some string within a file name, that file will
+be ignored.
+Note that since `\e' is a quote character, it must be doubled to become
+part of the regular expression. Variables are expanded in
+.Ar pattern list
+but not shell file pattern matching characters. To include a `$', it
+must be escaped with `\e'.
+.Pp
+The
+.Ic special
+command is used to specify
+.Xr sh 1
+commands that are to be executed on the
+remote host after the file in
+.Ar name list
+is updated or installed.
+If the
+.Ar name list
+is omitted then the shell commands will be executed
+for every file updated or installed. The shell variable `FILE' is set
+to the current filename before executing the commands in
+.Ar string .
+.Ar String
+starts and ends with `"' and can cross multiple lines in
+.Ar distfile .
+Multiple commands to the shell should be separated by `;'.
+Commands are executed in the user's home directory on the host
+being updated.
+The
+.Ar special
+command can be used to rebuild private databases, etc.
+after a program has been updated.
+.Pp
+The following is a small example:
+.Bd -literal -offset indent
+HOSTS = ( matisse root@arpa )
+
+FILES = ( /bin /lib /usr/bin /usr/games
+\t/usr/include/{*.h,{stand,sys,vax*,pascal,machine}/*.h}
+\t/usr/lib /usr/man/man? /usr/ucb /usr/local/rdist )
+
+EXLIB = ( Mail.rc aliases aliases.dir aliases.pag crontab dshrc
+\tsendmail.cf sendmail.fc sendmail.hf sendmail.st uucp vfont )
+
+${FILES} -> ${HOSTS}
+\tinstall -R ;
+\texcept /usr/lib/${EXLIB} ;
+\texcept /usr/games/lib ;
+\tspecial /usr/lib/sendmail "/usr/lib/sendmail -bz" ;
+
+srcs:
+/usr/src/bin -> arpa
+\texcept_pat ( \e\e.o\e$ /SCCS\e$ ) ;
+
+IMAGEN = (ips dviimp catdvi)
+
+imagen:
+/usr/local/${IMAGEN} -> arpa
+\tinstall /usr/local/lib ;
+\tnotify ralph ;
+
+${FILES} :: stamp.cory
+\tnotify root@cory ;
+.Ed
+.Sh FILES
+.Bl -tag -width /tmp/rdist* -compact
+.It Pa distfile
+input command file
+.It Pa /tmp/rdist*
+temporary file for update lists
+.El
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr csh 1 ,
+.Xr stat 2
+.Sh HISTORY
+The
+.Nm rdist
+command appeared in
+.Bx 4.3 .
+.Sh DIAGNOSTICS
+A complaint about mismatch of rdist version numbers may really stem
+from some problem with starting your shell, e.g., you are in too many groups.
+.Sh BUGS
+Source files must reside on the local host where
+.Nm rdist
+is executed.
+.Pp
+There is no easy way to have a special command executed after all files
+in a directory have been updated.
+.Pp
+Variable expansion only works for name lists; there should be a general macro
+facility.
+.Pp
+.Nm Rdist
+aborts on files which have a negative mtime (before Jan 1, 1970).
+.Pp
+There should be a `force' option to allow replacement of non-empty directories
+by regular files or symlinks. A means of updating file modes and owners
+of otherwise identical files is also needed.
diff --git a/usr.bin/rdist/server.c b/usr.bin/rdist/server.c
new file mode 100644
index 00000000000..450c8ec8e83
--- /dev/null
+++ b/usr.bin/rdist/server.c
@@ -0,0 +1,1586 @@
+/*
+ * 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
+/* from: static char sccsid[] = "@(#)server.c 8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: server.c,v 1.1 1995/10/18 08:45:59 deraadt Exp $";
+#endif /* not lint */
+
+#include <sys/wait.h>
+#include "defs.h"
+
+#define ack() (void) write(rem, "\0\n", 2)
+#define err() (void) write(rem, "\1\n", 2)
+
+struct linkbuf *ihead; /* list of files with more than one link */
+char buf[BUFSIZ]; /* general purpose buffer */
+char target[BUFSIZ]; /* target/source directory name */
+char *tp; /* pointer to end of target name */
+char *Tdest; /* pointer to last T dest*/
+int catname; /* cat name to target name */
+char *stp[32]; /* stack of saved tp's for directories */
+int oumask; /* old umask for creating files */
+
+extern FILE *lfp; /* log file for mailing changes */
+
+static int chkparent __P((char *));
+static void clean __P((char *));
+static void comment __P((char *));
+static void dospecial __P((char *));
+static int fchog __P((int, char *, char *, char *, int));
+static void hardlink __P((char *));
+static void note __P((const char *, ...));
+static void query __P((char *));
+static void recvf __P((char *, int));
+static void removeit __P((struct stat *));
+static int response __P((void));
+static void rmchk __P((int));
+static struct linkbuf *
+ savelink __P((struct stat *));
+static void sendf __P((char *, int));
+static int update __P((char *, int, struct stat *));
+
+/*
+ * Server routine to read requests and process them.
+ * Commands are:
+ * Tname - Transmit file if out of date
+ * Vname - Verify if file out of date or not
+ * Qname - Query if file exists. Return mtime & size if it does.
+ */
+void
+server()
+{
+ char cmdbuf[BUFSIZ];
+ register char *cp;
+
+ signal(SIGHUP, cleanup);
+ signal(SIGINT, cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGTERM, cleanup);
+ signal(SIGPIPE, cleanup);
+
+ rem = 0;
+ oumask = umask(0);
+ (void) sprintf(buf, "V%d\n", VERSION);
+ (void) write(rem, buf, strlen(buf));
+
+ for (;;) {
+ cp = cmdbuf;
+ if (read(rem, cp, 1) <= 0)
+ return;
+ if (*cp++ == '\n') {
+ error("server: expected control record\n");
+ continue;
+ }
+ do {
+ if (read(rem, cp, 1) != 1)
+ cleanup(0);
+ } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
+ *--cp = '\0';
+ cp = cmdbuf;
+ switch (*cp++) {
+ case 'T': /* init target file/directory name */
+ catname = 1; /* target should be directory */
+ goto dotarget;
+
+ case 't': /* init target file/directory name */
+ catname = 0;
+ dotarget:
+ if (exptilde(target, cp) == NULL)
+ continue;
+ tp = target;
+ while (*tp)
+ tp++;
+ ack();
+ continue;
+
+ case 'R': /* Transfer a regular file. */
+ recvf(cp, S_IFREG);
+ continue;
+
+ case 'D': /* Transfer a directory. */
+ recvf(cp, S_IFDIR);
+ continue;
+
+ case 'K': /* Transfer symbolic link. */
+ recvf(cp, S_IFLNK);
+ continue;
+
+ case 'k': /* Transfer hard link. */
+ hardlink(cp);
+ continue;
+
+ case 'E': /* End. (of directory) */
+ *tp = '\0';
+ if (catname <= 0) {
+ error("server: too many 'E's\n");
+ continue;
+ }
+ tp = stp[--catname];
+ *tp = '\0';
+ ack();
+ continue;
+
+ case 'C': /* Clean. Cleanup a directory */
+ clean(cp);
+ continue;
+
+ case 'Q': /* Query. Does the file/directory exist? */
+ query(cp);
+ continue;
+
+ case 'S': /* Special. Execute commands */
+ dospecial(cp);
+ continue;
+
+#ifdef notdef
+ /*
+ * These entries are reserved but not currently used.
+ * The intent is to allow remote hosts to have master copies.
+ * Currently, only the host rdist runs on can have masters.
+ */
+ case 'X': /* start a new list of files to exclude */
+ except = bp = NULL;
+ case 'x': /* add name to list of files to exclude */
+ if (*cp == '\0') {
+ ack();
+ continue;
+ }
+ if (*cp == '~') {
+ if (exptilde(buf, cp) == NULL)
+ continue;
+ cp = buf;
+ }
+ if (bp == NULL)
+ except = bp = expand(makeblock(NAME, cp), E_VARS);
+ else
+ bp->b_next = expand(makeblock(NAME, cp), E_VARS);
+ while (bp->b_next != NULL)
+ bp = bp->b_next;
+ ack();
+ continue;
+
+ case 'I': /* Install. Transfer file if out of date. */
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("server: options not delimited\n");
+ return;
+ }
+ install(cp, opts);
+ continue;
+
+ case 'L': /* Log. save message in log file */
+ log(lfp, cp);
+ continue;
+#endif
+
+ case '\1':
+ nerrs++;
+ continue;
+
+ case '\2':
+ return;
+
+ default:
+ error("server: unknown command '%s'\n", cp);
+ case '\0':
+ continue;
+ }
+ }
+}
+
+/*
+ * Update the file(s) if they are different.
+ * destdir = 1 if destination should be a directory
+ * (i.e., more than one source is being copied to the same destination).
+ */
+void
+install(src, dest, destdir, opts)
+ char *src, *dest;
+ int destdir, opts;
+{
+ char *rname;
+ char destcopy[BUFSIZ];
+
+ if (dest == NULL) {
+ opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
+ dest = src;
+ }
+
+ if (nflag || debug) {
+ printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
+ opts & WHOLE ? " -w" : "",
+ opts & YOUNGER ? " -y" : "",
+ opts & COMPARE ? " -b" : "",
+ opts & REMOVE ? " -R" : "", src, dest);
+ if (nflag)
+ return;
+ }
+
+ rname = exptilde(target, src);
+ if (rname == NULL)
+ return;
+ tp = target;
+ while (*tp)
+ tp++;
+ /*
+ * If we are renaming a directory and we want to preserve
+ * the directory heirarchy (-w), we must strip off the leading
+ * directory name and preserve the rest.
+ */
+ if (opts & WHOLE) {
+ while (*rname == '/')
+ rname++;
+ destdir = 1;
+ } else {
+ rname = rindex(target, '/');
+ if (rname == NULL)
+ rname = target;
+ else
+ rname++;
+ }
+ if (debug)
+ printf("target = %s, rname = %s\n", target, rname);
+ /*
+ * Pass the destination file/directory name to remote.
+ */
+ (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0)
+ return;
+
+ if (destdir) {
+ strcpy(destcopy, dest);
+ Tdest = destcopy;
+ }
+ sendf(rname, opts);
+ Tdest = 0;
+}
+
+#define protoname() (pw ? pw->pw_name : user)
+#define protogroup() (gr ? gr->gr_name : group)
+/*
+ * Transfer the file or directory in target[].
+ * rname is the name of the file on the remote host.
+ */
+static void
+sendf(rname, opts)
+ char *rname;
+ int opts;
+{
+ register struct subcmd *sc;
+ struct stat stb;
+ int sizerr, f, u, len;
+ off_t i;
+ DIR *d;
+ struct direct *dp;
+ char *otp, *cp;
+ extern struct subcmd *subcmds;
+ static char user[15], group[15];
+
+ if (debug)
+ printf("sendf(%s, %x)\n", rname, opts);
+
+ if (except(target))
+ return;
+ if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ if ((u = update(rname, opts, &stb)) == 0) {
+ if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
+ (void) savelink(&stb);
+ return;
+ }
+
+ if (pw == NULL || pw->pw_uid != stb.st_uid)
+ if ((pw = getpwuid(stb.st_uid)) == NULL) {
+ log(lfp, "%s: no password entry for uid %d \n",
+ target, stb.st_uid);
+ pw = NULL;
+ (void)sprintf(user, ":%lu", stb.st_uid);
+ }
+ if (gr == NULL || gr->gr_gid != stb.st_gid)
+ if ((gr = getgrgid(stb.st_gid)) == NULL) {
+ log(lfp, "%s: no name for group %d\n",
+ target, stb.st_gid);
+ gr = NULL;
+ (void)sprintf(group, ":%lu", stb.st_gid);
+ }
+ if (u == 1) {
+ if (opts & VERIFY) {
+ log(lfp, "need to install: %s\n", target);
+ goto dospecial;
+ }
+ log(lfp, "installing: %s\n", target);
+ opts &= ~(COMPARE|REMOVE);
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ if ((d = opendir(target)) == NULL) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
+ stb.st_mode & 07777, protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ closedir(d);
+ return;
+ }
+
+ if (opts & REMOVE)
+ rmchk(opts);
+
+ 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--;
+ sendf(dp->d_name, opts);
+ }
+ closedir(d);
+ (void) write(rem, "E\n", 2);
+ (void) response();
+ tp = otp;
+ *tp = '\0';
+ return;
+
+ case S_IFLNK:
+ if (u != 1)
+ opts |= COMPARE;
+ if (stb.st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = savelink(&stb)) != NULL) {
+ /* install link */
+ if (*lp->target == 0)
+ (void) sprintf(buf, "k%o %s %s\n", opts,
+ lp->pathname, rname);
+ else
+ (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+ lp->target, lp->pathname, rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ (void) response();
+ return;
+ }
+ }
+ (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts,
+ stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+ protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0)
+ return;
+ sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
+ (void) write(rem, buf, stb.st_size);
+ if (debug)
+ printf("readlink = %.*s\n", (int)stb.st_size, buf);
+ goto done;
+
+ case S_IFREG:
+ break;
+
+ default:
+ error("%s: not a file or directory\n", target);
+ return;
+ }
+
+ if (u == 2) {
+ if (opts & VERIFY) {
+ log(lfp, "need to update: %s\n", target);
+ goto dospecial;
+ }
+ log(lfp, "updating: %s\n", target);
+ }
+
+ if (stb.st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = savelink(&stb)) != NULL) {
+ /* install link */
+ if (*lp->target == 0)
+ (void) sprintf(buf, "k%o %s %s\n", opts,
+ lp->pathname, rname);
+ else
+ (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+ lp->target, lp->pathname, rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ (void) response();
+ return;
+ }
+ }
+
+ if ((f = open(target, O_RDONLY, 0)) < 0) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts,
+ stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+ protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ (void) close(f);
+ return;
+ }
+ sizerr = 0;
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ int amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt)
+ sizerr = 1;
+ (void) write(rem, buf, amt);
+ }
+ (void) close(f);
+done:
+ if (sizerr) {
+ error("%s: file changed size\n", target);
+ err();
+ } else
+ ack();
+ f = response();
+ if (f < 0 || f == 0 && (opts & COMPARE))
+ return;
+dospecial:
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != SPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
+ continue;
+ log(lfp, "special \"%s\"\n", sc->sc_name);
+ if (opts & VERIFY)
+ continue;
+ (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ while (response() > 0)
+ ;
+ }
+}
+
+static struct linkbuf *
+savelink(stp)
+ struct stat *stp;
+{
+ struct linkbuf *lp;
+
+ for (lp = ihead; lp != NULL; lp = lp->nextp)
+ if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
+ lp->count--;
+ return(lp);
+ }
+ lp = (struct linkbuf *) malloc(sizeof(*lp));
+ if (lp == NULL)
+ log(lfp, "out of memory, link information lost\n");
+ else {
+ lp->nextp = ihead;
+ ihead = lp;
+ lp->inum = stp->st_ino;
+ lp->devnum = stp->st_dev;
+ lp->count = stp->st_nlink - 1;
+ strcpy(lp->pathname, target);
+ if (Tdest)
+ strcpy(lp->target, Tdest);
+ else
+ *lp->target = 0;
+ }
+ return(NULL);
+}
+
+/*
+ * Check to see if file needs to be updated on the remote machine.
+ * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
+ * and 3 if comparing binaries to determine if out of date.
+ */
+static int
+update(rname, opts, stp)
+ char *rname;
+ int opts;
+ struct stat *stp;
+{
+ register char *cp, *s;
+ register off_t size;
+ register time_t mtime;
+
+ if (debug)
+ printf("update(%s, %x, %x)\n", rname, opts, stp);
+
+ /*
+ * Check to see if the file exists on the remote machine.
+ */
+ (void) sprintf(buf, "Q%s\n", rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+again:
+ cp = s = buf;
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+
+ switch (*s++) {
+ case 'Y':
+ break;
+
+ case 'N': /* file doesn't exist so install it */
+ return(1);
+
+ case '\1':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ return(0);
+
+ case '\3':
+ *--cp = '\0';
+ if (lfp != NULL)
+ log(lfp, "update: note: %s\n", s);
+ goto again;
+
+ default:
+ *--cp = '\0';
+ error("update: unexpected response '%s'\n", s);
+ return(0);
+ }
+
+ if (*s == '\n')
+ return(2);
+
+ if (opts & COMPARE)
+ return(3);
+
+ size = 0;
+ while (isdigit(*s))
+ size = size * 10 + (*s++ - '0');
+ if (*s++ != ' ') {
+ error("update: size not delimited\n");
+ return(0);
+ }
+ mtime = 0;
+ while (isdigit(*s))
+ mtime = mtime * 10 + (*s++ - '0');
+ if (*s != '\n') {
+ error("update: mtime not delimited\n");
+ return(0);
+ }
+ /*
+ * File needs to be updated?
+ */
+ if (opts & YOUNGER) {
+ if (stp->st_mtime == mtime)
+ return(0);
+ if (stp->st_mtime < mtime) {
+ log(lfp, "Warning: %s: remote copy is newer\n", target);
+ return(0);
+ }
+ } else if (stp->st_mtime == mtime && stp->st_size == size)
+ return(0);
+ return(2);
+}
+
+/*
+ * Query. Check to see if file exists. Return one of the following:
+ * N\n - doesn't exist
+ * Ysize mtime\n - exists and its a regular file (size & mtime of file)
+ * Y\n - exists and its a directory or symbolic link
+ * ^Aerror message\n
+ */
+static void
+query(name)
+ char *name;
+{
+ struct stat stb;
+
+ if (catname)
+ (void) sprintf(tp, "/%s", name);
+
+ if (lstat(target, &stb) < 0) {
+ if (errno == ENOENT)
+ (void) write(rem, "N\n", 2);
+ else
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ *tp = '\0';
+ return;
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFREG:
+ (void) sprintf(buf, "Y%qd %ld\n", stb.st_size,
+ stb.st_mtime);
+ (void) write(rem, buf, strlen(buf));
+ break;
+
+ case S_IFLNK:
+ case S_IFDIR:
+ (void) write(rem, "Y\n", 2);
+ break;
+
+ default:
+ error("%s: not a file or directory\n", name);
+ break;
+ }
+ *tp = '\0';
+}
+
+static void
+recvf(cmd, type)
+ char *cmd;
+ int type;
+{
+ register char *cp;
+ int f, mode, opts, wrerr, olderrno;
+ off_t i, size;
+ time_t mtime;
+ struct stat stb;
+ struct timeval tvp[2];
+ char *owner, *group;
+ char new[BUFSIZ];
+ extern char *tempname;
+
+ cp = cmd;
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: options not delimited\n");
+ return;
+ }
+ mode = 0;
+ while (*cp >= '0' && *cp <= '7')
+ mode = (mode << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: mode not delimited\n");
+ return;
+ }
+ size = 0;
+ while (isdigit(*cp))
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: size not delimited\n");
+ return;
+ }
+ mtime = 0;
+ while (isdigit(*cp))
+ mtime = mtime * 10 + (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: mtime not delimited\n");
+ return;
+ }
+ owner = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("recvf: owner name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+ group = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("recvf: group name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+
+ if (type == S_IFDIR) {
+ if (catname >= sizeof(stp)) {
+ error("%s:%s: too many directory levels\n",
+ host, target);
+ return;
+ }
+ stp[catname] = tp;
+ if (catname++) {
+ *tp++ = '/';
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ }
+ if (opts & VERIFY) {
+ ack();
+ return;
+ }
+ if (lstat(target, &stb) == 0) {
+ if (ISDIR(stb.st_mode)) {
+ if ((stb.st_mode & 07777) == mode) {
+ ack();
+ return;
+ }
+ buf[0] = '\0';
+ (void) sprintf(buf + 1,
+ "%s: Warning: remote mode %o != local mode %o\n",
+ target, stb.st_mode & 07777, mode);
+ (void) write(rem, buf, strlen(buf + 1) + 1);
+ return;
+ }
+ errno = ENOTDIR;
+ } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
+ chkparent(target) == 0 && mkdir(target, mode) == 0)) {
+ if (fchog(-1, target, owner, group, mode) == 0)
+ ack();
+ return;
+ }
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ tp = stp[--catname];
+ *tp = '\0';
+ return;
+ }
+
+ if (catname)
+ (void) sprintf(tp, "/%s", cp);
+ cp = rindex(target, '/');
+ if (cp == NULL)
+ strcpy(new, tempname);
+ else if (cp == target)
+ (void) sprintf(new, "/%s", tempname);
+ else {
+ *cp = '\0';
+ (void) sprintf(new, "%s/%s", target, tempname);
+ *cp = '/';
+ }
+
+ if (type == S_IFLNK) {
+ int j;
+
+ ack();
+ cp = buf;
+ for (i = 0; i < size; i += j) {
+ if ((j = read(rem, cp, size - i)) <= 0)
+ cleanup(0);
+ cp += j;
+ }
+ *cp = '\0';
+ if (response() < 0) {
+ err();
+ return;
+ }
+ if (symlink(buf, new) < 0) {
+ if (errno != ENOENT || chkparent(new) < 0 ||
+ symlink(buf, new) < 0)
+ goto badnew1;
+ }
+ mode &= 0777;
+ if (opts & COMPARE) {
+ char tbuf[BUFSIZ];
+
+ if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
+ i == size && strncmp(buf, tbuf, size) == 0) {
+ (void) unlink(new);
+ ack();
+ return;
+ }
+ if (opts & VERIFY)
+ goto differ;
+ }
+ goto fixup;
+ }
+
+ if ((f = creat(new, mode)) < 0) {
+ if (errno != ENOENT || chkparent(new) < 0 ||
+ (f = creat(new, mode)) < 0)
+ goto badnew1;
+ }
+
+ ack();
+ wrerr = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ int amt = BUFSIZ;
+
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ int j = read(rem, cp, amt);
+
+ if (j <= 0) {
+ (void) close(f);
+ (void) unlink(new);
+ cleanup(0);
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (wrerr == 0 && write(f, buf, amt) != amt) {
+ olderrno = errno;
+ wrerr++;
+ }
+ }
+ if (response() < 0) {
+ err();
+ goto badnew2;
+ }
+ if (wrerr)
+ goto badnew1;
+ if (opts & COMPARE) {
+ FILE *f1, *f2;
+ int c;
+
+ if ((f1 = fopen(target, "r")) == NULL)
+ goto badtarget;
+ if ((f2 = fopen(new, "r")) == NULL) {
+badnew1: error("%s:%s: %s\n", host, new, strerror(errno));
+ goto badnew2;
+ }
+ while ((c = getc(f1)) == getc(f2))
+ if (c == EOF) {
+ (void) fclose(f1);
+ (void) fclose(f2);
+ ack();
+ goto badnew2;
+ }
+ (void) fclose(f1);
+ (void) fclose(f2);
+ if (opts & VERIFY) {
+differ: buf[0] = '\0';
+ (void) sprintf(buf + 1, "need to update: %s\n",target);
+ (void) write(rem, buf, strlen(buf + 1) + 1);
+ goto badnew2;
+ }
+ }
+
+ /*
+ * Set last modified time
+ */
+ tvp[0].tv_sec = time(0);
+ tvp[0].tv_usec = 0;
+ tvp[1].tv_sec = mtime;
+ tvp[1].tv_usec = 0;
+ if (utimes(new, tvp) < 0)
+ note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
+
+ if (fchog(f, new, owner, group, mode) < 0) {
+badnew2: (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+ (void) close(f);
+
+fixup: if (rename(new, target) < 0) {
+badtarget: error("%s:%s: %s\n", host, target, strerror(errno));
+ (void) unlink(new);
+ return;
+ }
+
+ if (opts & COMPARE) {
+ buf[0] = '\0';
+ (void) sprintf(buf + 1, "updated %s\n", target);
+ (void) write(rem, buf, strlen(buf + 1) + 1);
+ } else
+ ack();
+}
+
+/*
+ * Creat a hard link to existing file.
+ */
+static void
+hardlink(cmd)
+ char *cmd;
+{
+ register char *cp;
+ struct stat stb;
+ char *oldname;
+ int opts, exists = 0;
+
+ cp = cmd;
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("hardlink: options not delimited\n");
+ return;
+ }
+ oldname = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("hardlink: oldname name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+
+ if (catname) {
+ (void) sprintf(tp, "/%s", cp);
+ }
+ if (lstat(target, &stb) == 0) {
+ int mode = stb.st_mode & S_IFMT;
+ if (mode != S_IFREG && mode != S_IFLNK) {
+ error("%s:%s: not a regular file\n", host, target);
+ return;
+ }
+ exists = 1;
+ }
+ if (chkparent(target) < 0 ) {
+ error("%s:%s: %s (no parent)\n",
+ host, target, strerror(errno));
+ return;
+ }
+ if (exists && (unlink(target) < 0)) {
+ error("%s:%s: %s (unlink)\n",
+ host, target, strerror(errno));
+ return;
+ }
+ if (link(oldname, target) < 0) {
+ error("%s:can't link %s to %s\n",
+ host, target, oldname);
+ return;
+ }
+ ack();
+}
+
+/*
+ * Check to see if parent directory exists and create one if not.
+ */
+static int
+chkparent(name)
+ char *name;
+{
+ register char *cp;
+ struct stat stb;
+
+ cp = rindex(name, '/');
+ if (cp == NULL || cp == name)
+ return(0);
+ *cp = '\0';
+ if (lstat(name, &stb) < 0) {
+ if (errno == ENOENT && chkparent(name) >= 0 &&
+ mkdir(name, 0777 & ~oumask) >= 0) {
+ *cp = '/';
+ return(0);
+ }
+ } else if (ISDIR(stb.st_mode)) {
+ *cp = '/';
+ return(0);
+ }
+ *cp = '/';
+ return(-1);
+}
+
+/*
+ * Change owner, group and mode of file.
+ */
+static int
+fchog(fd, file, owner, group, mode)
+ int fd;
+ char *file, *owner, *group;
+ int mode;
+{
+ register int i;
+ int uid, gid;
+ extern char user[];
+ extern int userid;
+
+ uid = userid;
+ if (userid == 0) {
+ if (*owner == ':') {
+ uid = atoi(owner + 1);
+ } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
+ if ((pw = getpwnam(owner)) == NULL) {
+ if (mode & 04000) {
+ note("%s:%s: unknown login name, clearing setuid",
+ host, owner);
+ mode &= ~04000;
+ uid = 0;
+ }
+ } else
+ uid = pw->pw_uid;
+ } else
+ uid = pw->pw_uid;
+ if (*group == ':') {
+ gid = atoi(group + 1);
+ goto ok;
+ }
+ } else if ((mode & 04000) && strcmp(user, owner) != 0)
+ mode &= ~04000;
+ gid = -1;
+ if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
+ if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
+ || ((gr = getgrnam(group)) == NULL)) {
+ if (mode & 02000) {
+ note("%s:%s: unknown group", host, group);
+ mode &= ~02000;
+ }
+ } else
+ gid = gr->gr_gid;
+ } else
+ gid = gr->gr_gid;
+ if (userid && gid >= 0) {
+ if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
+ if (!(strcmp(user, gr->gr_mem[i])))
+ goto ok;
+ mode &= ~02000;
+ gid = -1;
+ }
+ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
+ note("%s: %s chown: %s", host, file, strerror(errno));
+ else if (mode & 07000 &&
+ (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
+ note("%s: %s chmod: %s", host, file, strerror(errno));
+ return(0);
+}
+
+/*
+ * Check for files on the machine being updated that are not on the master
+ * machine and remove them.
+ */
+static void
+rmchk(opts)
+ int opts;
+{
+ register char *cp, *s;
+ struct stat stb;
+
+ if (debug)
+ printf("rmchk()\n");
+
+ /*
+ * Tell the remote to clean the files from the last directory sent.
+ */
+ (void) sprintf(buf, "C%o\n", opts & VERIFY);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0)
+ return;
+ for (;;) {
+ cp = s = buf;
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+
+ switch (*s++) {
+ case 'Q': /* Query if file should be removed */
+ /*
+ * Return the following codes to remove query.
+ * N\n -- file exists - DON'T remove.
+ * Y\n -- file doesn't exist - REMOVE.
+ */
+ *--cp = '\0';
+ (void) sprintf(tp, "/%s", s);
+ if (debug)
+ printf("check %s\n", target);
+ if (except(target))
+ (void) write(rem, "N\n", 2);
+ else if (lstat(target, &stb) < 0)
+ (void) write(rem, "Y\n", 2);
+ else
+ (void) write(rem, "N\n", 2);
+ break;
+
+ case '\0':
+ *--cp = '\0';
+ if (*s != '\0')
+ log(lfp, "%s\n", s);
+ break;
+
+ case 'E':
+ *tp = '\0';
+ ack();
+ return;
+
+ case '\1':
+ case '\2':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ if (buf[0] == '\2')
+ lostconn(0);
+ break;
+
+ default:
+ error("rmchk: unexpected response '%s'\n", buf);
+ err();
+ }
+ }
+}
+
+/*
+ * Check the current directory (initialized by the 'T' command to server())
+ * for extraneous files and remove them.
+ */
+static void
+clean(cp)
+ register char *cp;
+{
+ DIR *d;
+ register struct direct *dp;
+ struct stat stb;
+ char *otp;
+ int len, opts;
+
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp != '\0') {
+ error("clean: options not delimited\n");
+ return;
+ }
+ if ((d = opendir(target)) == NULL) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ return;
+ }
+ ack();
+
+ 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/%s: Name too long\n",
+ host, target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ if (lstat(target, &stb) < 0) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ continue;
+ }
+ (void) sprintf(buf, "Q%s\n", dp->d_name);
+ (void) write(rem, buf, strlen(buf));
+ cp = buf;
+ do {
+ if (read(rem, cp, 1) != 1)
+ cleanup(0);
+ } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+ *--cp = '\0';
+ cp = buf;
+ if (*cp != 'Y')
+ continue;
+ if (opts & VERIFY) {
+ cp = buf;
+ *cp++ = '\0';
+ (void) sprintf(cp, "need to remove: %s\n", target);
+ (void) write(rem, buf, strlen(cp) + 1);
+ } else
+ removeit(&stb);
+ }
+ closedir(d);
+ (void) write(rem, "E\n", 2);
+ (void) response();
+ tp = otp;
+ *tp = '\0';
+}
+
+/*
+ * Remove a file or directory (recursively) and send back an acknowledge
+ * or an error message.
+ */
+static void
+removeit(stp)
+ struct stat *stp;
+{
+ DIR *d;
+ struct direct *dp;
+ register char *cp;
+ struct stat stb;
+ char *otp;
+ int len;
+
+ switch (stp->st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFLNK:
+ if (unlink(target) < 0)
+ goto bad;
+ goto removed;
+
+ case S_IFDIR:
+ break;
+
+ default:
+ error("%s:%s: not a plain file\n", host, target);
+ return;
+ }
+
+ if ((d = opendir(target)) == NULL)
+ goto bad;
+
+ 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/%s: Name too long\n",
+ host, target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ if (lstat(target, &stb) < 0) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ continue;
+ }
+ removeit(&stb);
+ }
+ closedir(d);
+ tp = otp;
+ *tp = '\0';
+ if (rmdir(target) < 0) {
+bad:
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ return;
+ }
+removed:
+ cp = buf;
+ *cp++ = '\0';
+ (void) sprintf(cp, "removed %s\n", target);
+ (void) write(rem, buf, strlen(cp) + 1);
+}
+
+/*
+ * Execute a shell command to handle special cases.
+ */
+static void
+dospecial(cmd)
+ char *cmd;
+{
+ int fd[2], status, pid, i;
+ register char *cp, *s;
+ char sbuf[BUFSIZ];
+ extern int userid, groupid;
+
+ if (pipe(fd) < 0) {
+ error("%s\n", strerror(errno));
+ return;
+ }
+ if ((pid = fork()) == 0) {
+ /*
+ * Return everything the shell commands print.
+ */
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open(_PATH_DEVNULL, O_RDONLY);
+ (void) dup(fd[1]);
+ (void) dup(fd[1]);
+ (void) close(fd[0]);
+ (void) close(fd[1]);
+ setgid(groupid);
+ setuid(userid);
+ execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
+ _exit(127);
+ }
+ (void) close(fd[1]);
+ s = sbuf;
+ *s++ = '\0';
+ while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
+ cp = buf;
+ do {
+ *s++ = *cp++;
+ if (cp[-1] != '\n') {
+ if (s < &sbuf[sizeof(sbuf)-1])
+ continue;
+ *s++ = '\n';
+ }
+ /*
+ * Throw away blank lines.
+ */
+ if (s == &sbuf[2]) {
+ s--;
+ continue;
+ }
+ (void) write(rem, sbuf, s - sbuf);
+ s = &sbuf[1];
+ } while (--i);
+ }
+ if (s > &sbuf[1]) {
+ *s++ = '\n';
+ (void) write(rem, sbuf, s - sbuf);
+ }
+ while ((i = wait(&status)) != pid && i != -1)
+ ;
+ if (i == -1)
+ status = -1;
+ (void) close(fd[0]);
+ if (status)
+ error("shell returned %d\n", status);
+ else
+ ack();
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+log(FILE *fp, const char *fmt, ...)
+#else
+log(fp, fmt, va_alist)
+ FILE *fp;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ /* Print changes locally if not quiet mode */
+ if (!qflag)
+ (void)vprintf(fmt, ap);
+
+ /* Save changes (for mailing) if really updating files */
+ if (!(options & VERIFY) && fp != NULL)
+ (void)vfprintf(fp, fmt, ap);
+ va_end(ap);
+}
+
+void
+#if __STDC__
+error(const char *fmt, ...)
+#else
+error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ static FILE *fp;
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ ++nerrs;
+ if (!fp && !(fp = fdopen(rem, "w")))
+ return;
+ if (iamremote) {
+ (void)fprintf(fp, "%crdist: ", 0x01);
+ (void)vfprintf(fp, fmt, ap);
+ fflush(fp);
+ }
+ else {
+ fflush(stdout);
+ (void)fprintf(stderr, "rdist: ");
+ (void)vfprintf(stderr, fmt, ap);
+ fflush(stderr);
+ }
+ if (lfp != NULL) {
+ (void)fprintf(lfp, "rdist: ");
+ (void)vfprintf(lfp, fmt, ap);
+ fflush(lfp);
+ }
+ va_end(ap);
+}
+
+void
+#if __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ static FILE *fp;
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ ++nerrs;
+ if (!fp && !(fp = fdopen(rem, "w")))
+ return;
+ if (iamremote) {
+ (void)fprintf(fp, "%crdist: ", 0x02);
+ (void)vfprintf(fp, fmt, ap);
+ fflush(fp);
+ }
+ else {
+ fflush(stdout);
+ (void)fprintf(stderr, "rdist: ");
+ (void)vfprintf(stderr, fmt, ap);
+ fflush(stderr);
+ }
+ if (lfp != NULL) {
+ (void)fprintf(lfp, "rdist: ");
+ (void)vfprintf(lfp, fmt, ap);
+ fflush(lfp);
+ }
+ cleanup(0);
+}
+
+static int
+response()
+{
+ char *cp, *s;
+ char resp[BUFSIZ];
+
+ if (debug)
+ printf("response()\n");
+
+ cp = s = resp;
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
+
+ switch (*s++) {
+ case '\0':
+ *--cp = '\0';
+ if (*s != '\0') {
+ log(lfp, "%s\n", s);
+ return(1);
+ }
+ return(0);
+ case '\3':
+ *--cp = '\0';
+ log(lfp, "Note: %s\n",s);
+ return(response());
+
+ default:
+ s--;
+ /* fall into... */
+ case '\1':
+ case '\2':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ if (resp[0] == '\2')
+ lostconn(0);
+ return(-1);
+ }
+}
+
+/*
+ * Remove temporary files and do any cleanup operations before exiting.
+ */
+void
+cleanup(signo)
+ int signo;
+{
+ (void) unlink(tempfile);
+ exit(1);
+}
+
+static void
+#if __STDC__
+note(const char *fmt, ...)
+#else
+note(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ static char buf[BUFSIZ];
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ comment(buf);
+}
+
+static void
+comment(s)
+ char *s;
+{
+ char c;
+
+ c = '\3';
+ write(rem, &c, 1);
+ write(rem, s, strlen(s));
+ c = '\n';
+ write(rem, &c, 1);
+}