summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authordm <dm@cvs.openbsd.org>1996-02-03 12:13:04 +0000
committerdm <dm@cvs.openbsd.org>1996-02-03 12:13:04 +0000
commitf34705a30c83ef27acf58b47c75c736e6858247a (patch)
tree2121eb2901c910af7604845f5538c2fc26d01ac3 /usr.bin
parentfdb8a9c5e300099e2c65b848550471fb1d9f0cd4 (diff)
rdist 6.1.1
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/Makefile6
-rw-r--r--usr.bin/oldrdist/Makefile20
-rw-r--r--usr.bin/oldrdist/defs.h181
-rw-r--r--usr.bin/oldrdist/docmd.c630
-rw-r--r--usr.bin/oldrdist/expand.c667
-rw-r--r--usr.bin/oldrdist/gram.y509
-rw-r--r--usr.bin/oldrdist/lookup.c167
-rw-r--r--usr.bin/oldrdist/main.c (renamed from usr.bin/rdist/main.c)2
-rw-r--r--usr.bin/oldrdist/oldrdist.1413
-rw-r--r--usr.bin/oldrdist/pathnames.h39
-rw-r--r--usr.bin/oldrdist/server.c (renamed from usr.bin/rdist/server.c)2
-rw-r--r--usr.bin/rdist/Makefile9
-rw-r--r--usr.bin/rdist/child.c581
-rw-r--r--usr.bin/rdist/client.c1239
-rw-r--r--usr.bin/rdist/common.c995
-rw-r--r--usr.bin/rdist/config-data.h93
-rw-r--r--usr.bin/rdist/config-def.h114
-rw-r--r--usr.bin/rdist/config.h128
-rw-r--r--usr.bin/rdist/defs.h432
-rw-r--r--usr.bin/rdist/distopt.c183
-rw-r--r--usr.bin/rdist/docmd.c1292
-rw-r--r--usr.bin/rdist/expand.c531
-rw-r--r--usr.bin/rdist/gram.y326
-rw-r--r--usr.bin/rdist/isexec.c267
-rw-r--r--usr.bin/rdist/lookup.c39
-rw-r--r--usr.bin/rdist/message.c868
-rw-r--r--usr.bin/rdist/os-openbsd.h157
-rw-r--r--usr.bin/rdist/pathnames.h22
-rw-r--r--usr.bin/rdist/rdist.1959
-rw-r--r--usr.bin/rdist/rdist.c454
-rw-r--r--usr.bin/rdist/rshrcmd.c106
-rw-r--r--usr.bin/rdist/setargs.c161
-rw-r--r--usr.bin/rdist/signal.c89
-rw-r--r--usr.bin/rdist/types.h101
-rw-r--r--usr.bin/rdist/version.h58
-rw-r--r--usr.bin/rdistd/Makefile9
-rw-r--r--usr.bin/rdistd/filesys-os.c402
-rw-r--r--usr.bin/rdistd/filesys.c482
-rw-r--r--usr.bin/rdistd/filesys.h168
-rw-r--r--usr.bin/rdistd/message.c868
-rw-r--r--usr.bin/rdistd/rdistd.183
-rw-r--r--usr.bin/rdistd/rdistd.c113
-rw-r--r--usr.bin/rdistd/server.c1642
43 files changed, 14397 insertions, 1210 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 035fdcd526c..18169cf03e5 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -1,5 +1,5 @@
# from: @(#)Makefile 5.8.1.1 (Berkeley) 5/8/91
-# $Id: Makefile,v 1.5 1996/01/29 00:54:01 deraadt Exp $
+# $Id: Makefile,v 1.6 1996/02/03 12:11:34 dm Exp $
SUBDIR= apply apropos asa at banner basename bdes biff cal calendar cap_mkdb \
checknr chflags chpass cksum cmp col colcrt colrm column comm \
@@ -10,8 +10,8 @@ SUBDIR= apply apropos asa at banner basename bdes biff cal calendar cap_mkdb \
lex locate \
lock logger login logname look lorder m4 machine mail make man mesg \
mkdep mkfifo mkstr modstat msgs netstat newsyslog nfsstat nice \
- nohup pagesize passwd paste patch pr printenv printf quota \
- rdist renice rev rlogin rpcgen rpcinfo rs \
+ nohup oldrdist pagesize passwd paste patch pr printenv printf quota \
+ rdist rdistd renice rev rlogin rpcgen rpcinfo rs \
rsh rup ruptime rusers rwall rwho \
script sed shar showmount skey skeyinit soelim split strings \
su sup systat tail talk tcopy tee telnet tftp time \
diff --git a/usr.bin/oldrdist/Makefile b/usr.bin/oldrdist/Makefile
new file mode 100644
index 00000000000..2d11b217d11
--- /dev/null
+++ b/usr.bin/oldrdist/Makefile
@@ -0,0 +1,20 @@
+# from: @(#)Makefile 5.11 (Berkeley) 3/12/91
+# $Id: Makefile,v 1.1 1996/02/03 12:11:51 dm 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
+MAN= oldrdist.1
+
+LDADD= -lcompat
+DPADD= ${LIBCOMPAT}
+
+realinstall:
+ install ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${PROG} ${DESTDIR}${BINDIR}/oldrdist
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/oldrdist/defs.h b/usr.bin/oldrdist/defs.h
new file mode 100644
index 00000000000..ebfab36f2b6
--- /dev/null
+++ b/usr.bin/oldrdist/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 1996/02/03 12:11:53 dm 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/oldrdist/docmd.c b/usr.bin/oldrdist/docmd.c
new file mode 100644
index 00000000000..5fc534421d7
--- /dev/null
+++ b/usr.bin/oldrdist/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 1996/02/03 12:11:55 dm 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/oldrdist/expand.c b/usr.bin/oldrdist/expand.c
new file mode 100644
index 00000000000..06e021b4859
--- /dev/null
+++ b/usr.bin/oldrdist/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 1996/02/03 12:11:56 dm 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/oldrdist/gram.y b/usr.bin/oldrdist/gram.y
new file mode 100644
index 00000000000..8639687a95c
--- /dev/null
+++ b/usr.bin/oldrdist/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 1996/02/03 12:11:57 dm 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/oldrdist/lookup.c b/usr.bin/oldrdist/lookup.c
new file mode 100644
index 00000000000..11e6c6aefc0
--- /dev/null
+++ b/usr.bin/oldrdist/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 1996/02/03 12:11:59 dm 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/oldrdist/main.c
index 037836000bd..fd487b0b484 100644
--- a/usr.bin/rdist/main.c
+++ b/usr.bin/oldrdist/main.c
@@ -39,7 +39,7 @@ static char copyright[] =
#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 $";
+static char *rcsid = "$Id: main.c,v 1.1 1996/02/03 12:12:00 dm Exp $";
#endif /* not lint */
#include "defs.h"
diff --git a/usr.bin/oldrdist/oldrdist.1 b/usr.bin/oldrdist/oldrdist.1
new file mode 100644
index 00000000000..35ae3f82e54
--- /dev/null
+++ b/usr.bin/oldrdist/oldrdist.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: oldrdist.1,v 1.1 1996/02/03 12:12:01 dm 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/oldrdist/pathnames.h b/usr.bin/oldrdist/pathnames.h
new file mode 100644
index 00000000000..85872f27f0d
--- /dev/null
+++ b/usr.bin/oldrdist/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 1996/02/03 12:12:02 dm Exp $
+ */
+
+#include <paths.h>
+
+#define _PATH_RDIST "rdist"
diff --git a/usr.bin/rdist/server.c b/usr.bin/oldrdist/server.c
index f112164c72e..c3ef0e19de8 100644
--- a/usr.bin/rdist/server.c
+++ b/usr.bin/oldrdist/server.c
@@ -33,7 +33,7 @@
#ifndef lint
/* from: static char sccsid[] = "@(#)server.c 8.1 (Berkeley) 6/9/93"; */
-static char *rcsid = "$Id: server.c,v 1.2 1995/12/15 08:58:45 deraadt Exp $";
+static char *rcsid = "$Id: server.c,v 1.1 1996/02/03 12:12:03 dm Exp $";
#endif /* not lint */
#include <sys/wait.h>
diff --git a/usr.bin/rdist/Makefile b/usr.bin/rdist/Makefile
index f3f5de8532f..f6d186cb4a5 100644
--- a/usr.bin/rdist/Makefile
+++ b/usr.bin/rdist/Makefile
@@ -1,12 +1,11 @@
# from: @(#)Makefile 5.11 (Berkeley) 3/12/91
-# $Id: Makefile,v 1.1 1995/10/18 08:45:58 deraadt Exp $
+# $Id: Makefile,v 1.2 1996/02/03 12:12:07 dm Exp $
PROG= rdist
-CFLAGS+=-I${.CURDIR}
-SRCS= docmd.c expand.c lookup.c main.c server.c
+CFLAGS+=-I. -DOS_H=\"os-openbsd.h\"
+SRCS= child.c client.c common.c distopt.c docmd.c expand.c isexec.c \
+ lookup.c message.c rdist.c rshrcmd.c setargs.c signal.c
OBJS+= gram.o
-BINOWN= root
-BINMODE=4555
CLEANFILES=y.tab.h
LDADD= -lcompat
diff --git a/usr.bin/rdist/child.c b/usr.bin/rdist/child.c
new file mode 100644
index 00000000000..5beac4e730f
--- /dev/null
+++ b/usr.bin/rdist/child.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: child.c,v 1.1 1996/02/03 12:12:09 dm Exp $";
+
+static char sccsid[] = "@(#)docmd.c 5.1 (Berkeley) 6/6/85";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * Functions for rdist related to children
+ */
+
+#include "defs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#if defined(NEED_SYS_SELECT_H)
+#include <sys/select.h>
+#endif /* NEED_SYS_SELECT_H */
+
+typedef enum _PROCSTATE {
+ PSrunning,
+ PSdead
+} PROCSTATE;
+
+/*
+ * Structure for child rdist processes mainted by the parent
+ */
+struct _child {
+ char *c_name; /* Name of child */
+ int c_readfd; /* Read file descriptor */
+ pid_t c_pid; /* Process ID */
+ PROCSTATE c_state; /* Running? */
+ struct _child *c_next; /* Next entry */
+};
+typedef struct _child CHILD;
+
+static CHILD *childlist = NULL; /* List of children */
+int activechildren = 0; /* Number of active children */
+extern int maxchildren; /* Max active children */
+static int needscan = FALSE; /* Need to scan children */
+
+/*
+ * Remove a child that has died (exited)
+ * from the list of active children
+ */
+static void removechild(child)
+ CHILD *child;
+{
+ register CHILD *pc, *prevpc;
+
+ debugmsg(DM_CALL, "removechild(%s, %d, %d) start",
+ child->c_name, child->c_pid, child->c_readfd);
+
+ /*
+ * Find the child in the list
+ */
+ for (pc = childlist, prevpc = NULL; pc != NULL;
+ prevpc = pc, pc = pc->c_next)
+ if (pc == child)
+ break;
+
+ if (pc == NULL)
+ error("RemoveChild called with bad child %s %d %d",
+ child->c_name, child->c_pid, child->c_readfd);
+ else {
+ /*
+ * Remove the child
+ */
+#if defined(POSIX_SIGNALS)
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
+#else /* !POSIX_SIGNALS */
+ int oldmask;
+
+ oldmask = sigblock(sigmask(SIGCHLD));
+#endif /* POSIX_SIGNALS */
+
+ if (prevpc != NULL)
+ prevpc->c_next = pc->c_next;
+ else
+ childlist = pc->c_next;
+
+#if defined(POSIX_SIGNALS)
+ sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)NULL);
+#else
+ sigsetmask(oldmask);
+#endif /* POSIX_SIGNALS */
+
+ (void) free(child->c_name);
+ --activechildren;
+ (void) close(child->c_readfd);
+ (void) free(pc);
+ }
+
+ debugmsg(DM_CALL, "removechild() end");
+}
+
+/*
+ * Create a totally new copy of a child.
+ */
+static CHILD *copychild(child)
+ CHILD *child;
+{
+ register CHILD *newc;
+
+ newc = (CHILD *) xmalloc(sizeof(CHILD));
+
+ newc->c_name = strdup(child->c_name);
+ newc->c_readfd = child->c_readfd;
+ newc->c_pid = child->c_pid;
+ newc->c_state = child->c_state;
+ newc->c_next = NULL;
+
+ return(newc);
+}
+
+/*
+ * Add a child to the list of children.
+ */
+static void addchild(child)
+ CHILD *child;
+{
+ register CHILD *pc;
+
+ debugmsg(DM_CALL, "addchild() start\n");
+
+ pc = copychild(child);
+ pc->c_next = childlist;
+ childlist = pc;
+
+ ++activechildren;
+
+ debugmsg(DM_MISC,
+ "addchild() created '%s' pid %d fd %d (active=%d)\n",
+ child->c_name, child->c_pid, child->c_readfd, activechildren);
+}
+
+/*
+ * Read input from a child process.
+ */
+static void readchild(child)
+ CHILD *child;
+{
+ char rbuf[BUFSIZ];
+ int amt;
+
+ debugmsg(DM_CALL, "[readchild(%s, %d, %d) start]",
+ child->c_name, child->c_pid, child->c_readfd);
+
+ /*
+ * Check that this is a valid child.
+ */
+ if (child->c_name == NULL || child->c_readfd <= 0) {
+ debugmsg(DM_MISC, "[readchild(%s, %d, %d) bad child]",
+ child->c_name, child->c_pid, child->c_readfd);
+ return;
+ }
+
+ /*
+ * Read from child and display the result.
+ */
+ while ((amt = read(child->c_readfd, rbuf, sizeof(rbuf))) > 0) {
+ /* XXX remove these debug calls */
+ debugmsg(DM_MISC, "[readchild(%s, %d, %d) got %d bytes]",
+ child->c_name, child->c_pid, child->c_readfd, amt);
+
+ (void) xwrite(fileno(stdout), rbuf, amt);
+
+ debugmsg(DM_MISC, "[readchild(%s, %d, %d) write done]",
+ child->c_name, child->c_pid, child->c_readfd);
+ }
+
+ debugmsg(DM_MISC, "readchild(%s, %d, %d) done: amt = %d errno = %d\n",
+ child->c_name, child->c_pid, child->c_readfd, amt, errno);
+
+ /*
+ * See if we've reached EOF
+ */
+ if (amt == 0)
+ debugmsg(DM_MISC, "readchild(%s, %d, %d) at EOF\n",
+ child->c_name, child->c_pid, child->c_readfd);
+}
+
+/*
+ * Wait for processes to exit. If "block" is true, then we block
+ * until a process exits. Otherwise, we return right away. If
+ * a process does exit, then the pointer "statval" is set to the
+ * exit status of the exiting process, if statval is not NULL.
+ */
+static int waitproc(statval, block)
+ int *statval;
+ int block;
+{
+ WAIT_ARG_TYPE status;
+ int pid, exitval;
+
+ debugmsg(DM_CALL, "waitproc() %s, active children = %d...\n",
+ (block) ? "blocking" : "nonblocking", activechildren);
+
+#if WAIT_TYPE == WAIT_WAITPID
+ pid = waitpid(-1, &status, (block) ? 0 : WNOHANG);
+#else
+#if WAIT_TYPE == WAIT_WAIT3
+ pid = wait3(&status, (block) ? 0 : WNOHANG, NULL);
+#endif /* WAIT_WAIT3 */
+#endif /* WAIT_WAITPID */
+
+#if defined(WEXITSTATUS)
+ exitval = WEXITSTATUS(status);
+#else
+ exitval = status.w_retcode;
+#endif /* defined(WEXITSTATUS) */
+
+ if (pid > 0 && exitval != 0) {
+ nerrs++;
+ debugmsg(DM_MISC,
+ "Child process %d exited with status %d.\n",
+ pid, exitval);
+ }
+
+ if (statval)
+ *statval = exitval;
+
+ debugmsg(DM_CALL, "waitproc() done (activechildren = %d)\n",
+ activechildren);
+
+ return(pid);
+}
+
+/*
+ * Check to see if any children have exited, and if so, read any unread
+ * input and then remove the child from the list of children.
+ */
+static void reap()
+{
+ register CHILD *pc;
+ int status = 0;
+ pid_t pid;
+
+ debugmsg(DM_CALL, "reap() called\n");
+
+ /*
+ * Reap every child that has exited. Break out of the
+ * loop as soon as we run out of children that have
+ * exited so far.
+ */
+ for ( ; ; ) {
+ /*
+ * Do a non-blocking check for exiting processes
+ */
+ pid = waitproc(&status, FALSE);
+ debugmsg(DM_MISC,
+ "reap() pid = %d status = %d activechildren=%d\n",
+ pid, status, activechildren);
+
+ /*
+ * See if a child really exited
+ */
+ if (pid == 0)
+ break;
+ if (pid < 0) {
+ if (errno != ECHILD)
+ error("Wait failed: %s", SYSERR);
+ break;
+ }
+
+ /*
+ * Find the process (pid) and mark it as dead.
+ */
+ for (pc = childlist; pc; pc = pc->c_next)
+ if (pc->c_pid == pid) {
+ needscan = TRUE;
+ pc->c_state = PSdead;
+ }
+
+ }
+
+ /*
+ * Reset signals
+ */
+ (void) signal(SIGCHLD, reap);
+
+ debugmsg(DM_CALL, "reap() done\n");
+}
+
+/*
+ * Scan the children list to find the child that just exited,
+ * read any unread input, then remove it from the list of active children.
+ */
+static void childscan()
+{
+ register CHILD *pc, *nextpc;
+
+ debugmsg(DM_CALL, "childscan() start");
+
+ for (pc = childlist; pc; pc = nextpc) {
+ nextpc = pc->c_next;
+ if (pc->c_state == PSdead) {
+ readchild(pc);
+ removechild(pc);
+ }
+ }
+
+ needscan = FALSE;
+ debugmsg(DM_CALL, "childscan() end");
+}
+
+/*
+#if defined HAVE_SELECT
+ *
+ * Wait for children to send output for us to read.
+ *
+#else !HAVE_SELECT
+ *
+ * Wait up for children to exit.
+ *
+#endif
+ */
+extern void waitup()
+{
+#if defined(HAVE_SELECT)
+ register int count;
+ register CHILD *pc;
+ fd_set rchildfds;
+
+ debugmsg(DM_CALL, "waitup() start\n");
+
+ if (needscan)
+ childscan();
+
+ if (activechildren <= 0)
+ return;
+
+ /*
+ * Settup which children we want to select() on.
+ */
+ FD_ZERO(&rchildfds);
+ for (pc = childlist; pc; pc = pc->c_next)
+ if (pc->c_readfd > 0) {
+ debugmsg(DM_MISC, "waitup() select on %d (%s)\n",
+ pc->c_readfd, pc->c_name);
+ FD_SET(pc->c_readfd, &rchildfds);
+ }
+
+ /*
+ * Actually call select()
+ */
+ /* XXX remove debugmsg() calls */
+ debugmsg(DM_MISC, "waitup() Call select(), activechildren=%d\n",
+ activechildren);
+
+ count = select(FD_SETSIZE, &rchildfds, (fd_set *) NULL,
+ (fd_set *) NULL, (struct timeval *) NULL);
+
+ debugmsg(DM_MISC, "waitup() select returned %d activechildren = %d\n",
+ count, activechildren);
+
+ /*
+ * select() will return count < 0 and errno == EINTR when
+ * there are no active children left.
+ */
+ if (count < 0) {
+ if (errno != EINTR)
+ error("Select failed reading children input: %s",
+ SYSERR);
+ return;
+ }
+
+ /*
+ * This should never happen.
+ */
+ if (count == 0) {
+ error("Select returned an unexpected count of 0.");
+ return;
+ }
+
+ /*
+ * Go through the list of children and read from each child
+ * which select() detected as ready for reading.
+ */
+ for (pc = childlist; pc && count > 0; pc = pc->c_next) {
+ /*
+ * Make sure child still exists
+ */
+ if (pc->c_name && kill(pc->c_pid, 0) < 0 &&
+ errno == ESRCH) {
+ debugmsg(DM_MISC,
+ "waitup() proc %d (%s) died unexpectedly!",
+ pc->c_pid, pc->c_name);
+ pc->c_state = PSdead;
+ needscan = TRUE;
+ }
+
+ if (pc->c_name == NULL ||
+ !FD_ISSET(pc->c_readfd, &rchildfds))
+ continue;
+
+ readchild(pc);
+ --count;
+ }
+
+#else /* !defined(HAVE_SELECT) */
+
+ /*
+ * The non-select() version of waitproc()
+ */
+ debugmsg(DM_CALL, "waitup() start\n");
+
+ if (waitproc((int *) NULL, TRUE) > 0)
+ --activechildren;
+
+#endif /* defined(HAVE_SELECT) */
+ debugmsg(DM_CALL, "waitup() end\n");
+}
+
+/*
+ * Spawn (create) a new child process for "cmd".
+ */
+extern int spawn(cmd, cmdlist)
+ struct cmd *cmd;
+ struct cmd *cmdlist;
+{
+ pid_t pid;
+ int fildes[2];
+ char *childname = cmd->c_name;
+
+ if (pipe(fildes) < 0) {
+ error("Cannot create pipe for %s: %s", childname, SYSERR);
+ return(-1);
+ }
+
+ pid = fork();
+ if (pid == (pid_t)-1) {
+ error("Cannot spawn child for %s: fork failed: %s",
+ childname, SYSERR);
+ return(-1);
+ } else if (pid > 0) {
+ /*
+ * Parent
+ */
+ static CHILD newchild;
+
+#if defined(FORK_MISSES)
+ /*
+ * XXX Some OS's have a bug where fork does not
+ * always return properly to the parent
+ * when a number of forks are done very quicky.
+ */
+ sleep(2);
+#endif /* FORK_MISSES */
+
+ /* Receive notification when the child exits */
+ (void) signal(SIGCHLD, reap);
+
+ /* Settup the new child */
+ newchild.c_next = NULL;
+ newchild.c_name = childname;
+ newchild.c_readfd = fildes[PIPE_READ];
+ newchild.c_pid = pid;
+ newchild.c_state = PSrunning;
+
+ /* We're not going to write to the child */
+ (void) close(fildes[PIPE_WRITE]);
+
+ /* Set non-blocking I/O */
+ if (setnonblocking(newchild.c_readfd, TRUE) < 0) {
+ error("Set nonblocking I/O failed: %s", SYSERR);
+ return(-1);
+ }
+
+ /* Add new child to child list */
+ addchild(&newchild);
+
+ /* Mark all other entries for this host as assigned */
+ markassigned(cmd, cmdlist);
+
+ debugmsg(DM_CALL,
+ "spawn() Forked child %d for host %s active = %d\n",
+ pid, childname, activechildren);
+ return(pid);
+ } else {
+ /*
+ * Child
+ */
+
+ /* We're not going to read from our parent */
+ (void) close(fildes[PIPE_READ]);
+
+ /* Make stdout and stderr go to PIPE_WRITE (our parent) */
+ if (dup2(fildes[PIPE_WRITE], (int)fileno(stdout)) < 0) {
+ error("Cannot duplicate stdout file descriptor: %s",
+ SYSERR);
+ return(-1);
+ }
+ if (dup2(fildes[PIPE_WRITE], (int)fileno(stderr)) < 0) {
+ error("Cannot duplicate stderr file descriptor: %s",
+ SYSERR);
+ return(-1);
+ }
+
+ return(0);
+ }
+}
+
+
+/*
+ * Enable or disable non-blocking I/O mode.
+ *
+ * Code is from INN by Rich Salz.
+ */
+#if NBIO_TYPE == NBIO_IOCTL
+#include <sys/ioctl.h>
+
+int setnonblocking(fd, flag)
+ int fd;
+ int flag;
+{
+ int state;
+
+ state = flag ? 1 : 0;
+ return(ioctl(fd, FIONBIO, (char *)&state));
+}
+
+#endif /* NBIO_IOCTL */
+
+
+#if NBIO_TYPE == NBIO_FCNTL
+int setnonblocking(fd, flag)
+ int fd;
+ int flag;
+{
+ int mode;
+
+ if ((mode = fcntl(fd, F_GETFL, 0)) < 0)
+ return(-1);
+ if (flag)
+ mode |= FNDELAY;
+ else
+ mode &= ~FNDELAY;
+ return(fcntl(fd, F_SETFL, mode));
+}
+#endif /* NBIO_FCNTL */
diff --git a/usr.bin/rdist/client.c b/usr.bin/rdist/client.c
new file mode 100644
index 00000000000..14bd028a9ec
--- /dev/null
+++ b/usr.bin/rdist/client.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: client.c,v 1.1 1996/02/03 12:12:11 dm Exp $";
+
+static char sccsid[] = "@(#)client.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * Routines used in client mode to communicate with remove server.
+ */
+
+#include "defs.h"
+#include "y.tab.h"
+
+/*
+ * Update status
+ */
+#define US_NOTHING 0 /* No update needed */
+#define US_NOENT 1 /* Entry does not exist */
+#define US_OUTDATE 2 /* Entry is out of date */
+#define US_DOCOMP 3 /* Do a binary comparison */
+#define US_MODE 4 /* Modes of file differ */
+
+struct linkbuf *ihead = NULL; /* list of files with more than one link */
+char buf[BUFSIZ]; /* general purpose buffer */
+u_char respbuff[BUFSIZ]; /* Response buffer */
+char target[BUFSIZ]; /* target/source directory name */
+char source[BUFSIZ]; /* source directory name */
+char *ptarget; /* pointer to end of target name */
+char *Tdest; /* pointer to last T dest*/
+struct namelist *updfilelist = NULL; /* List of updated files */
+
+static int sendit();
+
+/*
+ * return remote file pathname (relative from target)
+ */
+char *remfilename(src, dest, path, rname, destdir)
+ char *src, *dest, *path, *rname;
+ int destdir;
+{
+ extern struct namelist *filelist;
+ register char *lname, *cp;
+ static char buff[BUFSIZ];
+ int srclen, pathlen;
+ char *p;
+
+
+ debugmsg(DM_MISC,
+ "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n",
+ A(src), A(dest), A(path), A(rname), destdir);
+
+ if (!dest) {
+ debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path);
+ return(path);
+ }
+
+ if (!destdir) {
+ debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest);
+ return(dest);
+ }
+
+ buff[0] = CNULL;
+ lname = buff;
+ if (path && *path) {
+ cp = strrchr(path, '/');
+ if (cp == NULL)
+ (void) sprintf(buff, "%s/%s", dest, path);
+ else {
+ srclen = strlen(src);
+ pathlen = strlen(path);
+ if (srclen >= pathlen)
+ cp++; /* xbasename(path) */
+ else {
+ if (filelist && filelist->n_next == NULL)
+ /* path relative to src */
+ cp = path + srclen;
+ else {
+ if ((p = strrchr(src, '/')))
+ cp = path + srclen - strlen(p);
+ else
+ cp = path;
+ }
+ }
+ if ((*cp != '/') && *cp)
+ (void) sprintf(buff, "%s/%s", dest, cp);
+ else
+ (void) sprintf(buff, "%s%s", dest, cp);
+ }
+ } else
+ strcpy(lname, dest);
+
+ debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname);
+
+ return(lname);
+}
+
+/*
+ * 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) == 0)
+ return(1);
+ return(0);
+}
+
+/*
+ * Run any special commands for this file
+ */
+static void runspecial(starget, opts, rname, destdir)
+ char *starget;
+ opt_t opts;
+ char *rname;
+ int destdir;
+{
+ register struct subcmd *sc;
+ extern struct subcmd *subcmds;
+ char *rfile;
+
+ rfile = remfilename(source, Tdest, target, rname, destdir);
+
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != SPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
+ continue;
+ message(MT_CHANGE, "special \"%s\"", sc->sc_name);
+ if (IS_ON(opts, DO_VERIFY))
+ continue;
+ (void) sendcmd(C_SPECIAL,
+ "%s=%s;%s=%s;%s=%s;export %s %s %s;%s",
+ E_LOCFILE, starget,
+ E_REMFILE, rfile,
+ E_BASEFILE, xbasename(rfile),
+ E_LOCFILE, E_REMFILE, E_BASEFILE,
+ sc->sc_name);
+ while (response() > 0)
+ ;
+ }
+}
+
+/*
+ * If we're doing a target with a "cmdspecial" in it, then
+ * save the name of the file being updated for use with "cmdspecial".
+ */
+static void addcmdspecialfile(starget, rname, destdir)
+ char *starget;
+ char *rname;
+ int destdir;
+{
+ char *rfile;
+ struct namelist *new;
+ register struct subcmd *sc;
+ extern struct subcmd *subcmds;
+ int isokay = 0;
+
+ rfile = remfilename(source, Tdest, target, rname, destdir);
+
+ for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) {
+ if (sc->sc_type != CMDSPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
+ continue;
+ isokay = TRUE;
+ }
+
+ if (isokay) {
+ new = (struct namelist *) xmalloc(sizeof(struct namelist));
+ new->n_name = strdup(rfile);
+ new->n_next = updfilelist;
+ updfilelist = new;
+ }
+}
+
+/*
+ * Free the file list
+ */
+static void freecmdspecialfiles()
+{
+ register struct namelist *ptr, *save;
+
+ for (ptr = updfilelist; ptr; ) {
+ if (ptr->n_name) (void) free(ptr->n_name);
+ save = ptr->n_next;
+ (void) free(ptr);
+ if (save)
+ ptr = save->n_next;
+ else
+ ptr = NULL;
+ }
+ updfilelist = NULL;
+}
+
+/*
+ * Run commands for an entire cmd
+ */
+extern void runcmdspecial(cmd, filev, opts)
+ struct cmd *cmd;
+ char **filev;
+ opt_t opts;
+{
+ register struct subcmd *sc;
+ register struct namelist *f;
+ register char **cpp;
+ char *file;
+ int first = TRUE;
+
+ for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != CMDSPECIAL)
+ continue;
+ message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name);
+ if (IS_ON(opts, DO_VERIFY))
+ continue;
+ /* Send all the file names */
+ for (f = updfilelist; f != NULL; f = f->n_next) {
+ if (first) {
+ (void) sendcmd(C_CMDSPECIAL, NULL);
+ if (response() < 0)
+ return;
+ first = FALSE;
+ }
+ (void) sendcmd(RC_FILE, f->n_name);
+ if (response() < 0)
+ return;
+ }
+ if (first) {
+ (void) sendcmd(C_CMDSPECIAL, NULL);
+ if (response() < 0)
+ return;
+ first = FALSE;
+ }
+ /* Send command to run and wait for it to complete */
+ (void) sendcmd(RC_COMMAND, sc->sc_name);
+ while (response() > 0)
+ ;
+ first = TRUE; /* Reset in case there are more CMDSPECIAL's */
+ }
+ freecmdspecialfiles();
+}
+
+/*
+ * For security, reject filenames that contains a newline
+ */
+int checkfilename(name)
+ char *name;
+{
+ register char *cp;
+
+ if (strchr(name, '\n')) {
+ for (cp = name; *cp; cp++)
+ if (*cp == '\n')
+ *cp = '?';
+ message(MT_NERROR,
+ "Refuse to handle filename containing newline: %s",
+ name);
+ return(-1);
+ }
+
+ return(0);
+}
+
+/*
+ * Save and retrieve hard link info
+ */
+static struct linkbuf *linkinfo(statp)
+ struct stat *statp;
+{
+ struct linkbuf *lp;
+
+ for (lp = ihead; lp != NULL; lp = lp->nextp)
+ if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) {
+ lp->count--;
+ return(lp);
+ }
+
+ lp = (struct linkbuf *) xmalloc(sizeof(*lp));
+ lp->nextp = ihead;
+ ihead = lp;
+ lp->inum = statp->st_ino;
+ lp->devnum = statp->st_dev;
+ lp->count = statp->st_nlink - 1;
+ (void) strcpy(lp->pathname, target);
+ (void) strcpy(lp->src, source);
+ if (Tdest)
+ (void) strcpy(lp->target, Tdest);
+ else
+ *lp->target = CNULL;
+
+ return((struct linkbuf *) NULL);
+}
+
+/*
+ * Send a hardlink
+ */
+static int sendhardlink(opts, lp, rname, destdir)
+ opt_t opts;
+ struct linkbuf *lp;
+ char *rname;
+ int destdir;
+{
+ static char buff[MAXPATHLEN];
+ char *lname; /* name of file to link to */
+
+ debugmsg(DM_MISC,
+ "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
+ rname, lp->pathname, lp->src, lp->target);
+
+ if (*lp->target == CNULL)
+ (void) sendcmd(C_RECVHARDLINK, "%o %s %s",
+ opts, lp->pathname, rname);
+ else {
+ lname = buff;
+ strcpy(lname, remfilename(lp->src, lp->target,
+ lp->pathname, rname,
+ destdir));
+ debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname);
+ (void) sendcmd(C_RECVHARDLINK, "%o %s %s",
+ opts, lname, rname);
+ }
+
+ return(response());
+}
+
+/*
+ * Send a file
+ */
+static int sendfile(rname, opts, stb, user, group, destdir)
+ char *rname;
+ opt_t opts;
+ struct stat *stb;
+ char *user, *group;
+ int destdir;
+{
+ int goterr, f;
+ off_t i;
+
+ if (stb->st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = linkinfo(stb)) != NULL)
+ return(sendhardlink(opts, lp, rname, destdir));
+ }
+
+ if ((f = open(target, O_RDONLY)) < 0) {
+ error("%s: open for read failed: %s", target, SYSERR);
+ return(-1);
+ }
+
+ /*
+ * Send file info
+ */
+ (void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s",
+ opts, stb->st_mode & 07777,
+ (long) stb->st_size,
+ stb->st_mtime, stb->st_atime,
+ user, group, rname);
+ if (response() < 0) {
+ (void) close(f);
+ return(-1);
+ }
+
+ debugmsg(DM_MISC, "Send file '%s' %d bytes\n",
+ rname, (long) stb->st_size);
+
+ /*
+ * Set remote time out alarm handler.
+ */
+ (void) signal(SIGALRM, sighandler);
+
+ /*
+ * Actually transfer the file
+ */
+ goterr = 0;
+ for (i = 0; i < stb->st_size; i += BUFSIZ) {
+ int amt = BUFSIZ;
+
+ (void) alarm(rtimeout);
+ if (i + amt > stb->st_size)
+ amt = stb->st_size - i;
+ if (read(f, buf, amt) != amt) {
+ error("%s: File changed size", target);
+ err();
+ ++goterr;
+ /*
+ * XXX - We have to keep going because the
+ * server expects to receive a fixed number
+ * of bytes that we specified as the file size.
+ * We need Out Of Band communication to handle
+ * this situation gracefully.
+ */
+ }
+ if (xwrite(rem_w, buf, amt) < 0) {
+ error("%s: Error writing to client: %s",
+ target, SYSERR);
+ err();
+ ++goterr;
+ break;
+ }
+ (void) alarm(0);
+ }
+
+ (void) alarm(0); /* Insure alarm is off */
+ (void) close(f);
+
+ debugmsg(DM_MISC, "Send file '%s' %s.\n",
+ (goterr) ? "failed" : "complete", rname);
+
+ /*
+ * Check for errors and end send
+ */
+ if (goterr)
+ return(-1);
+ else {
+ ack();
+ f = response();
+ if (f < 0)
+ return(-1);
+ else if (f == 0 && IS_ON(opts, DO_COMPARE))
+ return(0);
+
+ runspecial(target, opts, rname, destdir);
+ addcmdspecialfile(target, rname, destdir);
+
+ return(0);
+ }
+}
+
+/*
+ * Check for files on the machine being updated that are not on the master
+ * machine and remove them.
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing happened.
+ * Return > 0 if anything is updated.
+ */
+static int rmchk(opts)
+ opt_t opts;
+{
+ register u_char *s;
+ struct stat stb;
+ int didupdate = 0;
+ int n;
+
+ debugmsg(DM_CALL, "rmchk()\n");
+
+ /*
+ * Tell the remote to clean the files from the last directory sent.
+ */
+ (void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY));
+ if (response() < 0)
+ return(-1);
+
+ for ( ; ; ) {
+ n = remline(s = respbuff, sizeof(respbuff), TRUE);
+ if (n <= 0) {
+ error("rmchk: unexpected control record");
+ return(didupdate);
+ }
+
+ switch (*s++) {
+ case CC_QUERY: /* Query if file should be removed */
+ /*
+ * Return the following codes to remove query.
+ * CC_NO -- file exists - DON'T remove.
+ * CC_YES -- file doesn't exist - REMOVE.
+ */
+ (void) sprintf(ptarget, "%s%s",
+ (ptarget[-1] == '/' ? "" : "/"), s);
+ debugmsg(DM_MISC, "check %s\n", target);
+ if (except(target))
+ (void) sendcmd(CC_NO, NULL);
+ else if (lstat(target, &stb) < 0) {
+ if (sendcmd(CC_YES, NULL) == 0)
+ didupdate = 1;
+ } else
+ (void) sendcmd(CC_NO, NULL);
+ break;
+
+ case CC_END:
+ *ptarget = CNULL;
+ ack();
+ return(didupdate);
+
+ case C_LOGMSG:
+ if (n > 0)
+ message(MT_INFO, "%s", s);
+ break;
+
+ case C_ERRMSG:
+ message(MT_NERROR, "%s", s);
+ return(didupdate);
+
+ case C_FERRMSG:
+ message(MT_FERROR, "%s", s);
+ finish();
+
+ default:
+ error("rmchk: unexpected response '%s'", respbuff);
+ err();
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Send a directory
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing happened.
+ * Return > 0 if anything is updated.
+ */
+static int senddir(rname, opts, stb, user, group, destdir)
+ char *rname;
+ opt_t opts;
+ struct stat *stb;
+ char *user, *group;
+ int destdir;
+{
+ DIRENTRY *dp;
+ DIR *d;
+ char *optarget, *cp;
+ int len;
+ int didupdate = 0;
+
+ /*
+ * Send recvdir command in recvit() format.
+ */
+ (void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s",
+ opts, stb->st_mode & 07777, user, group, rname);
+ if (response() < 0)
+ return(-1);
+
+ /*
+ * Don't descend into directory
+ */
+ if (IS_ON(opts, DO_NODESCEND))
+ return(0);
+
+ if (IS_ON(opts, DO_REMOVE))
+ if (rmchk(opts) > 0)
+ ++didupdate;
+
+ if ((d = opendir(target)) == NULL) {
+ error("%s: opendir failed: %s", target, SYSERR);
+ return(-1);
+ }
+
+ optarget = ptarget;
+ len = ptarget - target;
+ while (dp = readdir(d)) {
+ if (!strcmp(dp->d_name, ".") ||
+ !strcmp(dp->d_name, ".."))
+ continue;
+ if (len + 1 + (int) strlen(dp->d_name) >= MAXPATHLEN - 1) {
+ error("%s/%s: Name too long", target,
+ dp->d_name);
+ continue;
+ }
+ ptarget = optarget;
+ if (ptarget[-1] != '/')
+ *ptarget++ = '/';
+ cp = dp->d_name;
+ while (*ptarget++ = *cp++)
+ ;
+ ptarget--;
+ if (sendit(dp->d_name, opts, destdir) > 0)
+ didupdate = 1;
+ }
+ (void) closedir(d);
+
+ (void) sendcmd(C_END, NULL);
+ (void) response();
+
+ ptarget = optarget;
+ *ptarget = CNULL;
+
+ return(didupdate);
+}
+
+/*
+ * Send a link
+ */
+static int sendlink(rname, opts, stb, user, group, destdir)
+ char *rname;
+ opt_t opts;
+ struct stat *stb;
+ char *user;
+ char *group;
+ int destdir;
+{
+ int sizerr, f, n;
+ static char tbuf[BUFSIZ];
+ char lbuf[MAXPATHLEN];
+ u_char *s;
+
+ debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir);
+
+ if (stb->st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = linkinfo(stb)) != NULL)
+ return(sendhardlink(opts, lp, rname, destdir));
+ }
+
+ /*
+ * Gather and send basic link info
+ */
+ (void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s",
+ opts, stb->st_mode & 07777,
+ (long) stb->st_size,
+ stb->st_mtime, stb->st_atime,
+ user, group, rname);
+ if (response() < 0)
+ return(-1);
+
+ /*
+ * Gather and send additional link info
+ */
+ sizerr = (readlink(target, lbuf, sizeof(lbuf)) != stb->st_size);
+ (void) sprintf(tbuf, "%.*s", (int) stb->st_size, lbuf);
+ (void) sendcmd(C_NONE, "%s\n", tbuf);
+
+ if (sizerr) {
+ error("%s: file changed size", target);
+ err();
+ } else
+ ack();
+
+ /*
+ * Check response
+ */
+ f = response();
+ if (f < 0)
+ return(-1);
+ else if (f == 0 && IS_ON(opts, DO_COMPARE))
+ return(0);
+
+ /*
+ * Read and process responses from server.
+ * The server may send multiple messages regarding
+ * file deletes if the remote target is a directory.
+ */
+ for (;;) {
+ n = remline(s = respbuff, sizeof(respbuff), TRUE);
+ if (n == -1) /* normal EOF */
+ return(0);
+ if (n == 0) {
+ error("expected control record");
+ continue;
+ }
+
+ switch (*s++) {
+ case C_END: /* End of send operation */
+ *ptarget = CNULL;
+ ack();
+ runspecial(target, opts, rname, destdir);
+ addcmdspecialfile(target, rname, destdir);
+ return(0);
+
+ case C_LOGMSG:
+ if (n > 0)
+ message(MT_INFO, "%s", s);
+ break;
+
+ case C_ERRMSG:
+ message(MT_NERROR, "%s", s);
+ return(-1);
+
+ case C_FERRMSG:
+ message(MT_FERROR, "%s", s);
+ finish();
+
+ default:
+ error("install link: unexpected response '%s'",
+ respbuff);
+ err();
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Check to see if file needs to be updated on the remote machine.
+ * Returns:
+ * US_NOTHING - no update
+ * US_NOENT - remote doesn't exist
+ * US_OUTDATE - out of date
+ * US_DOCOMP - comparing binaries to determine if out of date
+ * US_MODE - File modes do not match
+ */
+static int update(rname, opts, statp)
+ char *rname;
+ opt_t opts;
+ struct stat *statp;
+{
+ register off_t size;
+ register time_t mtime;
+ unsigned short lmode;
+ unsigned short rmode;
+ char *owner = NULL, *group = NULL;
+ int done, n;
+ u_char *cp;
+
+ debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp);
+
+ if (IS_ON(opts, DO_NOEXEC))
+ if (isexec(target, statp)) {
+ debugmsg(DM_MISC, "%s is an executable\n", target);
+ return(US_NOTHING);
+ }
+
+ /*
+ * Check to see if the file exists on the remote machine.
+ */
+ (void) sendcmd(C_QUERY, "%s", rname);
+
+ for (done = 0; !done;) {
+ n = remline(cp = respbuff, sizeof(respbuff), TRUE);
+ if (n <= 0) {
+ error("update: unexpected control record in response to query");
+ return(US_NOTHING);
+ }
+
+ switch (*cp++) {
+ case QC_ONNFS: /* Resides on a NFS */
+ debugmsg(DM_MISC,
+ "update: %s is on a NFS. Skipping...\n",
+ rname);
+ return(US_NOTHING);
+
+ case QC_SYM: /* Is a symbolic link */
+ debugmsg(DM_MISC,
+ "update: %s is a symlink. Skipping...\n",
+ rname);
+ return(US_NOTHING);
+
+ case QC_ONRO: /* Resides on a Read-Only fs */
+ debugmsg(DM_MISC,
+ "update: %s is on a RO fs. Skipping...\n",
+ rname);
+ return(US_NOTHING);
+
+ case QC_YES:
+ done = 1;
+ break;
+
+ case QC_NO: /* file doesn't exist so install it */
+ return(US_NOENT);
+
+ case C_ERRMSG:
+ if (cp)
+ message(MT_NERROR, "%s", cp);
+ return(US_NOTHING);
+
+ case C_FERRMSG:
+ if (cp)
+ message(MT_FERROR, "%s", cp);
+ finish();
+
+ case C_NOTEMSG:
+ if (cp)
+ message(MT_NOTICE, "%s", cp);
+ break;
+ /* Goto top of loop */
+
+ default:
+ error("update: unexpected response to query '%s'", cp);
+ return(US_NOTHING);
+ }
+ }
+
+ /*
+ * Target exists, but no other info passed
+ */
+ if (n <= 1 || !S_ISREG(statp->st_mode))
+ return(US_OUTDATE);
+
+ if (IS_ON(opts, DO_COMPARE))
+ return(US_DOCOMP);
+
+ /*
+ * Parse size
+ */
+ size = strtol(cp, &cp, 10);
+ if (*cp++ != ' ') {
+ error("update: size not delimited");
+ return(US_NOTHING);
+ }
+
+ /*
+ * Parse mtime
+ */
+ mtime = strtol(cp, &cp, 10);
+ if (*cp++ != ' ') {
+ error("update: mtime not delimited");
+ return(US_NOTHING);
+ }
+
+ /*
+ * Parse remote file mode
+ */
+ rmode = strtol(cp, &cp, 8);
+ if (cp && *cp)
+ ++cp;
+
+ /*
+ * Be backwards compatible
+ */
+ if (cp && *cp != CNULL) {
+ /*
+ * Parse remote file owner
+ */
+ owner = strtok((char *)cp, " ");
+ if (owner == NULL) {
+ error("update: owner not delimited");
+ return(US_NOTHING);
+ }
+
+ /*
+ * Parse remote file group
+ */
+ group = strtok((char *) NULL, " ");
+ if (group == NULL) {
+ error("update: group not delimited");
+ return(US_NOTHING);
+ }
+ }
+
+ /*
+ * File needs to be updated?
+ */
+ lmode = statp->st_mode & 07777;
+
+ debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n",
+ rname, lmode, rmode);
+ debugmsg(DM_MISC, "update(%s,) size %d mtime %d owner '%s' grp '%s'\n",
+ rname, (int) size, mtime, owner, group);
+
+ if (statp->st_mtime != mtime) {
+ if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
+ message(MT_WARNING,
+ "%s: Warning: remote copy is newer",
+ target);
+ return(US_NOTHING);
+ }
+ return(US_OUTDATE);
+ }
+
+ /*
+ * If the mode of a file does not match the local mode, the
+ * whole file is updated. This is done both to insure that
+ * a bogus version of the file has not been installed and to
+ * avoid having to handle weird cases of chmod'ing symlinks
+ * and such.
+ */
+ if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
+ debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n",
+ lmode, rmode);
+ return(US_OUTDATE);
+ }
+
+ if (statp->st_size != size) {
+ debugmsg(DM_MISC, "size does not match (%d != %d).\n",
+ (int) statp->st_size, size);
+ return(US_OUTDATE);
+ }
+
+ /*
+ * Check ownership
+ */
+ if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
+ if (!IS_ON(opts, DO_NUMCHKOWNER)) {
+ /* Check by string compare */
+ if (strcmp(owner, getusername(statp->st_uid,
+ target, opts)) != 0) {
+ debugmsg(DM_MISC,
+ "owner does not match (%s != %s).\n",
+ getusername(statp->st_uid,
+ target, opts), owner);
+ return(US_OUTDATE);
+ }
+ } else {
+ /*
+ * Check numerically.
+ * Allow negative numbers.
+ */
+ while (*owner && !isdigit(*owner) && (*owner != '-'))
+ ++owner;
+ if (owner && atoi(owner) != statp->st_uid) {
+ debugmsg(DM_MISC,
+ "owner does not match (%d != %s).\n",
+ statp->st_uid, owner);
+ return(US_OUTDATE);
+ }
+ }
+ }
+
+ if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
+ if (!IS_ON(opts, DO_NUMCHKGROUP)) {
+ /* Check by string compare */
+ if (strcmp(group, getgroupname(statp->st_gid,
+ target, opts)) != 0) {
+ debugmsg(DM_MISC,
+ "group does not match (%s != %s).\n",
+ getgroupname(statp->st_gid,
+ target, opts), group);
+ return(US_OUTDATE);
+ }
+ } else {
+ /* Check numerically */
+ /* Allow negative gid */
+ while (*group && !isdigit(*group) && (*group != '-'))
+ ++group;
+ if (group && atoi(group) != statp->st_gid) {
+ debugmsg(DM_MISC,
+ "group does not match (%d != %s).\n",
+ statp->st_gid, group);
+ return(US_OUTDATE);
+ }
+ }
+ }
+
+ return(US_NOTHING);
+}
+
+/*
+ * Stat a file
+ */
+static int dostat(file, statbuf, opts)
+ char *file;
+ struct stat *statbuf;
+ opt_t opts;
+{
+ int s;
+
+ if (IS_ON(opts, DO_FOLLOW))
+ s = stat(file, statbuf);
+ else
+ s = lstat(file, statbuf);
+
+ if (s < 0)
+ error("%s: %s failed: %s", file,
+ IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
+ return(s);
+}
+
+/*
+ * Transfer the file or directory in target[].
+ * rname is the name of the file on the remote host.
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing happened.
+ * Return > 0 if anything is updated.
+ */
+static int sendit(rname, opts, destdir)
+ char *rname;
+ opt_t opts;
+ int destdir;
+{
+ static struct stat stb;
+ extern struct subcmd *subcmds;
+ char *user, *group;
+ int u, len;
+ int didupdate = 0;
+
+ /*
+ * Remove possible accidental newline
+ */
+ len = strlen(rname);
+ if (len > 0 && rname[len-1] == '\n')
+ rname[len-1] = CNULL;
+
+ if (checkfilename(rname) != 0)
+ return(-1);
+
+ debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts);
+
+ if (except(target))
+ return(0);
+
+ if (dostat(target, &stb, opts) < 0)
+ return(-1);
+
+ /*
+ * Does rname need updating?
+ */
+ u = update(rname, opts, &stb);
+ debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n",
+ rname, opts, target, u);
+
+ /*
+ * Don't need to update the file, but we may need to save hardlink
+ * info.
+ */
+ if (u == US_NOTHING) {
+ if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
+ (void) linkinfo(&stb);
+ return(0);
+ }
+
+ /*
+ * File mode needs changing
+ */
+ if (u == US_MODE) {
+ if (IS_ON(opts, DO_VERIFY)) {
+ message(MT_INFO, "%s: need to chmod to %04o",
+ target, stb.st_mode & 07777);
+ runspecial(target, opts, rname, destdir);
+ return(1);
+ }
+ message(MT_CHANGE, "%s: chmod to %04o",
+ target, stb.st_mode & 07777);
+ (void) sendcmd(C_CHMOD, "%o %04o %s",
+ opts, stb.st_mode & 07777, rname);
+ (void) response();
+ return(1);
+ }
+
+ user = getusername(stb.st_uid, target, opts);
+ group = getgroupname(stb.st_gid, target, opts);
+
+ /*
+ * No entry - need to install
+ */
+ if (u == US_NOENT) {
+ if (IS_ON(opts, DO_VERIFY)) {
+ message(MT_INFO, "%s: need to install", target);
+ runspecial(target, opts, rname, destdir);
+ return(1);
+ }
+ if (!IS_ON(opts, DO_QUIET))
+ message(MT_CHANGE, "%s: installing", target);
+ FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
+ }
+
+ /*
+ * Handle special file types, including directories and symlinks
+ */
+ if (S_ISDIR(stb.st_mode)) {
+ if (senddir(rname, opts, &stb, user, group, destdir) > 0)
+ didupdate = 1;
+ } else if (S_ISLNK(stb.st_mode)) {
+ if (u != US_NOENT)
+ FLAG_ON(opts, DO_COMPARE);
+ /*
+ * Since we always send link info to the server
+ * so the server can determine if the remote link
+ * is correct, we never get any acknowledge meant
+ * from the server whether the link was really
+ * updated or not.
+ */
+ (void) sendlink(rname, opts, &stb, user, group, destdir);
+ } else if (S_ISREG(stb.st_mode)) {
+ if (u == US_OUTDATE) {
+ if (IS_ON(opts, DO_VERIFY)) {
+ message(MT_INFO, "%s: need to update", target);
+ runspecial(target, opts, rname, destdir);
+ return(1);
+ }
+ if (!IS_ON(opts, DO_QUIET))
+ message(MT_CHANGE, "%s: updating", target);
+ }
+ if (sendfile(rname, opts, &stb, user, group, destdir) == 0)
+ didupdate = 1;
+ } else
+ error("%s: unknown file type", target);
+
+ return(didupdate);
+}
+
+/*
+ * Remove temporary files and do any cleanup operations before exiting.
+ */
+extern void cleanup()
+{
+ char *file;
+#ifdef USE_STATDB
+ extern char statfile[];
+
+ (void) unlink(statfile);
+#endif
+
+ if (file = getnotifyfile())
+ (void) unlink(file);
+}
+
+/*
+ * 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).
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing updated.
+ * Return > 0 if something was updated.
+ */
+extern int install(src, dest, ddir, destdir, opts)
+ char *src, *dest;
+ int ddir, destdir;
+ opt_t opts;
+{
+ static char destcopy[MAXPATHLEN];
+ char *rname;
+ int didupdate = 0;
+
+ debugmsg(DM_CALL,
+ "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n",
+ (src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
+ /*
+ * Save source name
+ */
+ if (IS_ON(opts, DO_WHOLE))
+ source[0] = CNULL;
+ else
+ (void) strcpy(source, src);
+
+ if (dest == NULL) {
+ FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
+ dest = src;
+ }
+
+ if (checkfilename(dest) != 0)
+ return(-1);
+
+ if (nflag || debug) {
+ static char buff[BUFSIZ];
+ char *cp;
+
+ cp = getondistoptlist(opts);
+ (void) sprintf(buff, "%s%s%s %s %s",
+ IS_ON(opts, DO_VERIFY) ? "verify" : "install",
+ (cp) ? " -o" : "", (cp) ? cp : "",
+ src, dest);
+ if (nflag) {
+ printf("%s\n", buff);
+ return(0);
+ } else
+ debugmsg(DM_MISC, "%s\n", buff);
+ }
+
+ rname = exptilde(target, src);
+ if (rname == NULL)
+ return(-1);
+ ptarget = target;
+ while (*ptarget)
+ ptarget++;
+ /*
+ * 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 (IS_ON(opts, DO_WHOLE)) {
+ while (*rname == '/')
+ rname++;
+ ddir = 1;
+ destdir = 1;
+ } else {
+ rname = strrchr(target, '/');
+ /* Check if no '/' or target ends in '/' */
+ if (rname == NULL ||
+ rname+1 == NULL ||
+ *(rname+1) == CNULL)
+ rname = target;
+ else
+ rname++;
+ }
+
+ debugmsg(DM_MISC,
+ "install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n",
+ target, source, rname, dest, destdir, ddir);
+
+ /*
+ * Pass the destination file/directory name to remote.
+ */
+ if (ddir)
+ (void) sendcmd(C_DIRTARGET, "%o %s", opts, dest);
+ else
+ (void) sendcmd(C_TARGET, "%o %s", opts, dest);
+ if (response() < 0)
+ return(-1);
+
+ /*
+ * Save the name of the remote target destination if we are
+ * in WHOLE mode (destdir > 0) or if the source and destination
+ * are not the same. This info will be used later for maintaining
+ * hardlink info.
+ */
+ if (destdir || (src && dest && strcmp(src, dest))) {
+ (void) strcpy(destcopy, dest);
+ Tdest = destcopy;
+ }
+
+ didupdate = sendit(rname, opts, destdir);
+ Tdest = 0;
+
+ return(didupdate);
+}
diff --git a/usr.bin/rdist/common.c b/usr.bin/rdist/common.c
new file mode 100644
index 00000000000..80c9b119f62
--- /dev/null
+++ b/usr.bin/rdist/common.c
@@ -0,0 +1,995 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: common.c,v 1.1 1996/02/03 12:12:12 dm Exp $";
+
+static char sccsid[] = "@(#)common.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* !lint */
+
+/*
+ * Things common to both the client and server.
+ */
+
+#include "defs.h"
+#if defined(NEED_UTIME_H)
+#include <utime.h>
+#endif /* defined(NEED_UTIME_H) */
+
+/*
+ * Variables common to both client and server
+ */
+char host[MAXHOSTNAMELEN]; /* Name of this host */
+UID_T userid = (UID_T)-1; /* User's UID */
+GID_T groupid = (GID_T)-1; /* User's GID */
+char *homedir = NULL; /* User's $HOME */
+char *locuser = NULL; /* Local User's name */
+int isserver = FALSE; /* We're the server */
+int amchild = 0; /* This PID is a child */
+int do_fork = 1; /* Fork child process */
+char *currenthost = NULL; /* Current client hostname */
+char *progname = NULL; /* Name of this program */
+int rem_r = -1; /* Client file descriptor */
+int rem_w = -1; /* Client file descriptor */
+struct passwd *pw = NULL; /* Local user's pwd entry */
+int contimedout = FALSE; /* Connection timed out */
+int proto_version = -1; /* Protocol version */
+int rtimeout = RTIMEOUT; /* Response time out */
+jmp_buf finish_jmpbuf; /* Finish() jmp buffer */
+char **realargv; /* Real main() argv */
+int realargc; /* Real main() argc */
+opt_t options = 0; /* Global install options */
+
+/*
+ * Front end to write() that handles partial write() requests.
+ */
+extern WRITE_RETURN_T xwrite(fd, buf, len)
+ int fd;
+ void *buf;
+ WRITE_AMT_T len;
+{
+ WRITE_AMT_T nleft = len;
+ WRITE_RETURN_T nwritten;
+ register char *ptr = buf;
+
+ while (nleft > 0) {
+ if ((nwritten = write(fd, ptr, nleft)) <= 0) {
+ return nwritten;
+ }
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+
+ return len;
+}
+
+/*
+ * Set program name
+ */
+extern void setprogname(argv)
+ char **argv;
+{
+ register char *cp;
+
+ if (!progname) {
+ progname = strdup(argv[0]);
+ if (cp = strrchr(progname, '/'))
+ progname = cp + 1;
+ }
+}
+
+/*
+ * Do run-time initialization
+ */
+extern int init(argc, argv, envp)
+ /*ARGSUSED*/
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register int i;
+ register char *cp;
+
+ if (!isserver)
+ (void) signal(SIGSEGV, sighandler);
+
+ setprogname(argv);
+
+ /*
+ * Save a copy of our argc and argv before setargs() overwrites them
+ */
+ realargc = argc;
+ realargv = (char **) xmalloc(sizeof(char *) * (argc+1));
+ for (i = 0; i < argc; i++)
+ realargv[i] = strdup(argv[i]);
+
+#if defined(SETARGS)
+ setargs_settup(argc, argv, envp);
+#endif /* SETARGS */
+
+ pw = getpwuid(userid = getuid());
+ if (pw == NULL) {
+ error("Your user id (%d) is not known to this system.",
+ getuid());
+ return(-1);
+ }
+
+ debugmsg(DM_MISC, "UserID = %d pwname = '%s' home = '%s'\n",
+ userid, pw->pw_name, pw->pw_dir);
+ homedir = strdup(pw->pw_dir);
+ locuser = strdup(pw->pw_name);
+ groupid = pw->pw_gid;
+ gethostname(host, sizeof(host));
+ if ((cp = strchr(host, '.')) != NULL)
+ *cp = CNULL;
+
+ /*
+ * If we're not root, disable paranoid ownership checks
+ * since normal users cannot chown() files.
+ */
+ if (!isserver && userid != 0) {
+ FLAG_ON(options, DO_NOCHKOWNER);
+ FLAG_ON(options, DO_NOCHKGROUP);
+ }
+
+ return(0);
+}
+
+/*
+ * Finish things up before ending.
+ */
+extern void finish()
+{
+ extern jmp_buf finish_jmpbuf;
+
+ debugmsg(DM_CALL,
+ "finish() called: do_fork = %d amchild = %d isserver = %d",
+ do_fork, amchild, isserver);
+ cleanup();
+
+ /*
+ * There's no valid finish_jmpbuf for the rdist master parent.
+ */
+ if (!do_fork || amchild || isserver) {
+ longjmp(finish_jmpbuf, 1);
+ /*NOTREACHED*/
+ error("Unexpected failure of longjmp() in finish()");
+ exit(2);
+ } else
+ exit(1);
+}
+
+/*
+ * Handle lost connections
+ */
+extern void lostconn()
+{
+ /* Prevent looping */
+ (void) signal(SIGPIPE, SIG_IGN);
+
+ rem_r = rem_w = -1; /* Ensure we don't try to send to server */
+ checkhostname();
+ error("Lost connection to %s",
+ (currenthost) ? currenthost : "(unknown)");
+
+ finish();
+}
+
+/*
+ * Do a core dump
+ */
+extern void coredump()
+{
+ error("Segmentation violation - dumping core [PID = %d, %s]",
+ getpid(),
+ (isserver) ? "isserver" : ((amchild) ? "amchild" : "parent"));
+ abort();
+ /*NOTREACHED*/
+ fatalerr("Abort failed - no core dump. Exiting...");
+}
+
+/*
+ * General signal handler
+ */
+extern void sighandler(sig)
+ int sig;
+{
+ debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
+
+ switch (sig) {
+ case SIGALRM:
+ contimedout = TRUE;
+ checkhostname();
+ error("Response time out");
+ finish();
+ break;
+
+ case SIGPIPE:
+ lostconn();
+ break;
+
+ case SIGFPE:
+ debug = !debug;
+ break;
+
+ case SIGSEGV:
+ coredump();
+ break;
+
+ case SIGHUP:
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ finish();
+ break;
+
+ default:
+ fatalerr("No signal handler defined for signal %d.", sig);
+ }
+}
+
+/*
+ * Function to actually send the command char and message to the
+ * remote host.
+ */
+static int sendcmdmsg(cmd, msg)
+ char cmd;
+ char *msg;
+{
+ int len;
+
+ if (rem_w < 0)
+ return(-1);
+
+ /*
+ * All commands except C_NONE should have a newline
+ */
+ if (cmd != C_NONE && !strchr(msg + 1, '\n'))
+ (void) strcat(msg + 1, "\n");
+
+ if (cmd == C_NONE)
+ len = strlen(msg);
+ else {
+ len = strlen(msg + 1) + 1;
+ msg[0] = cmd;
+ }
+
+ debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
+ cmd, cmd,
+ (cmd == C_NONE) ? len-1 : len-2,
+ (cmd == C_NONE) ? msg : msg + 1);
+
+ return(!(xwrite(rem_w, msg, len) == len));
+}
+
+/*
+ * Send a command message to the remote host.
+ * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
+ * The fmt and arg? arguments are optional.
+ */
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg frontend to sendcmdmsg()
+ */
+extern int sendcmd(char cmd, char *fmt, ...)
+{
+ static char buf[BUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ if (fmt)
+ (void) vsprintf((cmd == C_NONE) ? buf : buf + 1, fmt, args);
+ else
+ buf[1] = CNULL;
+ va_end(args);
+
+ return(sendcmdmsg(cmd, buf));
+}
+#endif /* ARG_TYPE == ARG_STDARG */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs frontend to sendcmdmsg()
+ */
+extern int sendcmd(va_alist)
+ va_dcl
+{
+ static char buf[BUFSIZ];
+ va_list args;
+ char cmd;
+ char *fmt;
+
+ va_start(args);
+ /* XXX The "int" is necessary as a workaround for broken varargs */
+ cmd = (char) va_arg(args, int);
+ fmt = va_arg(args, char *);
+ if (fmt)
+ (void) vsprintf((cmd == C_NONE) ? buf : buf + 1, fmt, args);
+ else
+ buf[1] = CNULL;
+ va_end(args);
+
+ return(sendcmdmsg(cmd, buf));
+}
+#endif /* ARG_TYPE == ARG_VARARGS */
+
+#if !defined(ARG_TYPE)
+/*
+ * Stupid frontend to sendcmdmsg()
+ */
+/*VARARGS2*/
+extern int sendcmd(cmd, fmt, a1, a2, a3, a4, a5, a6, a7, a8)
+ char cmd;
+ char *fmt;
+{
+ static char buf[BUFSIZ];
+
+ if (fmt)
+ (void) sprintf((cmd == C_NONE) ? buf : buf + 1,
+ fmt, a1, a2, a3, a4, a5, a6, a7, a8);
+ else
+ buf[1] = CNULL;
+
+ return(sendcmdmsg(cmd, buf));
+}
+#endif /* !ARG_TYPE */
+
+/*
+ * Internal variables and routines for reading lines from the remote.
+ */
+static u_char rembuf[BUFSIZ];
+static u_char *remptr;
+static int remleft;
+
+#define remc() (--remleft < 0 ? remmore() : *remptr++)
+
+/*
+ * Back end to remote read()
+ */
+static int remread(fd, buf, bufsiz)
+ int fd;
+ u_char *buf;
+ int bufsiz;
+{
+ return(read(fd, (char *)buf, bufsiz));
+}
+
+static int remmore()
+{
+ (void) signal(SIGALRM, sighandler);
+ (void) alarm(rtimeout);
+
+ remleft = remread(rem_r, rembuf, sizeof(rembuf));
+
+ (void) alarm(0);
+
+ if (remleft < 0)
+ return (-2); /* error */
+ if (remleft == 0)
+ return (-1); /* EOF */
+ remptr = rembuf;
+ remleft--;
+ return (*remptr++);
+}
+
+/*
+ * Read an input line from the remote. Return the number of bytes
+ * stored (equivalent to strlen(p)). If `cleanup' is set, EOF at
+ * the beginning of a line is returned as EOF (-1); other EOFs, or
+ * errors, call cleanup() or lostconn(). In other words, unless
+ * the third argument is nonzero, this routine never returns failure.
+ */
+extern int remline(buffer, space, doclean)
+ register u_char *buffer;
+ int space;
+ int doclean;
+{
+ register int c, left = space;
+ register u_char *p = buffer;
+
+ if (rem_r < 0) {
+ error("Cannot read remote input: Remote descriptor not open.");
+ return(-1);
+ }
+
+ while (left > 0) {
+ if ((c = remc()) < -1) { /* error */
+ if (doclean) {
+ finish();
+ /*NOTREACHED*/
+ }
+ lostconn();
+ /*NOTREACHED*/
+ }
+ if (c == -1) { /* got EOF */
+ if (doclean) {
+ if (left == space)
+ return (-1);/* signal proper EOF */
+ finish(); /* improper EOF */
+ /*NOTREACHED*/
+ }
+ lostconn();
+ /*NOTREACHED*/
+ }
+ if (c == '\n') {
+ *p = CNULL;
+
+ if (debug) {
+ static char mbuf[BUFSIZ];
+
+ (void) sprintf(mbuf,
+ "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
+ buffer[0], buffer[0],
+ buffer + 1);
+
+ debugmsg(DM_PROTO, "%s", mbuf);
+ }
+
+ return (space - left);
+ }
+ *p++ = c;
+ left--;
+ }
+
+ /* this will probably blow the entire session */
+ error("remote input line too long");
+ p[-1] = CNULL; /* truncate */
+ return (space);
+}
+
+/*
+ * Non-line-oriented remote read.
+ */
+readrem(p, space)
+ char *p;
+ register int space;
+{
+ if (remleft <= 0) {
+ /*
+ * Set remote time out alarm.
+ */
+ (void) signal(SIGALRM, sighandler);
+ (void) alarm(rtimeout);
+
+ remleft = remread(rem_r, rembuf, sizeof(rembuf));
+
+ (void) alarm(0);
+ remptr = rembuf;
+ }
+
+ if (remleft <= 0)
+ return (remleft);
+ if (remleft < space)
+ space = remleft;
+
+ bcopy((char *) remptr, p, space);
+
+ remptr += space;
+ remleft -= space;
+
+ return (space);
+}
+
+/*
+ * Get the user name for the uid.
+ */
+extern char *getusername(uid, file, opts)
+ UID_T uid;
+ char *file;
+ opt_t opts;
+{
+ static char buf[100];
+ static UID_T lastuid = (UID_T)-1;
+ struct passwd *pwd = NULL;
+
+ /*
+ * The value of opts may have changed so we always
+ * do the opts check.
+ */
+ if (IS_ON(opts, DO_NUMCHKOWNER)) {
+ (void) sprintf(buf, ":%d", uid);
+ return(buf);
+ }
+
+ /*
+ * Try to avoid getpwuid() call.
+ */
+ if (lastuid == uid && buf[0])
+ return(buf);
+
+ lastuid = uid;
+
+ if ((pwd = getpwuid(uid)) == NULL) {
+ message(MT_WARNING,
+ "%s: No password entry for uid %d", file, uid);
+ (void) sprintf(buf, ":%d", uid);
+ } else
+ (void) strcpy(buf, pwd->pw_name);
+
+ return(buf);
+}
+
+/*
+ * Get the group name for the gid.
+ */
+extern char *getgroupname(gid, file, opts)
+ GID_T gid;
+ char *file;
+ opt_t opts;
+{
+ static char buf[100];
+ static GID_T lastgid = (GID_T)-1;
+ struct group *grp = NULL;
+
+ /*
+ * The value of opts may have changed so we always
+ * do the opts check.
+ */
+ if (IS_ON(opts, DO_NUMCHKGROUP)) {
+ (void) sprintf(buf, ":%d", gid);
+ return(buf);
+ }
+
+ /*
+ * Try to avoid getgrgid() call.
+ */
+ if (lastgid == gid && buf[0])
+ return(buf);
+
+ lastgid = gid;
+
+ if ((grp = (struct group *)getgrgid(gid)) == NULL) {
+ message(MT_WARNING, "%s: No name for group %d", file, gid);
+ (void) sprintf(buf, ":%d", gid);
+ } else
+ (void) strcpy(buf, grp->gr_name);
+
+ return(buf);
+}
+
+/*
+ * Read a response from the remote host.
+ */
+extern int response()
+{
+ static u_char resp[BUFSIZ];
+ u_char *s;
+ int n;
+
+ debugmsg(DM_CALL, "response() start\n");
+
+ n = remline(s = resp, sizeof(resp), 0);
+
+ n--;
+ switch (*s++) {
+ case C_ACK:
+ debugmsg(DM_PROTO, "received ACK\n");
+ return(0);
+ case C_LOGMSG:
+ if (n > 0) {
+ message(MT_CHANGE, "%s", s);
+ return(1);
+ }
+ debugmsg(DM_PROTO, "received EMPTY logmsg\n");
+ return(0);
+ case C_NOTEMSG:
+ if (s)
+ message(MT_NOTICE, "%s", s);
+ return(response());
+
+ default:
+ s--;
+ n++;
+ /* fall into... */
+
+ case C_ERRMSG: /* Normal error message */
+ if (s)
+ message(MT_NERROR, "%s", s);
+ return(-1);
+
+ case C_FERRMSG: /* Fatal error message */
+ if (s)
+ message(MT_FERROR, "%s", s);
+ finish();
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * This should be in expand.c but the other routines call other modules
+ * that we don't want to load in.
+ *
+ * Expand file names beginning with `~' into the
+ * user's home directory path name. Return a pointer in buf to the
+ * part corresponding to `file'.
+ */
+extern char *exptilde(ebuf, file)
+ char *ebuf;
+ register char *file;
+{
+ register char *s1, *s2, *s3;
+ extern char *homedir;
+
+ if (*file != '~') {
+ (void) strcpy(ebuf, file);
+ return(ebuf);
+ }
+ if (*++file == CNULL) {
+ s2 = homedir;
+ s3 = NULL;
+ } else if (*file == '/') {
+ s2 = homedir;
+ s3 = file;
+ } else {
+ s3 = file;
+ while (*s3 && *s3 != '/')
+ s3++;
+ if (*s3 == '/')
+ *s3 = CNULL;
+ else
+ s3 = NULL;
+ if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
+ if ((pw = getpwnam(file)) == NULL) {
+ error("%s: unknown user name", file);
+ if (s3 != NULL)
+ *s3 = '/';
+ return(NULL);
+ }
+ }
+ if (s3 != NULL)
+ *s3 = '/';
+ s2 = pw->pw_dir;
+ }
+ for (s1 = ebuf; *s1++ = *s2++; )
+ ;
+ s2 = --s1;
+ if (s3 != NULL) {
+ s2++;
+ while (*s1++ = *s3++)
+ ;
+ }
+ return(s2);
+}
+
+#if defined(DIRECT_RCMD)
+/*
+ * Set our effective user id to the user running us.
+ * This should be the uid we do most of our work as.
+ */
+extern int becomeuser()
+{
+ int r = 0;
+
+#if defined(HAVE_SAVED_IDS)
+ r = seteuid(userid);
+#else
+ r = setreuid(0, userid);
+#endif /* HAVE_SAVED_IDS */
+
+ if (r < 0)
+ error("becomeuser %d failed: %s (ruid = %d euid = %d)",
+ userid, SYSERR, getuid(), geteuid());
+
+ return(r);
+}
+#endif /* DIRECT_RCMD */
+
+#if defined(DIRECT_RCMD)
+/*
+ * Set our effective user id to "root" (uid = 0)
+ */
+extern int becomeroot()
+{
+ int r = 0;
+
+#if defined(HAVE_SAVED_IDS)
+ r = seteuid(0);
+#else
+ r = setreuid(userid, 0);
+#endif /* HAVE_SAVED_IDS */
+
+ if (r < 0)
+ error("becomeroot failed: %s (ruid = %d euid = %d)",
+ SYSERR, getuid(), geteuid());
+
+ return(r);
+}
+#endif /* DIRECT_RCMD */
+
+/*
+ * Set access and modify times of a given file
+ */
+extern int setfiletime(file, atime, mtime)
+ char *file;
+ time_t atime;
+ time_t mtime;
+{
+#if SETFTIME_TYPE == SETFTIME_UTIMES
+ struct timeval tv[2];
+
+ if (atime != 0 && mtime != 0) {
+ tv[0].tv_sec = atime;
+ tv[1].tv_sec = mtime;
+ tv[0].tv_usec = tv[1].tv_usec = (time_t) 0;
+ return(utimes(file, tv));
+ } else /* Set to current time */
+ return(utimes(file, (struct timeval *) NULL));
+
+#endif /* SETFTIME_UTIMES */
+
+#if SETFTIME_TYPE == SETFTIME_UTIME
+ struct utimbuf utbuf;
+
+ if (atime != 0 && mtime != 0) {
+ utbuf.actime = atime;
+ utbuf.modtime = mtime;
+ return(utime(file, &utbuf));
+ } else /* Set to current time */
+ return(utime(file, (struct utimbuf *)NULL));
+#endif /* SETFTIME_UTIME */
+
+#if !defined(SETFTIME_TYPE)
+ There is no "SETFTIME_TYPE" defined!
+#endif /* SETFTIME_TYPE */
+}
+
+/*
+ * Get version info
+ */
+extern char *getversion()
+{
+ static char buff[BUFSIZ];
+
+ (void) sprintf(buff,
+ "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
+ DISTVERSION, PATCHLEVEL, DISTSTATUS,
+ VERSION, DISTVERSION, PATCHLEVEL);
+
+ return(buff);
+}
+
+/*
+ * Execute a shell command to handle special cases.
+ * This is now common to both server and client
+ */
+void runcommand(cmd)
+ char *cmd;
+{
+ int fd[2], pid, i;
+ int status;
+ register char *cp, *s;
+ char sbuf[BUFSIZ], buf[BUFSIZ];
+
+ if (pipe(fd) < 0) {
+ error("pipe of %s failed: %s", cmd, SYSERR);
+ 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[PIPE_WRITE]);
+ (void) dup(fd[PIPE_WRITE]);
+ (void) close(fd[PIPE_READ]);
+ (void) close(fd[PIPE_WRITE]);
+ (void) execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
+ _exit(127);
+ }
+ (void) close(fd[PIPE_WRITE]);
+ s = sbuf;
+ *s++ = C_LOGMSG;
+ while ((i = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
+ cp = buf;
+ do {
+ *s++ = *cp++;
+ if (cp[-1] != '\n') {
+ if (s < (char *) &sbuf[sizeof(sbuf)-1])
+ continue;
+ *s++ = '\n';
+ }
+ /*
+ * Throw away blank lines.
+ */
+ if (s == &sbuf[2]) {
+ s--;
+ continue;
+ }
+ if (isserver)
+ (void) xwrite(rem_w, sbuf, s - sbuf);
+ else {
+ *s = CNULL;
+ message(MT_INFO, "%s", sbuf+1);
+ }
+ s = &sbuf[1];
+ } while (--i);
+ }
+ if (s > (char *) &sbuf[1]) {
+ *s++ = '\n';
+ if (isserver)
+ (void) xwrite(rem_w, sbuf, s - sbuf);
+ else {
+ *s = CNULL;
+ message(MT_INFO, "%s", sbuf+1);
+ }
+ }
+ while ((i = wait(&status)) != pid && i != -1)
+ ;
+ if (i == -1)
+ status = -1;
+ (void) close(fd[PIPE_READ]);
+ if (status)
+ error("shell returned %d", status);
+ else if (isserver)
+ ack();
+}
+
+/*
+ * Malloc with error checking
+ */
+char *xmalloc(amt)
+ int amt;
+{
+ char *ptr;
+ extern POINTER *malloc();
+
+ if ((ptr = (char *)malloc(amt)) == NULL)
+ fatalerr("Cannot malloc %d bytes of memory.", amt);
+
+ return(ptr);
+}
+
+/*
+ * realloc with error checking
+ */
+char *xrealloc(baseptr, amt)
+ char *baseptr;
+ unsigned int amt;
+{
+ char *new;
+ extern POINTER *realloc();
+
+ if ((new = (char *)realloc(baseptr, amt)) == NULL)
+ fatalerr("Cannot realloc %d bytes of memory.", amt);
+
+ return(new);
+}
+
+/*
+ * calloc with error checking
+ */
+char *xcalloc(num, esize)
+ unsigned num;
+ unsigned esize;
+{
+ char *ptr;
+ extern POINTER *calloc();
+
+ if ((ptr = (char *)calloc(num, esize)) == NULL)
+ fatalerr("Cannot calloc %d * %d = %d bytes of memory.",
+ num, esize, num * esize);
+
+ return(ptr);
+}
+
+/*
+ * Private version of basename()
+ */
+extern char *xbasename(path)
+ char *path;
+{
+ register char *cp;
+
+ if (cp = strrchr(path, '/'))
+ return(cp+1);
+ else
+ return(path);
+}
+
+/*
+ * Take a colon (':') seperated path to a file and
+ * search until a component of that path is found and
+ * return the found file name.
+ */
+extern char *searchpath(path)
+ char *path;
+{
+ register char *cp;
+ register char *file;
+ struct stat statbuf;
+
+ for (; ;) {
+ if (!path)
+ return((char *) NULL);
+ file = path;
+ cp = strchr(path, ':');
+ if (cp) {
+ path = cp + 1;
+ *cp = CNULL;
+ } else
+ path = NULL;
+ if (stat(file, &statbuf) == 0)
+ return(file);
+ /* Put back what we zapped */
+ if (path)
+ *cp = ':';
+ }
+}
+
+/*
+ * Set line buffering.
+ */
+extern int
+mysetlinebuf(fp)
+ FILE *fp;
+{
+#if SETBUF_TYPE == SETBUF_SETLINEBUF
+ return(setlinebuf(fp));
+#endif /* SETBUF_SETLINEBUF */
+#if SETBUF_TYPE == SETBUF_SETVBUF
+ return(setvbuf(stdout, NULL, _IOLBF, BUFSIZ));
+#endif /* SETBUF_SETVBUF */
+#if !defined(SETBUF_TYPE)
+ No SETBUF_TYPE is defined!
+#endif /* SETBUF_TYPE */
+}
+
+/*
+ * Our interface to system call to get a socket pair.
+ */
+int
+getsocketpair(domain, type, protocol, sv)
+ int domain;
+ int type;
+ int protocol;
+ int sv[];
+{
+#if SOCKPAIR_TYPE == SOCKPAIR_SOCKETPAIR
+ return(socketpair(domain, type, protocol, sv));
+#endif /* SOCKPAIR_SOCKETPAIR */
+#if SOCKPAIR_TYPE == SOCKPAIR_SPIPE
+ return(spipe(sv));
+#endif /* SOCKPAIR_SPIPE */
+#if !defined(SOCKPAIR_TYPE)
+ No SOCKPAIR_TYPE is defined!
+#endif /* SOCKPAIR_TYPE */
+}
diff --git a/usr.bin/rdist/config-data.h b/usr.bin/rdist/config-data.h
new file mode 100644
index 00000000000..9641dc5fdb9
--- /dev/null
+++ b/usr.bin/rdist/config-data.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1993 Michael A. Cooper
+ * Copyright (c) 1993 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.
+ */
+
+/*
+ * $Id: config-data.h,v 1.1 1996/02/03 12:12:14 dm Exp $
+ * @(#)configdata.h
+ */
+
+#ifndef __configdata_h__
+#define __configdata_h__
+
+/*
+ * Configuration data
+ */
+
+/*
+ * Define the read and write values for the file descriptor array
+ * used by pipe().
+ */
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+/*
+ * Directory information
+ */
+#if DIR_TYPE == DIR_DIRECT
+#include <sys/dir.h>
+typedef struct direct DIRENTRY;
+#define D_NAMLEN(p) ((p)->d_namlen)
+#endif /* DIR_DIRECT */
+
+#if DIR_TYPE == DIR_DIRENT
+#include <dirent.h>
+typedef struct dirent DIRENTRY;
+#define D_NAMLEN(p) (strlen((p)->d_name))
+#endif /* DIR_DIRENT */
+
+/*
+ * Set a default buffering type.
+ */
+#if !defined(SETBUF_TYPE)
+#define SETBUF_TYPE SETBUF_SETLINEBUF
+#endif /* SETBUF_TYPE */
+
+/*
+ * Set a default get socket pair type.
+ */
+#if !defined(SOCKPAIR_TYPE)
+#define SOCKPAIR_TYPE SOCKPAIR_SOCKETPAIR
+#endif /* SOCKPAIR_TYPE */
+
+/*
+ * Set default write(2) return and amount types.
+ */
+#if !defined(WRITE_RETURN_T)
+#define WRITE_RETURN_T int /* What write() returns */
+#endif /* WRITE_RETURN_T */
+#if !defined(WRITE_AMT_T)
+#define WRITE_AMT_T int /* Amount to write */
+#endif /* WRITE_AMT_T */
+
+#endif /* __configdata_h__ */
diff --git a/usr.bin/rdist/config-def.h b/usr.bin/rdist/config-def.h
new file mode 100644
index 00000000000..b6d24f9320a
--- /dev/null
+++ b/usr.bin/rdist/config-def.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1993 Michael A. Cooper
+ * Copyright (c) 1993 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.
+ */
+
+/*
+ * $Id: config-def.h,v 1.1 1996/02/03 12:12:15 dm Exp $
+ * @(#)configdef.h
+ */
+
+#ifndef __configdef_h__
+#define __configdef_h__
+
+/*
+ * Configuration definetions
+ */
+
+/*
+ * Types of wait() functions
+ */
+#define WAIT_WAIT3 1
+#define WAIT_WAITPID 2
+
+/*
+ * Types of directory routines
+ */
+#define DIR_DIRECT 1
+#define DIR_DIRENT 2
+
+/*
+ * Types of filesystem info routines
+ */
+#define FSI_GETFSSTAT 1
+#define FSI_GETMNT 2
+#define FSI_MNTCTL 3
+#define FSI_GETMNTENT 4
+
+/*
+ * Types of non-blocking I/O.
+ */
+#define NBIO_FCNTL 1
+#define NBIO_IOCTL 2
+
+/*
+ * Types of executable formats
+ */
+#define EXE_AOUT 1
+#define EXE_COFF 2
+#define EXE_MACHO 3
+#define EXE_HPEXEC 4
+#define EXE_ELF 5
+#define EXE_ELF_AND_COFF 6
+
+/*
+ * Types of set filetime functions
+ */
+#define SETFTIME_UTIMES 1 /* Have utimes() */
+#define SETFTIME_UTIME 2 /* Have utime() */
+
+/*
+ * Types of statfs() calls
+ */
+#define STATFS_BSD 1
+#define STATFS_SYSV 2
+#define STATFS_OSF1 3
+
+/*
+ * Arg types
+ */
+#define ARG_VARARGS 1
+#define ARG_STDARG 2
+
+/*
+ * Set buffering types
+ */
+#define SETBUF_SETLINEBUF 1
+#define SETBUF_SETVBUF 2
+
+/*
+ * Socket Pair types
+ */
+#define SOCKPAIR_SOCKETPAIR 1
+#define SOCKPAIR_SPIPE 2
+
+#endif /* __configdef_h__ */
diff --git a/usr.bin/rdist/config.h b/usr.bin/rdist/config.h
new file mode 100644
index 00000000000..f593de6565a
--- /dev/null
+++ b/usr.bin/rdist/config.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1993 Michael A. Cooper
+ * Copyright (c) 1993 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.
+ */
+
+/*
+ * $Id: config.h,v 1.1 1996/02/03 12:12:16 dm Exp $
+ * @(#)config.h
+ */
+
+#ifndef __config_h__
+#define __config_h__
+
+/*
+ * Configuration parameters
+ */
+
+#include OS_H
+
+/*
+ * Include system pathname header file. Usually this is <paths.h>.
+ * If your system doesn't have such a file, use "paths.h"
+ */
+#if defined(PATHS_H)
+# include PATHS_H
+#else
+# include "paths.h"
+#endif
+
+/*
+ * Define _PATH_OLDRDIST to be the name of the original rdist that
+ * was distributed with 4.3BSD.
+ *
+ * If you want to be backwards compability with the old rdist, uncomment
+ # the "#define" line. If you don't want to be backwards compability or
+ * don't have the old rdist, then uncomment the "#undef" line.
+ */
+#ifndef _PATH_OLDRDIST
+#define _PATH_OLDRDIST "/usr/bin/oldrdist" /* Enable compat */
+#endif
+/*#undef _PATH_OLDRDIST*/ /* Disable compat */
+
+/*
+ * Check to see if file is on a NFS. If it is, the file is
+ * skipped unless the hostname specified in the Distfile has
+ * a trailing "+". e.g. "foobar+". This feature is enabled by
+ * the -N option. If your system does not support NFS or you don't
+ * want the -N option, undefine this.
+ */
+#define NFS_CHECK
+
+/*
+ * Check to see if file on a Read-Only filesystem. If it is, no
+ * attempt is made to update the file. This feature is enabled by
+ * the -O option.
+ */
+#define RO_CHECK
+
+/*
+ * Default value for the maximum number of clients to update at once.
+ * Can be changed with the -M option.
+ */
+#define MAXCHILDREN 4
+
+/*
+ * Response Time Out interval (in seconds).
+ * Should be long enough to allow transfer of large files.
+ * The -t option can be used to override this value.
+ */
+#define RTIMEOUT 900
+
+/*
+ * Define LOG_OPTS to be the syslog/openlog() logging options you
+ * wish to use. Define to be 0 if you don't want any options.
+ * Define LOG_FACILITY to be the syslog/openlog() facility to log
+ * to. Both LOG_OPTS and LOG_FACILITY values are defined in <syslog.h>
+ * If you don't have syslog, then undefine both values.
+ */
+#define LOG_OPTS LOG_PID
+#if defined(LOG_DAEMON)
+# define LOG_FACILITY LOG_DAEMON
+#endif
+
+/*
+ * Syslog levels. Define these to match the levels you want to log
+ * via syslog(). These are defined in <syslog.h>. If you don't want
+ * a particuliar level logged _ever_, undefine it. What is logged is
+ * usually controlled via command line options, so you normally should
+ * not need to undefine these.
+ */
+#define SL_FERROR LOG_INFO /* Fatal errors */
+#define SL_NERROR LOG_INFO /* Normal errors */
+#define SL_WARNING LOG_INFO /* Warnings */
+#define SL_CHANGE LOG_INFO /* Things that change */
+#define SL_INFO LOG_INFO /* General info */
+#define SL_NOTICE LOG_NOTICE /* General notices */
+#define SL_DEBUG LOG_DEBUG /* Debugging */
+
+#endif /* __config_h__ */
diff --git a/usr.bin/rdist/defs.h b/usr.bin/rdist/defs.h
index 731f43427b4..c369e493731 100644
--- a/usr.bin/rdist/defs.h
+++ b/usr.bin/rdist/defs.h
@@ -1,6 +1,8 @@
+#ifndef __DEFS_H__
+#define __DEFS_H__
/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1983 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
@@ -29,98 +31,261 @@
* 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>
+/*
+ * $Id: defs.h,v 1.2 1996/02/03 12:12:18 dm Exp $
+ * @(#)defs.h 5.2 (Berkeley) 3/20/86
+ */
+/*
+ * POSIX settings
+ */
+#if defined(_POSIX_SOURCE)
+#include <unistd.h>
+#include <stdlib.h>
+#endif /* _POSIX_SOURCE */
+#include <stdio.h>
+#include <ctype.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 <syslog.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include "version.h"
+#include "config-def.h"
+#include "config.h"
+#include "config-data.h"
#include "pathnames.h"
+#include "types.h"
+
+#if !defined(yacc)
+/* #include "y.tab.h" */
+#endif /* yacc */
+
+#include <signal.h>
/*
- * The version number should be changed whenever the protocol changes.
+ * This belongs in os-svr4.h but many SVR4 OS's
+ * define SVR4 externel to Rdist so we put this
+ * check here.
*/
-#define VERSION 3
+#if defined(SVR4)
+#define NEED_FCNTL_H
+#define NEED_UNISTD_H
+#define NEED_NETDB_H
+#endif /* defined(SVR4) */
- /* 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
+#if defined(NEED_NETDB_H)
+#include <netdb.h>
+#endif /* NEED_NETDB_H */
+#if defined(NEED_FCNTL_H)
+#include <fcntl.h>
+#endif /* NEED_FCNTL_H */
+#if defined(NEED_LIMITS_H)
+#include <limits.h>
+#endif /* NEED_LIMITS_H */
+#if defined(NEED_UNISTD_H)
+#include <unistd.h>
+#endif /* NEED_UNISTD_H */
+#if defined(NEED_STRING_H)
+#include <string.h>
+#endif /* NEED_STRING_H */
+
+#if defined(ARG_TYPE)
+#if ARG_TYPE == ARG_STDARG
+#include <stdarg.h>
+#endif
+#if ARG_TYPE == ARG_VARARGS
+#include <varargs.h>
+#endif
+#endif /* ARG_TYPE */
+
+ /* boolean truth */
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+ /* file modes */
+#ifndef S_IXUSR
+#define S_IXUSR 0000100
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0000010
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0000001
+#endif
/* lexical definitions */
-#define QUOTE 0200 /* used internally for quoted characters */
-#define TRIM 0177 /* Mask to strip quote bit */
+#define QUOTECHAR 160 /* quote next character */
/* 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
+#define INMAX 3500
/* expand type definitions */
-#define E_VARS 0x1
-#define E_SHELL 0x2
-#define E_TILDE 0x4
-#define E_ALL 0x7
+#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 LOOKUP 0
+#define INSERT 1
+#define REPLACE 2
-#define ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+ /* Bit flag test macros */
+#define IS_ON(b,f) (b > 0 && (b & f))
+#define IS_OFF(b,f) !(IS_ON(b,f))
+#define FLAG_ON(b,f) b |= f
+#define FLAG_OFF(b,f) b &= ~(f)
-#define ALLOC(x) (struct x *) malloc(sizeof(struct x))
+/*
+ * POSIX systems should already have S_* defined.
+ */
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#ifndef S_ISLNK
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+
+#define ALLOC(x) (struct x *) xmalloc(sizeof(struct x))
+#define A(s) ((s) ? s : "<null>")
+
+/*
+ * Environment variable names
+ */
+#define E_FILES "FILES" /* List of files */
+#define E_LOCFILE "FILE" /* Local Filename */
+#define E_REMFILE "REMFILE" /* Remote Filename */
+#define E_BASEFILE "BASEFILE" /* basename of Remote File */
-struct namelist { /* for making lists of strings */
+/*
+ * Suffix to use when saving files
+ */
+#ifndef SAVE_SUFFIX
+#define SAVE_SUFFIX ".OLD"
+#endif
+
+/*
+ * Get system error string
+ */
+#define SYSERR strerror(errno)
+
+#define COMMENT_CHAR '#' /* Config file comment char */
+#define CNULL '\0' /* NULL character */
+
+/*
+ * These are the top level protocol commands.
+ */
+#define C_NONE '=' /* No command - pass cleanly */
+#define C_ERRMSG '\1' /* Log an error message */
+#define C_FERRMSG '\2' /* Log a fatal error message */
+#define C_NOTEMSG '\3' /* Log a note message */
+#define C_LOGMSG '\4' /* Log a message */
+#define C_ACK '\5' /* Acknowledge */
+#define C_SETCONFIG 'c' /* Set configuration parameters */
+#define C_DIRTARGET 'T' /* Set target directory name */
+#define C_TARGET 't' /* Set target file name */
+#define C_RECVREG 'R' /* Receive a regular file */
+#define C_RECVDIR 'D' /* Receive a directory */
+#define C_RECVSYMLINK 'K' /* Receive a symbolic link */
+#define C_RECVHARDLINK 'k' /* Receive a hard link */
+#define C_END 'E' /* Indicate end of recieve/send */
+#define C_CLEAN 'C' /* Clean up */
+#define C_QUERY 'Q' /* Query without checking */
+#define C_SPECIAL 'S' /* Execute special command */
+#define C_CMDSPECIAL 's' /* Execute cmd special command */
+#define C_CHMOD 'M' /* Chmod a file */
+
+#define ack() (void) sendcmd(C_ACK, (char *)NULL)
+#define err() (void) sendcmd(C_ERRMSG, (char *)NULL)
+
+/*
+ * Session startup commands.
+ */
+#define S_VERSION 'V' /* Version number */
+#define S_REMOTEUSER 'R' /* Remote user name */
+#define S_LOCALUSER 'L' /* Local user name */
+#define S_END 'E' /* End of session startup commands */
+
+/*
+ * These are the commands for "set config".
+ */
+#define SC_FREESPACE 's' /* Set min free space */
+#define SC_FREEFILES 'f' /* Set min free files */
+#define SC_HOSTNAME 'H' /* Set client hostname */
+#define SC_LOGGING 'L' /* Set logging options */
+
+/*
+ * Query commands
+ */
+#define QC_ONNFS 'F' /* File exists & is on a NFS */
+#define QC_ONRO 'O' /* File exists & is on a readonly fs */
+#define QC_NO 'N' /* File does not exist */
+#define QC_SYM 'l' /* File exists & is a symlink */
+#define QC_YES 'Y' /* File does exist */
+
+/*
+ * Clean commands
+ */
+#define CC_QUERY 'Q' /* Query if file should be rm'ed */
+#define CC_END 'E' /* End of cleaning */
+#define CC_YES 'Y' /* File doesn't exist - remove */
+#define CC_NO 'N' /* File does exist - don't remove */
+
+/*
+ * Run Command commands
+ */
+#define RC_FILE 'F' /* Name of a target file */
+#define RC_COMMAND 'C' /* Command to run */
+
+/*
+ * Name list
+ */
+struct namelist { /* for making lists of strings */
char *n_name;
struct namelist *n_next;
};
+/*
+ * Sub command structure
+ */
struct subcmd {
short sc_type; /* type - INSTALL,NOTIFY,EXCEPT,SPECIAL */
- short sc_options;
+ opt_t sc_options;
char *sc_name;
struct namelist *sc_args;
struct subcmd *sc_next;
};
+/*
+ * Cmd flags
+ */
+#define CMD_ASSIGNED 0x01 /* This entry has been assigned */
+#define CMD_CONNFAILED 0x02 /* Connection failed */
+#define CMD_NOCHKNFS 0x04 /* Disable NFS checks */
+
+/*
+ * General command structure
+ */
struct cmd {
int c_type; /* type - ARROW,DCOLON */
+ int c_flags; /* flags - CMD_USED,CMD_FAILED */
char *c_name; /* hostname or time stamp file name */
char *c_label; /* label for partial update */
struct namelist *c_files;
@@ -128,54 +293,121 @@ struct cmd {
struct cmd *c_next;
};
+/*
+ * Hard link buffer information
+ */
struct linkbuf {
ino_t inum;
dev_t devnum;
int count;
char pathname[BUFSIZ];
+ char src[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));
+extern char *optarg; /* Option argument */
+extern char *path_remsh; /* Remote shell command */
+extern char buf[]; /* General purpose buffer */
+extern char host[]; /* Host name of master copy */
+extern char *currenthost; /* Name of current host */
+extern char *progname; /* Name of this program */
+extern char **realargv; /* Real argv */
+extern int optind; /* Option index into argv */
+extern int contimedout; /* Connection timed out */
+extern int debug; /* Debugging flag */
+extern opt_t defoptions; /* Default install options */
+extern int do_fork; /* Should we do fork()'ing */
+extern int errno; /* System error number */
+extern int isserver; /* Acting as remote server */
+extern int nerrs; /* Number of errors seen */
+extern int nflag; /* NOP flag, don't execute commands */
+extern opt_t options; /* Global options */
+extern int proto_version; /* Protocol version number */
+extern int realargc; /* Real argc */
+extern int rem_r; /* Remote file descriptor, reading */
+extern int rem_w; /* Remote file descriptor, writing */
+extern int rtimeout; /* Response time out in seconds */
+extern UID_T userid; /* User ID of rdist user */
+extern jmp_buf finish_jmpbuf; /* Setjmp buffer for finish() */
+extern struct group *gr; /* pointer to static area used by getgrent */
+extern struct linkbuf *ihead; /* list of files with more than one link */
+extern struct passwd *pw; /* pointer to static area used by getpwent */
+#ifdef USE_STATDB
+extern int dostatdb;
+extern int juststatdb;
+#endif /* USE_STATDB */
+
+/*
+ * System function declarations
+ */
+char *hasmntopt();
+char *strchr();
+char *strdup();
+char *strrchr();
+char *strtok();
+
+/*
+ * Our own declarations.
+ */
+char *exptilde();
+char *makestr();
+char *xcalloc();
+char *xmalloc();
+char *xrealloc();
+extern char *xbasename();
+extern char *getdistoptlist();
+extern char *getgroupname();
+extern char *getnlstr();
+extern char *getnotifyfile();
+extern char *getondistoptlist();
+extern char *getusername();
+extern char *getversion();
+extern char *msgparseopts();
+extern char *searchpath();
+extern int any();
+extern int init();
+extern int install();
+extern int isexec();
+extern int parsedistopts();
+extern int remline();
+extern int setfiletime();
+extern int spawn();
+extern struct subcmd *makesubcmd();
+extern void checkhostname();
+extern void cleanup();
+extern void complain();
+extern void docmds();
+extern void finish();
+extern void log();
+extern void logmsg();
+extern void lostconn();
+extern void markassigned();
+extern void msgprusage();
+extern void note();
+extern void runcmdspecial();
+extern void runcommand();
+extern void server();
+extern void setprogname();
+extern void sighandler();
+extern void waitup();
+struct namelist *expand();
+struct namelist *lookup();
+struct namelist *makenl();
+extern WRITE_RETURN_T xwrite();
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+extern void debugmsg(int, char *, ...);
+extern void error(char *, ...);
+extern void fatalerr(char *, ...);
+extern void message(int, char *, ...);
+extern void setproctitle(char *fmt, ...);
+#else
+extern void debugmsg();
+extern void error();
+extern void fatalerr();
+extern void message();
+extern void setproctitle();
+#endif
+
+#endif /* __DEFS_H__ */
diff --git a/usr.bin/rdist/distopt.c b/usr.bin/rdist/distopt.c
new file mode 100644
index 00000000000..9123ee5d971
--- /dev/null
+++ b/usr.bin/rdist/distopt.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: distopt.c,v 1.1 1996/02/03 12:12:20 dm Exp $";
+
+static char sccsid[] = "@(#)distopt.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* !lint */
+
+/*
+ * Dist Option functions
+ */
+
+#include "defs.h"
+
+/*
+ * Distfile Option Information
+ */
+DISTOPTINFO distoptinfo[] = {
+ { DO_CHKNFS, "chknfs" },
+ { DO_CHKREADONLY, "chkreadonly" },
+ { DO_CHKSYM, "chksym" },
+ { DO_COMPARE, "compare" },
+ { DO_FOLLOW, "follow" },
+ { DO_IGNLNKS, "ignlnks" },
+ { DO_NOCHKGROUP, "nochkgroup" },
+ { DO_NOCHKMODE, "nochkmode" },
+ { DO_NOCHKOWNER, "nochkowner" },
+ { DO_NODESCEND, "nodescend" },
+ { DO_NOEXEC, "noexec" },
+ { DO_NUMCHKGROUP, "numchkgroup" },
+ { DO_NUMCHKOWNER, "numchkowner" },
+ { DO_QUIET, "quiet" },
+ { DO_REMOVE, "remove" },
+ { DO_SAVETARGETS, "savetargets" },
+ { DO_VERIFY, "verify" },
+ { DO_WHOLE, "whole" },
+ { DO_YOUNGER, "younger" },
+ { 0 },
+};
+
+/*
+ * Get a Distfile Option entry named "name".
+ */
+extern DISTOPTINFO *getdistopt(name)
+ char *name;
+{
+ register int i;
+
+ for (i = 0; distoptinfo[i].do_name; ++i)
+ if (strcasecmp(name, distoptinfo[i].do_name) == 0)
+ return(&distoptinfo[i]);
+
+ return((DISTOPTINFO *) NULL);
+}
+
+/*
+ * Parse a dist option string. Set option flags to optptr.
+ * If doerrs is true, print out own error message. Returns
+ * 0 on success.
+ */
+extern int parsedistopts(str, optptr, doerrs)
+ char *str;
+ opt_t *optptr;
+ int doerrs;
+{
+ register char *string, *optstr;
+ DISTOPTINFO *distopt;
+ int negate;
+
+ /* strtok() is harmful */
+ string = strdup(str);
+
+ for (optstr = strtok(string, ","); optstr;
+ optstr = strtok((char *) NULL, ",")) {
+ if (strncasecmp(optstr, "no", 2) == 0)
+ negate = TRUE;
+ else
+ negate = FALSE;
+
+ /*
+ * Try looking up option name. If that fails
+ * and the option starts with "no", strip "no"
+ * from option and retry lookup.
+ */
+ if (distopt = getdistopt(optstr)) {
+ FLAG_ON(*optptr, distopt->do_value);
+ continue;
+ }
+ if (negate && (distopt = getdistopt(optstr+2))) {
+ FLAG_OFF(*optptr, distopt->do_value);
+ continue;
+ }
+ if (doerrs)
+ error("Dist option \"%s\" is not valid.", optstr);
+ }
+
+ if (string)
+ (void) free(string);
+
+ return(nerrs);
+}
+
+/*
+ * Get a list of the Distfile Option Entries.
+ */
+extern char *getdistoptlist()
+{
+ register int i;
+ static char buf[1024];
+
+ for (i = 0, buf[0] = CNULL; distoptinfo[i].do_name; ++i) {
+ if (buf[0] == CNULL)
+ (void) strcpy(buf, distoptinfo[i].do_name);
+ else {
+ (void) strcat(buf, ",");
+ (void) strcat(buf, distoptinfo[i].do_name);
+ }
+ }
+
+ return(buf);
+}
+
+/*
+ * Get a list of the Distfile Option Entries for each enabled
+ * value in "opts".
+ */
+extern char *getondistoptlist(opts)
+ opt_t opts;
+{
+ register int i;
+ static char buf[1024];
+
+ for (i = 0, buf[0] = CNULL; distoptinfo[i].do_name; ++i) {
+ if (!IS_ON(opts, distoptinfo[i].do_value))
+ continue;
+
+ if (buf[0] == CNULL)
+ (void) strcpy(buf, distoptinfo[i].do_name);
+ else {
+ (void) strcat(buf, ",");
+ (void) strcat(buf, distoptinfo[i].do_name);
+ }
+ }
+
+ return(buf);
+}
+
diff --git a/usr.bin/rdist/docmd.c b/usr.bin/rdist/docmd.c
index 4f71389e5ad..f9ac0620613 100644
--- a/usr.bin/rdist/docmd.c
+++ b/usr.bin/rdist/docmd.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1983 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
@@ -32,350 +32,494 @@
*/
#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 $";
+static char RCSid[] =
+"$Id: docmd.c,v 1.2 1996/02/03 12:12:22 dm Exp $";
+
+static char sccsid[] = "@(#)docmd.c 5.1 (Berkeley) 6/6/85";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
#endif /* not lint */
+/*
+ * Functions for rdist that do command (cmd) related activities.
+ */
+
#include "defs.h"
-#include <setjmp.h>
+#include "y.tab.h"
+#include <sys/socket.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 *));
+struct subcmd *subcmds; /* list of sub-commands for
+ current cmd */
+struct namelist *filelist; /* list of source files */
+extern struct cmd *cmds; /* Initialized by yyparse() */
+time_t lastmod; /* Last modify time */
+
+extern char target[];
+extern char *ptarget;
+extern int activechildren;
+extern int maxchildren;
+extern int amchild;
+extern char *path_rdistd;
+
+static void cmptime();
/*
- * Do the commands in cmds (initialized by yyparse).
+ * Signal end of connection.
*/
-void
-docmds(dhosts, argc, argv)
- char **dhosts;
- int argc;
- char **argv;
+static void closeconn()
{
- register struct cmd *c;
- register struct namelist *f;
- register char **cpp;
- extern struct cmd *cmds;
+ debugmsg(DM_CALL, "closeconn() called\n");
- signal(SIGHUP, cleanup);
- signal(SIGINT, cleanup);
- signal(SIGQUIT, cleanup);
- signal(SIGTERM, cleanup);
+ if (rem_w >= 0) {
+ /* We don't care if the connection is still good or not */
+ signal(SIGPIPE, SIG_IGN);
- 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);
- }
+ (void) sendcmd(C_FERRMSG, NULL);
+ (void) close(rem_w);
+ (void) close(rem_r); /* This can't hurt */
+ rem_w = -1;
+ rem_r = -1;
}
- closeconn();
}
/*
- * Process commands for sending files to other machines.
+ * 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
-doarrow(filev, files, rhost, cmds)
- char **filev;
- struct namelist *files;
+static void notify(rhost, to, lmod)
char *rhost;
- struct subcmd *cmds;
+ register struct namelist *to;
+ time_t lmod;
{
- register struct namelist *f;
- register struct subcmd *sc;
- register char **cpp;
- int n, ddir, opts = options;
+ register int fd, len;
+ FILE *pf, *popen();
+ struct stat stb;
+ static char buf[BUFSIZ];
+ char *file;
- if (debug)
- printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
+ if (IS_ON(options, DO_VERIFY) || to == NULL)
+ return;
- if (files == NULL) {
- error("no files to be updated\n");
+ if ((file = getnotifyfile()) == NULL)
return;
+
+ if (!IS_ON(options, DO_QUIET)) {
+ message(MT_INFO, "notify %s%s %s",
+ (rhost) ? "@" : "",
+ (rhost) ? rhost : "", getnlstr(to));
}
- 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);
- }
+ return;
+
+ debugmsg(DM_MISC, "notify() temp file = '%s'", file);
+
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ error("%s: open for reading failed: %s", file, SYSERR);
+ return;
}
- 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);
+ if (fstat(fd, &stb) < 0) {
+ error("%s: fstat failed: %s", file, SYSERR);
+ (void) close(fd);
+ return;
}
-done:
- if (!nflag) {
- (void) signal(SIGPIPE, cleanup);
- (void) fclose(lfp);
- lfp = NULL;
+ if (stb.st_size == 0) {
+ (void) close(fd);
+ return;
}
- 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 pipe to mailling program.
+ * Set IFS to avoid possible security problem with users
+ * setting "IFS=/".
+ */
+ (void) sprintf(buf, "IFS=\" \t\"; export IFS; %s -oi -t",
+ _PATH_SENDMAIL);
+ pf = popen(buf, "w");
+ if (pf == NULL) {
+ error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
+ (void) unlink(file);
+ (void) close(fd);
+ return;
+ }
+ /*
+ * Output the proper header information.
+ */
+ (void) fprintf(pf, "From: rdist (Remote distribution program)\n");
+ (void) fprintf(pf, "To:");
+ if (!any('@', to->n_name) && rhost != NULL)
+ (void) fprintf(pf, " %s@%s", to->n_name, rhost);
+ else
+ (void) fprintf(pf, " %s", to->n_name);
+ to = to->n_next;
+ while (to != NULL) {
+ if (!any('@', to->n_name) && rhost != NULL)
+ (void) fprintf(pf, ", %s@%s", to->n_name, rhost);
+ else
+ (void) fprintf(pf, ", %s", to->n_name);
+ to = to->n_next;
+ }
+ (void) putc('\n', pf);
+ if (rhost != NULL)
+ (void) fprintf(pf,
+ "Subject: files updated by rdist from %s to %s\n",
+ host, rhost);
+ else
+ (void) fprintf(pf, "Subject: files updated after %s\n",
+ ctime(&lmod));
+ (void) putc('\n', pf);
+ (void) putc('\n', pf);
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0)
+ (void) fwrite(buf, 1, len, pf);
+
+ (void) pclose(pf);
+ (void) close(fd);
+ (void) unlink(file);
+}
+
+/*
+ * XXX Hack for NFS. If a hostname from the distfile
+ * ends with a '+', then the normal restriction of
+ * skipping files that are on an NFS filesystem is
+ * bypassed. We always strip '+' to be consistent.
+ */
+static void checkcmd(cmd)
+ struct cmd *cmd;
+{
+ int l;
+
+ if (!cmd || !(cmd->c_name)) {
+ debugmsg(DM_MISC, "checkcmd() NULL cmd parameter");
+ return;
+ }
+
+ l = strlen(cmd->c_name);
+ if (l <= 0)
+ return;
+ if (cmd->c_name[l-1] == '+') {
+ cmd->c_flags |= CMD_NOCHKNFS;
+ cmd->c_name[l-1] = CNULL;
+ }
+}
+
+/*
+ * Mark all other entries for this command (cmd)
+ * as assigned.
+ */
+extern void markassigned(cmd, cmdlist)
+ struct cmd *cmd;
+ struct cmd *cmdlist;
+{
+ register struct cmd *pcmd;
+
+ for (pcmd = cmdlist; pcmd; pcmd = pcmd->c_next) {
+ checkcmd(pcmd);
+ if (pcmd->c_type == cmd->c_type &&
+ strcmp(pcmd->c_name, cmd->c_name)==0)
+ pcmd->c_flags |= CMD_ASSIGNED;
}
}
/*
+ * Mark the command "cmd" as failed for all commands in list cmdlist.
+ */
+static void markfailed(cmd, cmdlist)
+ struct cmd *cmd;
+ struct cmd *cmdlist;
+{
+ register struct cmd *pc;
+
+ if (!cmd) {
+ debugmsg(DM_MISC, "markfailed() NULL cmd parameter");
+ return;
+ }
+
+ checkcmd(cmd);
+ cmd->c_flags |= CMD_CONNFAILED;
+ for (pc = cmdlist; pc; pc = pc->c_next) {
+ checkcmd(pc);
+ if (pc->c_type == cmd->c_type &&
+ strcmp(pc->c_name, cmd->c_name)==0)
+ pc->c_flags |= CMD_CONNFAILED;
+ }
+}
+
+static int remotecmd(rhost, luser, ruser, cmd)
+ char *rhost;
+ char *luser, *ruser;
+ char *cmd;
+{
+ int desc;
+#if defined(DIRECT_RCMD)
+ static int port = -1;
+#endif /* DIRECT_RCMD */
+
+ debugmsg(DM_MISC, "local user = %s remote user = %s\n", luser, ruser);
+ debugmsg(DM_MISC, "Remote command = '%s'\n", cmd);
+
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ (void) signal(SIGALRM, sighandler);
+ (void) alarm(RTIMEOUT);
+
+#if defined(DIRECT_RCMD)
+ (void) signal(SIGPIPE, sighandler);
+
+ if (port < 0) {
+ struct servent *sp;
+
+ if ((sp = getservbyname("shell", "tcp")) == NULL)
+ fatalerr("shell/tcp: unknown service");
+ port = sp->s_port;
+ }
+
+ if (becomeroot() != 0)
+ exit(1);
+ desc = rcmd(&rhost, port, luser, ruser, cmd, 0);
+ if (becomeuser() != 0)
+ exit(1);
+#else /* !DIRECT_RCMD */
+ debugmsg(DM_MISC, "Remote shell command = '%s'\n", path_remsh);
+ (void) signal(SIGPIPE, SIG_IGN);
+ desc = rshrcmd(&rhost, -1, luser, ruser, cmd, 0);
+ if (desc > 0)
+ (void) signal(SIGPIPE, sighandler);
+#endif /* DIRECT_RCMD */
+
+ (void) alarm(0);
+
+ return(desc);
+}
+
+/*
* Create a connection to the rdist server on the machine rhost.
+ * Return 0 if the connection fails or 1 if it succeeds.
*/
-static int
-makeconn(rhost)
+static int makeconn(rhost)
char *rhost;
{
register char *ruser, *cp;
static char *cur_host = NULL;
- static int port = -1;
- char tuser[20];
+ extern char *locuser;
+ extern long min_freefiles, min_freespace;
+ extern char *remotemsglist;
+ char tuser[BUFSIZ], buf[BUFSIZ];
+ u_char respbuff[BUFSIZ];
int n;
- extern char user[];
- extern int userid;
- if (debug)
- printf("makeconn(%s)\n", rhost);
+ debugmsg(DM_CALL, "makeconn(%s)", rhost);
- if (cur_host != NULL && rem >= 0) {
+ /*
+ * See if we're already connected to this host
+ */
+ if (cur_host != NULL && rem_w >= 0) {
if (strcmp(cur_host, rhost) == 0)
return(1);
closeconn();
}
+
+ /*
+ * Determine remote user and current host names
+ */
cur_host = rhost;
- cp = index(rhost, '@');
+ cp = strchr(rhost, '@');
+
if (cp != NULL) {
char c = *cp;
- *cp = '\0';
- strncpy(tuser, rhost, sizeof(tuser)-1);
+ *cp = CNULL;
+ (void) strncpy((char *)tuser, rhost, sizeof(tuser)-1);
*cp = c;
rhost = cp + 1;
ruser = tuser;
- if (*ruser == '\0')
- ruser = user;
+ if (*ruser == CNULL)
+ ruser = locuser;
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;
+ ruser = locuser;
- if ((sp = getservbyname("shell", "tcp")) == NULL)
- fatal("shell/tcp: unknown service");
- port = sp->s_port;
- }
+ if (!IS_ON(options, DO_QUIET))
+ message(MT_VERBOSE, "updating host %s", rhost);
- if (debug) {
- printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
- printf("buf = %s\n", buf);
- }
+ (void) sprintf(buf, "%.*s -S", sizeof(buf)-5, path_rdistd);
+
+ if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0)
+ return(0);
- fflush(stdout);
- seteuid(0);
- rem = rcmd(&rhost, port, user, ruser, buf, 0);
- seteuid(userid);
- if (rem < 0)
+ /*
+ * First thing received should be S_VERSION
+ */
+ n = remline(respbuff, sizeof(respbuff), TRUE);
+ if (n <= 0 || respbuff[0] != S_VERSION) {
+ error("Unexpected input from server: \"%s\".", respbuff);
+ closeconn();
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;
+ /*
+ * For future compatibility we check to see if the server
+ * sent it's version number to us. If it did, we use it,
+ * otherwise, we send our version number to the server and let
+ * it decide if it can handle our protocol version.
+ */
+ if (respbuff[1] == CNULL) {
+ /*
+ * The server wants us to send it our version number
+ */
+ (void) sendcmd(S_VERSION, "%d", VERSION);
+ if (response() < 0)
+ return(0);
+ } else {
+ /*
+ * The server sent it's version number to us
+ */
+ proto_version = atoi(&respbuff[1]);
+ if (proto_version != VERSION) {
+ fatalerr(
+ "Server version (%d) is not the same as local version (%d).",
+ proto_version, VERSION);
+ return(0);
+ }
}
-}
-
-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;
+ /*
+ * Send config commands
+ */
+ if (host[0]) {
+ (void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host);
+ if (response() < 0)
+ return(0);
+ }
+ if (min_freespace) {
+ (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREESPACE,
+ min_freespace);
+ if (response() < 0)
+ return(0);
+ }
+ if (min_freefiles) {
+ (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREEFILES,
+ min_freefiles);
+ if (response() < 0)
+ return(0);
+ }
+ if (remotemsglist) {
+ (void) sendcmd(C_SETCONFIG, "%c%s", SC_LOGGING, remotemsglist);
+ if (response() < 0)
+ return(0);
+ }
- 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.
+ * Process commands for sending files to other machines.
*/
-static void
-dodcolon(filev, files, stamp, cmds)
+static void doarrow(cmd, filev)
+ struct cmd *cmd;
char **filev;
- struct namelist *files;
- char *stamp;
- struct subcmd *cmds;
{
- register struct subcmd *sc;
register struct namelist *f;
+ register struct subcmd *sc;
register char **cpp;
- struct timeval tv[2];
- struct timezone tz;
- struct stat stb;
-
- if (debug)
- printf("dodcolon()\n");
+ int n, ddir, destdir, opts = options;
+ struct namelist *files;
+ struct subcmd *sbcmds;
+ char *rhost;
+ int didupdate = 0;
- if (files == NULL) {
- error("no files to be updated\n");
+ if (!cmd) {
+ debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
return;
}
- if (stat(stamp, &stb) < 0) {
- error("%s: %s\n", stamp, strerror(errno));
+
+ files = cmd->c_files;
+ sbcmds = cmd->c_cmds;
+ rhost = cmd->c_name;
+
+ if (files == NULL) {
+ error("No files to be updated on %s for target \"%s\"",
+ rhost, cmd->c_label);
return;
}
- if (debug)
- printf("%s: %ld\n", stamp, stb.st_mtime);
- subcmds = cmds;
- lastmod = stb.st_mtime;
- if (nflag || (options & VERIFY))
- tfp = NULL;
+ debugmsg(DM_CALL, "doarrow(%x, %s, %x) start",
+ files, A(rhost), sbcmds);
+
+ if (nflag)
+ (void) printf("updating host %s\n", rhost);
else {
- if ((tfp = fopen(tempfile, "w")) == NULL) {
- error("%s: %s\n", stamp, strerror(errno));
+ if (cmd->c_flags & CMD_CONNFAILED) {
+ debugmsg(DM_MISC,
+ "makeconn %s failed before; skipping\n",
+ rhost);
+ return;
+ }
+
+ if (setjmp(finish_jmpbuf)) {
+ debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
+ markfailed(cmd, cmds);
return;
}
- (void) gettimeofday(&tv[0], &tz);
- tv[1] = tv[0];
- (void) utimes(stamp, tv);
+
+ if (!makeconn(rhost)) {
+ markfailed(cmd, cmds);
+ return;
+ }
+ }
+
+ subcmds = sbcmds;
+ filelist = files;
+
+ n = 0;
+ for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != INSTALL)
+ continue;
+ n++;
+ /*
+ * destination is a directory if one of the following is true:
+ * a) more than one name specified on left side of -> directive
+ * b) basename of destination in "install" directive is "."
+ * (e.g. install /tmp/.;)
+ * c) name on left side of -> directive is a directory on local system.
+ *
+ * We need 2 destdir flags (destdir and ddir) because single directory
+ * source is handled differently. In this case, ddir is 0 (which
+ * tells install() not to send DIRTARGET directive to remote rdistd)
+ * and destdir is 1 (which tells remfilename() how to build the FILE
+ * variables correctly). In every other case, destdir and ddir will
+ * have the same value.
+ */
+ ddir = files->n_next != NULL; /* destination is a directory */
+ if (!ddir) {
+ struct stat s;
+ int isadir = 0;
+
+ if (lstat(files->n_name, &s) == 0)
+ isadir = S_ISDIR(s.st_mode);
+ if (!isadir && sc->sc_name && *sc->sc_name)
+ ddir = !strcmp(xbasename(sc->sc_name),".");
+ destdir = isadir | ddir;
+ } else
+ destdir = ddir;
+
+ debugmsg(DM_MISC,
+ "Debug files->n_next= %d, destdir=%d, ddir=%d",
+ files->n_next, destdir, ddir);
+
+ if (!sc->sc_name || !*sc->sc_name) {
+ destdir = 0;
+ ddir = 0;
}
+ debugmsg(DM_MISC,
+ "Debug sc->sc_name=%x, destdir=%d, ddir=%d",
+ sc->sc_name, destdir, ddir);
+
for (f = files; f != NULL; f = f->n_next) {
if (filev) {
for (cpp = filev; *cpp; cpp++)
@@ -384,247 +528,539 @@ dodcolon(filev, files, stamp, cmds)
continue;
}
found:
- tp = NULL;
- cmptime(f->n_name);
+ if (install(f->n_name, sc->sc_name, ddir, destdir,
+ sc->sc_options) > 0)
+ ++didupdate;
+ opts = sc->sc_options;
}
- 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);
-}
+ } /* end loop for each INSTALL command */
-/*
- * 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;
+ /* if no INSTALL commands present, do default install */
+ if (!n) {
+ for (f = files; f != NULL; f = f->n_next) {
+ if (filev) {
+ for (cpp = filev; *cpp; cpp++)
+ if (strcmp(f->n_name, *cpp) == 0)
+ goto found2;
+ continue;
+ }
+ found2:
+ /* ddir & destdir set to zero for default install */
+ if (install(f->n_name, NULL, 0, 0, options) > 0)
+ ++didupdate;
+ }
}
+done:
/*
- * first time cmptime() is called?
+ * Run any commands for the entire cmd
*/
- 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;
+ if (didupdate > 0) {
+ runcmdspecial(cmd, filev, opts);
+ didupdate = 0;
}
- switch (stb.st_mode & S_IFMT) {
- case S_IFREG:
- break;
+ if (!nflag)
+ (void) signal(SIGPIPE, cleanup);
- case S_IFDIR:
- rcmptime(&stb);
- return;
+ for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
+ if (sc->sc_type == NOTIFY)
+ notify(rhost, sc->sc_args, (time_t) 0);
- default:
- error("%s: not a plain file\n", name);
- return;
+ if (!nflag) {
+ register struct linkbuf *nextl, *l;
+
+ for (l = ihead; l != NULL; free((char *)l), l = nextl) {
+ nextl = l->nextp;
+ if (contimedout || IS_ON(opts, DO_IGNLNKS) ||
+ l->count == 0)
+ continue;
+ message(MT_WARNING, "%s: Warning: %d %s link%s",
+ l->pathname, abs(l->count),
+ (l->count > 0) ? "missing" : "extra",
+ (l->count == 1) ? "" : "s");
+ }
+ ihead = NULL;
+ }
+}
+
+okname(name)
+ register char *name;
+{
+ register char *cp = name;
+ register int c, isbad;
+
+ for (isbad = FALSE; *cp && !isbad; ++cp) {
+ c = *cp;
+ if (c & 0200)
+ isbad = TRUE;
+ if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
+ isbad = TRUE;
}
- if (stb.st_mtime > lastmod)
- log(tfp, "new: %s\n", name);
+ if (isbad) {
+ error("Invalid user name \"%s\"\n", name);
+ return(0);
+ }
+ return(1);
}
-static void
-rcmptime(st)
+static void rcmptime(st, sbcmds, env)
struct stat *st;
+ struct subcmd *sbcmds;
+ char **env;
{
register DIR *d;
- register struct direct *dp;
+ register DIRENTRY *dp;
register char *cp;
- char *otp;
+ char *optarget;
int len;
- if (debug)
- printf("rcmptime(%x)\n", st);
+ debugmsg(DM_CALL, "rcmptime(%x) start", st);
- if ((d = opendir(target)) == NULL) {
- error("%s: %s\n", target, strerror(errno));
+ if ((d = opendir((char *) target)) == NULL) {
+ error("%s: open directory failed: %s", target, SYSERR);
return;
}
- otp = tp;
- len = tp - target;
+ optarget = ptarget;
+ len = ptarget - target;
while (dp = readdir(d)) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
- if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+ if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) {
error("%s/%s: Name too long\n", target, dp->d_name);
continue;
}
- tp = otp;
- *tp++ = '/';
+ ptarget = optarget;
+ *ptarget++ = '/';
cp = dp->d_name;
- while (*tp++ = *cp++)
+ while (*ptarget++ = *cp++)
;
- tp--;
- cmptime(target);
+ ptarget--;
+ cmptime(target, sbcmds, env);
}
- closedir(d);
- tp = otp;
- *tp = '\0';
+ (void) closedir((DIR *) d);
+ ptarget = optarget;
+ *ptarget = '\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.
+ * Compare the mtime of file to the list of time stamps.
*/
-static void
-notify(file, rhost, to, lmod)
- char *file, *rhost;
- register struct namelist *to;
- time_t lmod;
+static void cmptime(name, sbcmds, env)
+ char *name;
+ struct subcmd *sbcmds;
+ char **env;
{
- register int fd, len;
+ struct subcmd *sc;
struct stat stb;
- FILE *pf;
+ int inlist();
- if ((options & VERIFY) || to == NULL)
- return;
- if (!qflag) {
- printf("notify ");
- if (rhost)
- printf("@%s ", rhost);
- prnames(to);
- }
- if (nflag)
- return;
+ debugmsg(DM_CALL, "cmptime(%s)", name);
- 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);
+ if (except(name))
return;
- }
- if (stb.st_size == 0) {
- (void) close(fd);
+
+ if (nflag) {
+ (void) printf("comparing dates: %s\n", name);
return;
}
+
/*
- * Create a pipe to mailling program.
+ * first time cmptime() is called?
*/
- (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);
+ if (ptarget == NULL) {
+ if (exptilde(target, name) == NULL)
+ return;
+ ptarget = name = target;
+ while (*ptarget)
+ ptarget++;
+ }
+ if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
+ error("%s: cannot access file: %s", name, SYSERR);
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;
+
+ if (S_ISDIR(stb.st_mode)) {
+ rcmptime(&stb, sbcmds, env);
+ return;
+ } else if (!S_ISREG(stb.st_mode)) {
+ error("%s: not a plain file", name);
+ return;
}
- 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);
+ if (stb.st_mtime > lastmod) {
+ message(MT_INFO, "%s: file is newer", name);
+ for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
+ char buf[BUFSIZ];
+ if (sc->sc_type != SPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, name))
+ continue;
+ (void) sprintf(buf, "%s=%s;%s",
+ E_LOCFILE, name, sc->sc_name);
+ message(MT_CHANGE, "special \"%s\"", buf);
+ if (*env) {
+ int len = strlen(*env);
+ *env = (char *) xrealloc(*env, len +
+ strlen(name) + 2);
+ *env[len] = CNULL;
+ (void) strcat(*env, name);
+ (void) strcat(*env, ":");
+ }
+ if (IS_ON(options, DO_VERIFY))
+ continue;
+
+ runcommand(buf);
+ }
+ }
}
/*
- * Return true if name is in the list.
+ * Process commands for comparing files to time stamp files.
*/
-int
-inlist(list, file)
- struct namelist *list;
- char *file;
+static void dodcolon(cmd, filev)
+ struct cmd *cmd;
+ char **filev;
{
- register struct namelist *nl;
+ register struct subcmd *sc;
+ register struct namelist *f;
+ register char *cp, **cpp;
+ struct stat stb;
+ struct namelist *files = cmd->c_files;
+ struct subcmd *sbcmds = cmd->c_cmds;
+ char *env, *stamp = cmd->c_name;
- for (nl = list; nl != NULL; nl = nl->n_next)
- if (!strcmp(file, nl->n_name))
- return(1);
- return(0);
+ debugmsg(DM_CALL, "dodcolon()");
+
+ if (files == NULL) {
+ error("No files to be updated for target \"%s\"",
+ cmd->c_label);
+ return;
+ }
+ if (stat(stamp, &stb) < 0) {
+ error("%s: stat failed: %s", stamp, SYSERR);
+ return;
+ }
+
+ debugmsg(DM_MISC, "%s: mtime %d\n", stamp, stb.st_mtime);
+
+ env = NULL;
+ for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type == CMDSPECIAL) {
+ env = (char *) xmalloc(sizeof(E_FILES) + 3);
+ (void) sprintf(env, "%s='", E_FILES);
+ break;
+ }
+ }
+
+ subcmds = sbcmds;
+ filelist = files;
+
+ lastmod = stb.st_mtime;
+ if (!nflag && !IS_ON(options, DO_VERIFY))
+ /*
+ * Set atime and mtime to current time
+ */
+ (void) setfiletime(stamp, (time_t) 0, (time_t) 0);
+
+ 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:
+ ptarget = NULL;
+ cmptime(f->n_name, sbcmds, &env);
+ }
+
+ for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type == NOTIFY)
+ notify((char *)NULL, sc->sc_args, (time_t)lastmod);
+ else if (sc->sc_type == CMDSPECIAL && env) {
+ char *p;
+ int len = strlen(env);
+
+ env = xrealloc(env,
+ len + strlen(sc->sc_name) + 2);
+ env[len] = CNULL;
+ if (*(p = &env[len - 1]) == ':')
+ *p = CNULL;
+ (void) strcat(env, "';");
+ (void) strcat(env, sc->sc_name);
+ message(MT_CHANGE, "cmdspecial \"%s\"", env);
+ if (!nflag && IS_OFF(options, DO_VERIFY))
+ runcommand(env);
+ (void) free(env);
+ env = NULL; /* so cmdspecial is only called once */
+ }
+ }
+ if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
+ (void) unlink(cp);
}
/*
* Return TRUE if file is in the exception list.
*/
-int
-except(file)
+extern int except(file)
char *file;
{
register struct subcmd *sc;
register struct namelist *nl;
- if (debug)
- printf("except(%s)\n", file);
+ debugmsg(DM_CALL, "except(%s)", 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))
+ if (sc->sc_type == EXCEPT) {
+ for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
+ if (strcmp(file, nl->n_name) == 0)
return(1);
- continue;
+ continue;
+ }
+ if (sc->sc_type == PATTERN) {
+ for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
+ char *cp, *re_comp();
+
+ if ((cp = re_comp(nl->n_name)) != NULL) {
+ error("Regex error \"%s\" for \"%s\".",
+ cp, nl->n_name);
+ return(0);
+ }
+ if (re_exec(file) > 0)
+ return(1);
}
- re_comp(nl->n_name);
- if (re_exec(file) > 0)
- return(1);
}
}
return(0);
}
-char *
-colon(cp)
- register char *cp;
+/*
+ * Do a specific command for a specific host
+ */
+static void docmdhost(cmd, filev)
+ struct cmd *cmd;
+ char **filev;
{
+ checkcmd(cmd);
- while (*cp) {
- if (*cp == ':')
- return(cp);
- if (*cp == '/')
- return(0);
- cp++;
+ /*
+ * If we're multi-threaded and we're the parent, spawn a
+ * new child process.
+ */
+ if (do_fork && !amchild) {
+ int pid;
+
+ /*
+ * If we're at maxchildren, wait for number of active
+ * children to fall below max number of children.
+ */
+ while (activechildren >= maxchildren)
+ waitup();
+
+ pid = spawn(cmd, cmds);
+ if (pid == 0)
+ /* Child */
+ amchild = 1;
+ else
+ /* Parent */
+ return;
+ }
+
+ /*
+ * Disable NFS checks
+ */
+ if (cmd->c_flags & CMD_NOCHKNFS)
+ FLAG_OFF(options, DO_CHKNFS);
+
+ if (!nflag) {
+ currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
+#if defined(SETARGS)
+ setproctitle("update %s", currenthost);
+#endif /* SETARGS */
+ }
+
+ switch (cmd->c_type) {
+ case ARROW:
+ doarrow(cmd, filev);
+ break;
+ case DCOLON:
+ dodcolon(cmd, filev);
+ break;
+ default:
+ fatalerr("illegal command type %d", cmd->c_type);
+ }
+}
+
+/*
+ * Do a specific command (cmd)
+ */
+static void docmd(cmd, argc, argv)
+ struct cmd *cmd;
+ int argc;
+ char **argv;
+{
+ register struct namelist *f;
+ register int i;
+
+ if (argc) {
+ for (i = 0; i < argc; i++) {
+ if (cmd->c_label != NULL &&
+ strcmp(cmd->c_label, argv[i]) == 0) {
+ docmdhost(cmd, (char **) NULL);
+ return;
+ }
+ for (f = cmd->c_files; f != NULL; f = f->n_next)
+ if (strcmp(f->n_name, argv[i]) == 0) {
+ docmdhost(cmd, &argv[i]);
+ return;
+ }
+ }
+ } else
+ docmdhost(cmd, (char **) NULL);
+}
+
+/*
+ *
+ * Multiple hosts are updated at once via a "ring" of at most
+ * maxchildren rdist processes. The parent rdist fork()'s a child
+ * for a given host. That child will update the given target files
+ * and then continue scanning through the remaining targets looking
+ * for more work for a given host. Meanwhile, the parent gets the
+ * next target command and makes sure that it hasn't encountered
+ * that host yet since the children are responsible for everything
+ * for that host. If no children have done this host, then check
+ * to see if the number of active proc's is less than maxchildren.
+ * If so, then spawn a new child for that host. Otherwise, wait
+ * for a child to finish.
+ *
+ */
+
+/*
+ * Do the commands in cmds (initialized by yyparse).
+ */
+extern void docmds(hostlist, argc, argv)
+ struct namelist *hostlist;
+ int argc;
+ char **argv;
+{
+ register struct cmd *c;
+ register char *cp;
+ register int i;
+
+ (void) signal(SIGHUP, sighandler);
+ (void) signal(SIGINT, sighandler);
+ (void) signal(SIGQUIT, sighandler);
+ (void) signal(SIGTERM, sighandler);
+
+ if (!nflag)
+ mysetlinebuf(stdout); /* Make output (mostly) clean */
+
+#if defined(USE_STATDB)
+ if (!nflag && (dostatdb || juststatdb)) {
+ extern long reccount;
+ message(MT_INFO, "Making stat database [%s] ... \n",
+ gettimestr());
+ if (mkstatdb() < 0)
+ error("Warning: Make stat database failed.");
+ message(MT_INFO,
+ "Stat database created: %d files stored [%s].\n",
+ reccount, gettimestr());
+ if (juststatdb)
+ return;
+ }
+#endif /* USE_STATDB */
+
+ /*
+ * Print errors for any command line targets we didn't find.
+ * If any errors are found, return to main() which will then exit.
+ */
+ for (i = 0; i < argc; i++) {
+ int found;
+
+ for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
+ if (c->c_label && argv[i] &&
+ strcmp(c->c_label, argv[i]) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ error("Label \"%s\" is not defined in the distfile.",
+ argv[i]);
+ }
+ if (nerrs)
+ return;
+
+ /*
+ * Main command loop. Loop through all the commands.
+ */
+ for (c = cmds; c != NULL; c = c->c_next) {
+ checkcmd(c);
+ if (do_fork) {
+ /*
+ * Let the children take care of their assigned host
+ */
+ if (amchild) {
+ if (strcmp(c->c_name, currenthost) != 0)
+ continue;
+ } else if (c->c_flags & CMD_ASSIGNED) {
+ /* This cmd has been previously assigned */
+ debugmsg(DM_MISC, "prev assigned: %s\n",
+ c->c_name);
+ continue;
+ }
+ }
+
+ if (hostlist) {
+ /* Do specific hosts as specified on command line */
+ register struct namelist *nlptr;
+
+ for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
+ /*
+ * Try an exact match and then a match
+ * without '@' (if present).
+ */
+ if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
+ ((cp = strchr(c->c_name, '@')) &&
+ strcmp(++cp, nlptr->n_name) == 0))
+ docmd(c, argc, argv);
+ continue;
+ } else
+ /* Do all of the command */
+ docmd(c, argc, argv);
+ }
+
+ if (do_fork) {
+ /*
+ * We're multi-threaded, so do appropriate shutdown
+ * actions based on whether we're the parent or a child.
+ */
+ if (amchild) {
+ if (!IS_ON(options, DO_QUIET))
+ message(MT_VERBOSE, "updating of %s finished",
+ currenthost);
+ closeconn();
+ cleanup();
+ exit(nerrs);
+ }
+
+ /*
+ * Wait for all remaining active children to finish
+ */
+ while (activechildren > 0) {
+ debugmsg(DM_MISC,
+ "Waiting for %d children to finish.\n",
+ activechildren);
+ waitup();
+ }
+ } else if (!nflag) {
+ /*
+ * We're single-threaded so close down current connection
+ */
+ closeconn();
+ cleanup();
}
- return(0);
}
diff --git a/usr.bin/rdist/expand.c b/usr.bin/rdist/expand.c
index 6b917331e3c..82aec43d9e3 100644
--- a/usr.bin/rdist/expand.c
+++ b/usr.bin/rdist/expand.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1983 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
@@ -32,45 +32,82 @@
*/
#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 $";
+static char RCSid[] =
+"$Id: expand.c,v 1.2 1996/02/03 12:12:23 dm Exp $";
+
+static char sccsid[] = "@(#)expand.c 5.2 (Berkeley) 3/28/86";
+
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
#endif /* not lint */
#include "defs.h"
-#define GAVSIZ NCARGS / 6
-#define LC '{'
-#define RC '}'
+#define MAXEARGS 2048
+#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;
+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 expany; /* any expansions done? */
+char *entp;
+char **sortbase;
+char *argvbuf[MAXEARGS];
+
+static int argcmp();
+void expstr();
+void expsh();
+void matchdir();
#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 *));
+static void Cat(s1, s2) /* quote in s1 and s2 */
+ register u_char *s1, *s2;
+{
+ register char *cp;
+ int len = strlen((char *)s1) + strlen((char *)s2) + 2;
+
+ if ((eargc + 1) >= MAXEARGS) {
+ yyerror("Too many names");
+ return;
+ }
+
+ eargv[++eargc] = (char *) NULL;
+ eargv[eargc - 1] = cp = xmalloc(len);
+
+ do {
+ if (*s1 == QUOTECHAR)
+ s1++;
+ } while (*cp++ = *s1++);
+ cp--;
+ do {
+ if (*s2 == QUOTECHAR)
+ s2++;
+ } while (*cp++ = *s2++);
+}
+
+static void addpath(c)
+ char c;
+{
+ if (pathp >= lastpathp) {
+ yyerror("Pathname too long");
+ return;
+ } else {
+ *pathp++ = c;
+ *pathp = CNULL;
+ }
+}
/*
* Take a list of names and expand any macros, etc.
@@ -82,49 +119,41 @@ static int smatch __P((char *, char *));
* Major portions of this were snarfed from csh/sh.glob.c.
*/
struct namelist *
-expand(list, wh)
+expand(list, wh) /* quote in list->n_name */
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;
+ if (debug)
+ debugmsg(DM_CALL, "expand(%x, %d) start, list = %s",
+ list, wh, getnlstr(list));
- for (nl = list; nl != NULL; nl = nl->n_next)
- for (cp = nl->n_name; *cp; cp++)
- *cp = *cp & TRIM;
- return(list);
- }
+ if (wh == 0)
+ fatalerr("expand() contains invalid 'wh' argument.");
which = wh;
path = tpathp = pathp = pathbuf;
- *pathp = '\0';
+ *pathp = CNULL;
lastpathp = &path[sizeof pathbuf - 2];
tilde = "";
eargc = 0;
eargv = sortbase = argvbuf;
- *eargv = 0;
- nleft = NCARGS - 4;
+ *eargv = (char *) NULL;
+
/*
* Walk the name list and expand names into eargv[];
*/
for (nl = list; nl != NULL; nl = nl->n_next)
- expstr(nl->n_name);
+ expstr((u_char *)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 = makenl((char *)NULL);
nl->n_name = eargv[n];
if (prev == NULL)
list = prev = nl;
@@ -133,103 +162,151 @@ expand(list, wh)
prev = nl;
}
}
- if (debug) {
- printf("expanded list = ");
- prnames(list);
- }
+
return(list);
}
-static void
-expstr(s)
- char *s;
+/*
+ * xstrchr() is a version of strchr() that
+ * handles u_char buffers.
+ */
+u_char *xstrchr(str, ch)
+ u_char *str;
+ int ch;
+{
+ register u_char *cp;
+
+ for (cp = str; cp && *cp != CNULL; ++cp)
+ if (ch == *cp)
+ return(cp);
+
+ return((u_char *)NULL);
+}
+
+void expstr(s)
+ u_char *s;
{
- register char *cp, *cp1;
+ register u_char *cp, *cp1;
register struct namelist *tp;
- char *tail;
- char buf[BUFSIZ];
+ u_char *tail;
+ u_char ebuf[BUFSIZ];
+ u_char varbuff[BUFSIZ];
int savec, oeargc;
- extern char homedir[];
+ extern char *homedir;
- if (s == NULL || *s == '\0')
+ if (s == NULL || *s == CNULL)
return;
- if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
- *cp++ = '\0';
- if (*cp == '\0') {
+ /*
+ * Remove quoted characters
+ */
+ if (IS_ON(which, E_VARS)) {
+ if ((int)strlen((char *)s) > sizeof(varbuff)) {
+ yyerror("Variable is too large.");
+ return;
+ }
+ for (cp = s, cp1 = varbuff; cp && *cp; ++cp) {
+ /*
+ * remove quoted character if the next
+ * character is not $
+ */
+ if (*cp == QUOTECHAR && *(cp+1) != '$')
+ ++cp;
+ else
+ *cp1++ = *cp;
+ }
+ *cp1 = CNULL;
+ s = varbuff;
+ }
+
+ /*
+ * Consider string 's' a variable that should be expanded if
+ * there is a '$' in 's' that is not quoted.
+ */
+ if (IS_ON(which, E_VARS) &&
+ ((cp = xstrchr(s, '$')) && !(cp > s && *(cp-1) == QUOTECHAR))) {
+ *cp++ = CNULL;
+ if (*cp == CNULL) {
yyerror("no variable name after '$'");
return;
}
if (*cp == LC) {
cp++;
- if ((tail = index(cp, RC)) == NULL) {
- yyerror("unmatched '{'");
- return;
+ for (cp1 = cp; ; cp1 = tail + 1) {
+ if ((tail = xstrchr(cp1, RC)) == NULL) {
+ yyerror("unmatched '{'");
+ return;
+ }
+ if (tail[-1] != QUOTECHAR) break;
}
- *tail++ = savec = '\0';
- if (*cp == '\0') {
+ *tail++ = savec = CNULL;
+ if (*cp == CNULL) {
yyerror("no variable name after '$'");
return;
}
} else {
tail = cp + 1;
savec = *tail;
- *tail = '\0';
+ *tail = CNULL;
}
- tp = lookup(cp, NULL, 0);
- if (savec != '\0')
+ tp = lookup((char *)cp, LOOKUP, (struct namelist *)NULL);
+ if (savec != CNULL)
*tail = savec;
if (tp != NULL) {
for (; tp != NULL; tp = tp->n_next) {
- sprintf(buf, "%s%s%s", s, tp->n_name, tail);
- expstr(buf);
+ (void) sprintf((char *)ebuf,
+ "%s%s%s", s, tp->n_name, tail);
+ expstr(ebuf);
}
return;
}
- sprintf(buf, "%s%s", s, tail);
- expstr(buf);
+ (void) sprintf((char *)ebuf, "%s%s", s, tail);
+ expstr(ebuf);
return;
}
- if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
- Cat(s, "");
+ if ((which & ~E_VARS) == 0 || !strcmp((char *)s, "{") ||
+ !strcmp((char *)s, "{}")) {
+ Cat(s, (u_char *)"");
sort();
return;
}
if (*s == '~') {
cp = ++s;
- if (*cp == '\0' || *cp == '/') {
+ if (*cp == CNULL || *cp == '/') {
tilde = "~";
- cp1 = homedir;
+ cp1 = (u_char *)homedir;
} else {
- tilde = cp1 = buf;
+ tilde = (char *)(cp1 = ebuf);
*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);
+ *cp1 = CNULL;
+ if (pw == NULL || strcmp(pw->pw_name,
+ (char *)ebuf+1) != 0) {
+ if ((pw = getpwnam((char *)ebuf+1)) == NULL) {
+ strcat((char *)ebuf,
+ ": unknown user name");
+ yyerror((char *)ebuf+1);
return;
}
}
- cp1 = pw->pw_dir;
+ cp1 = (u_char *)pw->pw_dir;
s = cp;
}
- for (cp = path; *cp++ = *cp1++; )
+ for (cp = (u_char *)path; *cp++ = *cp1++; )
;
- tpathp = pathp = cp - 1;
+ tpathp = pathp = (char *)cp - 1;
} else {
tpathp = pathp = path;
tilde = "";
}
- *pathp = '\0';
+ *pathp = CNULL;
if (!(which & E_SHELL)) {
if (which & E_TILDE)
- Cat(path, s);
+ Cat((u_char *)path, s);
else
- Cat(tilde, s);
+ Cat((u_char *)tilde, s);
sort();
return;
}
@@ -237,42 +314,42 @@ expstr(s)
expany = 0;
expsh(s);
if (eargc == oeargc)
- Cat(s, ""); /* "nonomatch" is set */
+ Cat(s, (u_char *)""); /* "nonomatch" is set */
sort();
}
-static int
+static
argcmp(a1, a2)
- const void *a1, *a2;
+ char **a1, **a2;
{
- return (strcmp(*(char **)a1, *(char **)a2));
+ return (strcmp(*a1, *a2));
}
/*
* If there are any Shell meta characters in the name,
* expand into a list, after searching directory
*/
-static void
-expsh(s)
- char *s;
+void expsh(s) /* quote in s */
+ u_char *s;
{
- register char *cp;
- register char *spathp, *oldcp;
+ register u_char *cp, *oldcp;
+ register char *spathp;
struct stat stb;
spathp = pathp;
cp = s;
while (!any(*cp, shchars)) {
- if (*cp == '\0') {
+ if (*cp == CNULL) {
if (!expany || stat(path, &stb) >= 0) {
if (which & E_TILDE)
- Cat(path, "");
+ Cat((u_char *)path, (u_char *)"");
else
- Cat(tilde, tpathp);
+ Cat((u_char *)tilde, (u_char *)tpathp);
}
goto endit;
}
+ if (*cp == QUOTECHAR) cp++;
addpath(*cp++);
}
oldcp = cp;
@@ -280,23 +357,22 @@ expsh(s)
cp--, pathp--;
if (*cp == '/')
cp++, pathp++;
- *pathp = '\0';
+ *pathp = CNULL;
if (*oldcp == '{') {
- execbrc(cp, NULL);
+ (void) execbrc(cp, (u_char *)NULL);
return;
}
- matchdir(cp);
+ matchdir((char *)cp);
endit:
pathp = spathp;
- *pathp = '\0';
+ *pathp = CNULL;
}
-static void
-matchdir(pattern)
+void matchdir(pattern) /* quote in pattern */
char *pattern;
{
struct stat stb;
- register struct direct *dp;
+ register DIRENTRY *dp;
DIR *dirp;
dirp = opendir(path);
@@ -307,18 +383,18 @@ matchdir(pattern)
}
if (fstat(dirp->dd_fd, &stb) < 0)
goto patherr1;
- if (!ISDIR(stb.st_mode)) {
+ if (!S_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);
+ Cat((u_char *)path, (u_char *)dp->d_name);
else {
- strcpy(pathp, dp->d_name);
- Cat(tilde, tpathp);
- *pathp = '\0';
+ (void) strcpy(pathp, dp->d_name);
+ Cat((u_char *)tilde, (u_char *)tpathp);
+ *pathp = CNULL;
}
}
closedir(dirp);
@@ -327,22 +403,23 @@ matchdir(pattern)
patherr1:
closedir(dirp);
patherr2:
- strcat(path, ": ");
- strcat(path, strerror(errno));
+ (void) strcat(path, ": ");
+ (void) strcat(path, SYSERR);
yyerror(path);
}
-static int
-execbrc(p, s)
- char *p, *s;
+execbrc(p, s) /* quote in p */
+ u_char *p, *s;
{
- char restbuf[BUFSIZ + 2];
- register char *pe, *pm, *pl;
+ u_char restbuf[BUFSIZ + 2];
+ register u_char *pe, *pm, *pl;
int brclev = 0;
- char *lm, savec, *spathp;
+ u_char *lm, savec;
+ char *spathp;
for (lm = restbuf; *p != '{'; *lm++ = *p++)
- continue;
+ if (*p == QUOTECHAR) *lm++ = *p++;
+
for (pe = ++p; *pe; pe++)
switch (*pe) {
@@ -358,10 +435,14 @@ execbrc(p, s)
case '[':
for (pe++; *pe && *pe != ']'; pe++)
- continue;
+ if (*p == QUOTECHAR) pe++;
if (!*pe)
yyerror("Missing ']'");
continue;
+
+ case QUOTECHAR: /* skip this character */
+ pe++;
+ continue;
}
pend:
if (brclev || !*pe) {
@@ -369,7 +450,8 @@ pend:
return (0);
}
for (pl = pm = p; pm <= pe; pm++)
- switch (*pm & (QUOTE|TRIM)) {
+ /* the strip code was a noop */
+ switch (*pm) {
case '{':
brclev++;
@@ -388,15 +470,15 @@ pend:
doit:
savec = *pm;
*pm = 0;
- strcpy(lm, pl);
- strcat(restbuf, pe + 1);
+ (void) strcpy((char *)lm, (char *)pl);
+ (void) strcat((char *)restbuf, (char *)pe + 1);
*pm = savec;
if (s == 0) {
spathp = pathp;
expsh(restbuf);
pathp = spathp;
*pathp = 0;
- } else if (amatch(s, restbuf))
+ } else if (amatch((char *)s, restbuf))
return (1);
sort();
pl = pm + 1;
@@ -404,16 +486,19 @@ doit:
case '[':
for (pm++; *pm && *pm != ']'; pm++)
- continue;
+ if (*pm == QUOTECHAR) pm++;
if (!*pm)
yyerror("Missing ']'");
continue;
+
+ case QUOTECHAR: /* skip one character */
+ pm++;
+ continue;
}
return (0);
}
-static int
-match(s, p)
+match(s, p) /* quote in p */
char *s, *p;
{
register int c;
@@ -430,9 +515,9 @@ match(s, p)
return (c);
}
-static int
-amatch(s, p)
- register char *s, *p;
+amatch(s, p) /* quote in p */
+ register char *s;
+ register u_char *p;
{
register int scc;
int ok, lc;
@@ -442,11 +527,11 @@ amatch(s, p)
expany = 1;
for (;;) {
- scc = *s++ & TRIM;
+ scc = *s++;
switch (c = *p++) {
case '{':
- return (execbrc(p - 1, s - 1));
+ return (execbrc((u_char *)p - 1, (u_char *)s - 1));
case '[':
ok = 0;
@@ -457,8 +542,9 @@ amatch(s, p)
break;
return (0);
}
+ if (cc == QUOTECHAR) cc = *p++;
if (cc == '-') {
- if (lc <= scc && scc <= *p++)
+ if (lc <= scc && scc <= (int)*p++)
ok++;
} else
if (scc == (lc = cc))
@@ -482,16 +568,16 @@ amatch(s, p)
return (1);
return (0);
- case '\0':
- return (scc == '\0');
+ case CNULL:
+ return (scc == CNULL);
default:
- if ((c & TRIM) != scc)
+ if (c != scc)
return (0);
continue;
case '?':
- if (scc == '\0')
+ if (scc == CNULL)
return (0);
continue;
@@ -504,164 +590,19 @@ slash:
while (*s)
addpath(*s++);
addpath('/');
- if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
- if (*p == '\0') {
+ if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode))
+ if (*p == CNULL) {
if (which & E_TILDE)
- Cat(path, "");
+ Cat((u_char *)path,
+ (u_char *)"");
else
- Cat(tilde, tpathp);
+ Cat((u_char *)tilde,
+ (u_char *)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);
+ *pathp = CNULL;
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
index 1eb190f6a4b..9e4d4515e1a 100644
--- a/usr.bin/rdist/gram.y
+++ b/usr.bin/rdist/gram.y
@@ -1,7 +1,8 @@
%{
/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993 Michael A. Cooper
+ * Copyright (c) 1993 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
@@ -33,48 +34,58 @@
*/
#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 $";
+static char RCSid[] =
+"$Id: gram.y,v 1.2 1996/02/03 12:12:26 dm Exp $";
+
+static char *sccsid = "@(#)gram.y 5.2 (Berkeley) 85/06/21";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
#endif /* not lint */
+/*
+ * Tell defs.h not to include y.tab.h
+ */
+#ifndef yacc
+#define yacc
+#endif
+
#include "defs.h"
+static struct namelist *addnl(), *subnl(), *andnl();
struct cmd *cmds = NULL;
struct cmd *last_cmd;
struct namelist *last_n;
struct subcmd *last_sc;
-
-static char *makestr __P((char *));
+int parendepth = 0;
%}
-%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
+%term ARROW 1
+%term COLON 2
+%term DCOLON 3
+%term NAME 4
+%term STRING 5
+%term INSTALL 6
+%term NOTIFY 7
+%term EXCEPT 8
+%term PATTERN 9
+%term SPECIAL 10
+%term CMDSPECIAL 11
+%term OPTION 12
%union {
- int intval;
- char *string;
- struct subcmd *subcmd;
- struct namelist *namel;
+ opt_t optval;
+ char *string;
+ struct subcmd *subcmd;
+ struct namelist *namel;
}
-%type <intval> OPTION, options
+%type <optval> OPTION, options
%type <string> NAME, STRING
-%type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
-%type <namel> namelist, names, opt_namelist
+%type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, CMDSPECIAL, cmdlist, cmd
+%type <namel> namelist, names, opt_namelist nlist
%%
@@ -82,17 +93,17 @@ file: /* VOID */
| file command
;
-command: NAME EQUAL namelist = {
+command: NAME '=' namelist = {
(void) lookup($1, INSERT, $3);
}
| namelist ARROW namelist cmdlist = {
- insert(NULL, $1, $3, $4);
+ insert((char *)NULL, $1, $3, $4);
}
| NAME COLON namelist ARROW namelist cmdlist = {
insert($1, $3, $5, $6);
}
| namelist DCOLON NAME cmdlist = {
- append(NULL, $1, $3, $4);
+ append((char *)NULL, $1, $3, $4);
}
| NAME COLON namelist DCOLON NAME cmdlist = {
append($1, $3, $5, $6);
@@ -100,10 +111,24 @@ command: NAME EQUAL namelist = {
| error
;
-namelist: NAME = {
+namelist: nlist {
+ $$ = $1;
+ }
+ | nlist '-' nlist {
+ $$ = subnl($1, $3);
+ }
+ | nlist '+' nlist {
+ $$ = addnl($1, $3);
+ }
+ | nlist '&' nlist {
+ $$ = andnl($1, $3);
+ }
+ ;
+
+nlist: NAME = {
$$ = makenl($1);
}
- | LP names RP = {
+ | '(' names ')' = {
$$ = $2;
}
;
@@ -136,7 +161,7 @@ cmdlist: /* VOID */ {
}
;
-cmd: INSTALL options opt_namelist SM = {
+cmd: INSTALL options opt_namelist ';' = {
register struct namelist *nl;
$1->sc_options = $2 | options;
@@ -152,17 +177,17 @@ cmd: INSTALL options opt_namelist SM = {
}
$$ = $1;
}
- | NOTIFY namelist SM = {
+ | NOTIFY namelist ';' = {
if ($2 != NULL)
$1->sc_args = expand($2, E_VARS);
$$ = $1;
}
- | EXCEPT namelist SM = {
+ | EXCEPT namelist ';' = {
if ($2 != NULL)
$1->sc_args = expand($2, E_ALL);
$$ = $1;
}
- | PATTERN namelist SM = {
+ | PATTERN namelist ';' = {
struct namelist *nl;
char *cp, *re_comp();
@@ -172,7 +197,13 @@ cmd: INSTALL options opt_namelist SM = {
$1->sc_args = expand($2, E_VARS);
$$ = $1;
}
- | SPECIAL opt_namelist STRING SM = {
+ | SPECIAL opt_namelist STRING ';' = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_ALL);
+ $1->sc_name = $3;
+ $$ = $1;
+ }
+ | CMDSPECIAL opt_namelist STRING ';' = {
if ($2 != NULL)
$1->sc_args = expand($2, E_ALL);
$1->sc_name = $3;
@@ -201,7 +232,6 @@ opt_namelist: /* VOID */ = {
int yylineno = 1;
extern FILE *fin;
-int
yylex()
{
static char yytext[INMAX];
@@ -226,21 +256,23 @@ again:
goto again;
case '=': /* EQUAL */
- return(EQUAL);
+ case ';': /* SM */
+ case '+':
+ case '&':
+ return(c);
case '(': /* LP */
- return(LP);
+ ++parendepth;
+ return(c);
case ')': /* RP */
- return(RP);
-
- case ';': /* SM */
- return(SM);
+ --parendepth;
+ return(c);
case '-': /* -> */
if ((c = getc(fin)) == '>')
return(ARROW);
- ungetc(c, fin);
+ (void) ungetc(c, fin);
c = '-';
break;
@@ -276,7 +308,7 @@ again:
case ':': /* : or :: */
if ((c = getc(fin)) == ':')
return(DCOLON);
- ungetc(c, fin);
+ (void) ungetc(c, fin);
return(COLON);
}
cp1 = yytext;
@@ -289,7 +321,7 @@ again:
if (c == '\\') {
if ((c = getc(fin)) != EOF) {
if (any(c, quotechars))
- c |= QUOTE;
+ *cp1++ = QUOTECHAR;
} else {
*cp1++ = '\\';
break;
@@ -298,41 +330,51 @@ again:
*cp1++ = c;
c = getc(fin);
if (c == EOF || any(c, " \"'\t()=;:\n")) {
- ungetc(c, fin);
+ (void) 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);
+ if (yytext[0] == '-' && yytext[1] == CNULL)
+ return '-';
+ if (yytext[0] == '-' && parendepth <= 0) {
+ opt_t opt = 0;
+ static char ebuf[BUFSIZ];
- case 'h':
- yylval.intval = FOLLOW;
- return(OPTION);
+ switch (yytext[1]) {
+ case 'o':
+ if (parsedistopts(&yytext[2], &opt, TRUE)) {
+ (void) sprintf(ebuf,
+ "Bad distfile options \"%s\".",
+ &yytext[2]);
+ yyerror(ebuf);
+ }
+ break;
- case 'i':
- yylval.intval = IGNLNKS;
- return(OPTION);
+ /*
+ * These options are obsoleted by -o.
+ */
+ case 'b': opt = DO_COMPARE; break;
+ case 'R': opt = DO_REMOVE; break;
+ case 'v': opt = DO_VERIFY; break;
+ case 'w': opt = DO_WHOLE; break;
+ case 'y': opt = DO_YOUNGER; break;
+ case 'h': opt = DO_FOLLOW; break;
+ case 'i': opt = DO_IGNLNKS; break;
+ case 'q': opt = DO_QUIET; break;
+ case 'x': opt = DO_NOEXEC; break;
+ case 'N': opt = DO_CHKNFS; break;
+ case 'O': opt = DO_CHKREADONLY; break;
+ case 's': opt = DO_SAVETARGETS; break;
+ case 'r': opt = DO_NODESCEND; break;
+
+ default:
+ (void) sprintf(ebuf, "Unknown option \"%s\".", yytext);
+ yyerror(ebuf);
}
+
+ yylval.optval = opt;
+ return(OPTION);
}
if (!strcmp(yytext, "install"))
c = INSTALL;
@@ -344,6 +386,8 @@ again:
c = PATTERN;
else if (!strcmp(yytext, "special"))
c = SPECIAL;
+ else if (!strcmp(yytext, "cmdspecial"))
+ c = CMDSPECIAL;
else {
yylval.string = makestr(yytext);
return(NAME);
@@ -352,8 +396,11 @@ again:
return(c);
}
-int
-any(c, str)
+/*
+ * XXX We should use strchr(), but most versions can't handle
+ * some of the characters we use.
+ */
+extern int any(c, str)
register int c;
register char *str;
{
@@ -366,18 +413,22 @@ any(c, str)
/*
* 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;
+ register struct namelist *h, *lasth;
+
+ debugmsg(DM_CALL, "insert(%s, %x, %x, %x) start, files = %s",
+ label == NULL ? "(null)" : label,
+ files, hosts, subcmds, getnlstr(files));
files = expand(files, E_VARS|E_SHELL);
hosts = expand(hosts, E_ALL);
- for (h = hosts; h != NULL; free(h), h = h->n_next) {
+ for (h = hosts; h != NULL; lasth = h, h = h->n_next,
+ free((char *)lasth)) {
/*
* Search command list for an update to the same host.
*/
@@ -395,13 +446,12 @@ insert(label, files, hosts, subcmds)
* 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_flags = 0;
nc->c_next = c;
if (prev == NULL)
cmds = nc;
@@ -417,7 +467,6 @@ insert(label, files, hosts, subcmds)
* 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;
@@ -427,8 +476,6 @@ append(label, files, stamp, 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;
@@ -446,30 +493,26 @@ append(label, files, stamp, subcmds)
/*
* Error printing routine in parser.
*/
-void
yyerror(s)
char *s;
{
- ++nerrs;
- fflush(stdout);
- fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
+ error("Error in distfile: line %d: %s", yylineno, s);
}
/*
* Return a copy of the string.
*/
-static char *
+char *
makestr(str)
char *str;
{
- register char *cp, *s;
+ char *cp;
- str = cp = malloc(strlen(s = str) + 1);
+ cp = strdup(str);
if (cp == NULL)
- fatal("ran out of memory\n");
- while (*cp++ = *s++)
- ;
- return(str);
+ fatalerr("ran out of memory");
+
+ return(cp);
}
/*
@@ -481,29 +524,108 @@ makenl(name)
{
register struct namelist *nl;
+ debugmsg(DM_CALL, "makenl(%s)", name == NULL ? "null" : name);
+
nl = ALLOC(namelist);
- if (nl == NULL)
- fatal("ran out of memory\n");
nl->n_name = name;
nl->n_next = NULL;
+
return(nl);
}
+
+/*
+ * Is the name p in the namelist nl?
+ */
+static int
+innl(nl, p)
+ struct namelist *nl;
+ char *p;
+{
+ for ( ; nl; nl = nl->n_next)
+ if (!strcmp(p, nl->n_name))
+ return(1);
+ return(0);
+}
+
+/*
+ * Join two namelists.
+ */
+static struct namelist *
+addnl(n1, n2)
+ struct namelist *n1, *n2;
+{
+ struct namelist *nl, *prev;
+
+ n1 = expand(n1, E_VARS);
+ n2 = expand(n2, E_VARS);
+ for (prev = NULL, nl = NULL; n1; n1 = n1->n_next, prev = nl) {
+ nl = makenl(n1->n_name);
+ nl->n_next = prev;
+ }
+ for (; n2; n2 = n2->n_next)
+ if (!innl(nl, n2->n_name)) {
+ nl = makenl(n2->n_name);
+ nl->n_next = prev;
+ prev = nl;
+ }
+ return(prev);
+}
+
+/*
+ * Copy n1 except for elements that are in n2.
+ */
+static struct namelist *
+subnl(n1, n2)
+ struct namelist *n1, *n2;
+{
+ struct namelist *nl, *prev;
+
+ n1 = expand(n1, E_VARS);
+ n2 = expand(n2, E_VARS);
+ for (prev = NULL; n1; n1 = n1->n_next)
+ if (!innl(n2, n1->n_name)) {
+ nl = makenl(n1->n_name);
+ nl->n_next = prev;
+ prev = nl;
+ }
+ return(prev);
+}
+
+/*
+ * Copy all items of n1 that are also in n2.
+ */
+static struct namelist *
+andnl(n1, n2)
+ struct namelist *n1, *n2;
+{
+ struct namelist *nl, *prev;
+
+ n1 = expand(n1, E_VARS);
+ n2 = expand(n2, E_VARS);
+ for (prev = NULL; n1; n1 = n1->n_next)
+ if (innl(n2, n1->n_name)) {
+ nl = makenl(n1->n_name);
+ nl->n_next = prev;
+ prev = nl;
+ }
+ return(prev);
+}
+
/*
* Make a sub command for lists of variables, commands, etc.
*/
-struct subcmd *
+extern 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/isexec.c b/usr.bin/rdist/isexec.c
new file mode 100644
index 00000000000..27a6ec435ca
--- /dev/null
+++ b/usr.bin/rdist/isexec.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: isexec.c,v 1.1 1996/02/03 12:12:27 dm Exp $";
+
+static char sccsid[] = "@(#)client.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+
+#include "defs.h"
+
+#if EXE_TYPE == EXE_AOUT
+/*
+ * BSD style A.OUT
+ */
+#include <a.out.h>
+
+static int _isexec(fd)
+ int fd;
+{
+ struct exec ehdr;
+
+ if ((read(fd, &ehdr, sizeof(ehdr)) == sizeof(ehdr)) &&
+ !N_BADMAG(ehdr))
+ return(TRUE);
+ else
+ return(FALSE);
+}
+#endif /* EXE_AOUT */
+
+
+#if EXE_TYPE == EXE_ELF_AND_COFF || EXE_TYPE == EXE_ELF
+/*
+ * Elf
+ */
+#include <elf.h>
+#define ISELF(h) (h.e_type == ET_EXEC)
+#endif /* EXE_ELF_AND_COFF || EXE_ELF */
+
+#if EXE_TYPE == EXE_ELF_AND_COFF || EXE_TYPE == EXE_COFF
+
+/*
+ * COFF
+ */
+#if defined(FILEHDR_H)
+#include FILEHDR_H
+#endif /* FILEHDR_H */
+
+#if !defined(ISCOFF)
+
+/*
+ * Stupid AIX
+ */
+#if defined(U802WRMAGIC) && defined(U802ROMAGIC) && defined(U802TOCMAGIC)
+#define ISCOFF(x) (((x)==U802WRMAGIC) || ((x)==U802TOCMAGIC) || \
+ ((x)==U802TOCMAGIC))
+#endif /* U802... */
+/*
+ * Stupid Umax4.3
+ */
+#if defined(NS32GMAGIC) || defined(NS32SMAGIC)
+#define ISCOFF(x) (((x)==NS32GMAGIC) || ((x)==NS32SMAGIC))
+#endif /* NS32 ... */
+
+#endif /* ISCOFF */
+
+#endif /* EXE_TYPE == EXE_ELF_AND_COFF || EXE_TYPE == EXE_COFF */
+
+#if EXE_TYPE == EXE_ELF_AND_COFF
+/*
+ * ELF and COFF
+ */
+typedef union {
+ struct filehdr coffhdr;
+ Elf32_Ehdr elfhdr;
+} hdr_t;
+#endif /* EXE_TYPE == EXE_ELF_AND_COFF */
+
+#if EXE_TYPE == EXE_ELF
+/*
+ * Elf
+ */
+#include <elf.h>
+typedef Elf32_Ehdr hdr_t;
+#endif /* EXE_TYPE == EXE_ELF */
+
+#if EXE_TYPE == EXE_COFF
+/*
+ * COFF
+ */
+
+#if defined(FILEHDR_H)
+#include FILEHDR_H
+#endif /* FILEHDR_H */
+
+typedef struct filehdr hdr_t;
+#endif /* EXE_TYPE == EXE_COFF */
+
+#if EXE_TYPE == EXE_ELF_AND_COFF || EXE_TYPE == EXE_ELF || EXE_TYPE == EXE_COFF
+/*
+ * System V style COFF and System V R4 style ELF
+ */
+static int _isexec(fd)
+ int fd;
+{
+ hdr_t hdr;
+
+ if (read(fd, &hdr, sizeof(hdr)) == sizeof(hdr)) {
+#if EXE_TYPE == EXE_ELF_AND_COFF
+ if (ISELF(hdr.elfhdr) || ISCOFF(hdr.coffhdr.f_magic))
+ return(TRUE);
+#endif
+#if EXE_TYPE == EXE_ELF
+ if (ISELF(hdr))
+ return(TRUE);
+#endif
+#if EXE_TYPE == EXE_COFF
+ if (ISCOFF(hdr.f_magic))
+ return(TRUE);
+#endif
+ }
+
+ return(FALSE);
+}
+#endif /* EXE_ELF_AND_COFF */
+
+
+#if EXE_TYPE == EXE_MACHO
+/*
+ * Mach-O format
+ */
+
+#if defined(NEXTSTEP) && NEXTSTEP >= 3
+# include <mach-o/loader.h>
+#else
+# include <sys/loader.h>
+#endif /* NEXTSTEP */
+
+#ifndef MH_CIGAM
+#define MH_CIGAM 0xcefaedfe
+#endif
+#ifndef FAT_MAGIC
+#define FAT_MAGIC 0xcafebabe
+#endif
+#ifndef FAT_CIGAM
+#define FAT_CIGAM 0xbebafeca
+#endif
+
+static int _isexec(fd)
+ int fd;
+{
+ struct mach_header ehdr;
+
+ if ((read(fd, &ehdr, sizeof(ehdr)) == sizeof(ehdr)) &&
+ (ehdr.magic == MH_MAGIC || ehdr.magic == MH_CIGAM ||
+ ehdr.magic == FAT_MAGIC || ehdr.magic == FAT_CIGAM))
+ return(TRUE);
+ else
+ return(FALSE);
+}
+#endif /* EXE_COFF */
+
+
+#if EXE_TYPE == EXE_HPEXEC
+/*
+ * HP 9000 executable format
+ */
+
+#ifdef hp9000s300
+
+#include <a.out.h>
+#define header exec
+#define ISEXEC(a) ((a.file_type)==EXEC_MAGIC || (a.file_type)==SHARE_MAGIC || \
+ (a.file_type)==DEMAND_MAGIC)
+
+#else /* ! hp9000s300 */
+
+#define ISEXEC(a) ((a)==EXEC_MAGIC || (a)==SHARE_MAGIC || (a)==DEMAND_MAGIC)
+#include <filehdr.h>
+
+#endif /* hp9000s300 */
+
+static int _isexec(fd)
+ int fd;
+{
+ struct header ehdr;
+
+ if ((read(fd, &ehdr, sizeof(ehdr)) == sizeof(ehdr)) &&
+ ISEXEC(ehdr.a_magic))
+ return(TRUE);
+ else
+ return(FALSE);
+}
+#endif /* EXE_HPEXEC */
+
+
+#if !defined(EXE_TYPE)
+/*
+ * Fake _isexec() call for unknown executable formats.
+ */
+static int _isexec(fd)
+ /*ARGSUSED*/
+ int fd;
+{
+ return(FALSE);
+}
+#endif /* !defined(EXE_TYPE) */
+
+/*
+ * Determine whether 'file' is an executable or not.
+ */
+extern int isexec(file, statp)
+ char *file;
+ struct stat *statp;
+{
+ int fd, r;
+
+ /*
+ * Must be a regular file that has some executable mode bit on
+ */
+ if (!S_ISREG(statp->st_mode) ||
+ !(statp->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
+ return(FALSE);
+
+ if ((fd = open(file, O_RDONLY, 0)) < 0)
+ return(FALSE);
+ r = _isexec(fd);
+ (void) close(fd);
+
+ return(r);
+}
+
diff --git a/usr.bin/rdist/lookup.c b/usr.bin/rdist/lookup.c
index 69e15dd6304..1477ac1a938 100644
--- a/usr.bin/rdist/lookup.c
+++ b/usr.bin/rdist/lookup.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1983 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
@@ -32,8 +32,14 @@
*/
#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 $";
+static char RCSid[] =
+"$Id: lookup.c,v 1.2 1996/02/03 12:12:29 dm Exp $";
+
+static char sccsid[] = "@(#)lookup.c 5.1 (Berkeley) 6/6/85";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
#endif /* not lint */
#include "defs.h"
@@ -54,7 +60,6 @@ static struct syment *hashtab[HASHSIZE];
/*
* Define a variable from a command line argument.
*/
-void
define(name)
char *name;
{
@@ -62,10 +67,9 @@ define(name)
register struct namelist *nl;
struct namelist *value;
- if (debug)
- printf("define(%s)\n", name);
+ debugmsg(DM_CALL, "define(%s)", name);
- cp = index(name, '=');
+ cp = strchr(name, '=');
if (cp == NULL)
value = NULL;
else if (cp[1] == '\0') {
@@ -75,6 +79,7 @@ define(name)
*cp++ = '\0';
value = makenl(cp);
} else {
+ value = NULL;
nl = NULL;
*cp++ = '\0';
do
@@ -119,7 +124,7 @@ define(name)
*/
struct namelist *
-lookup(name, action, value)
+lookup(name, action, value) /* %% in name. Ignore quotas in name */
char *name;
int action;
struct namelist *value;
@@ -127,10 +132,9 @@ lookup(name, action, value)
register unsigned n;
register char *cp;
register struct syment *s;
- char buf[256];
+ char ebuf[BUFSIZ];
- if (debug)
- printf("lookup(%s, %d, %x)\n", name, action, value);
+ debugmsg(DM_CALL, "lookup(%s, %d, %x)", name, action, value);
n = 0;
for (cp = name; *cp; )
@@ -142,26 +146,25 @@ lookup(name, action, value)
continue;
if (action != LOOKUP) {
if (action != INSERT || s->s_type != CONST) {
- (void)sprintf(buf, "%s redefined", name);
- yyerror(buf);
+ (void) sprintf(ebuf, "%s redefined", name);
+ yyerror(ebuf);
}
}
return(s->s_value);
}
if (action == LOOKUP) {
- (void)sprintf(buf, "%s undefined", name);
- yyerror(buf);
+ (void) sprintf(ebuf, "%s undefined", name);
+ yyerror(ebuf);
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/message.c b/usr.bin/rdist/message.c
new file mode 100644
index 00000000000..8724bc5095a
--- /dev/null
+++ b/usr.bin/rdist/message.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: message.c,v 1.1 1996/02/03 12:12:32 dm Exp $";
+
+static char sccsid[] = "@(#)common.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* !lint */
+
+/*
+ * Message handling functions for both rdist and rdistd.
+ */
+
+#include "defs.h"
+
+#define MSGBUFSIZ 32*1024
+
+int debug = 0; /* Debugging level */
+int nerrs = 0; /* Number of errors */
+char *tempfile = NULL; /* Name of temporary file */
+
+/*
+ * Message Types
+ */
+MSGTYPE msgtypes[] = {
+ { MT_CHANGE, "change" },
+ { MT_INFO, "info" },
+ { MT_NOTICE, "notice" },
+ { MT_NERROR, "nerror" },
+ { MT_FERROR, "ferror" },
+ { MT_WARNING, "warning" },
+ { MT_VERBOSE, "verbose" },
+ { MT_ALL, "all" },
+ { MT_DEBUG, "debug" },
+ { 0 },
+};
+
+static void msgsendstdout(), msgsendfile(), msgsendsyslog(),
+ msgsendnotify();
+
+/*
+ * Message Facilities
+ */
+MSGFACILITY msgfacility[] = {
+ { MF_STDOUT, "stdout", msgsendstdout },
+ { MF_FILE, "file", msgsendfile },
+ { MF_SYSLOG, "syslog", msgsendsyslog },
+ { MF_NOTIFY, "notify", msgsendnotify },
+ { 0 },
+};
+
+/*
+ * Print message logging usage message
+ */
+extern void msgprusage()
+{
+ register int i, x;
+
+ (void) fprintf(stderr, "\nWhere <msgopt> is of form\n");
+ (void) fprintf(stderr,
+ "\t<facility1>=<type1>,<type2>,...:<facility2>=<type1>,<type2>...\n");
+
+ (void) fprintf(stderr, "Valid <facility> names:");
+
+ for (i = 0; msgfacility[i].mf_name; ++i)
+ (void) fprintf(stderr, " %s", msgfacility[i].mf_name);
+
+ (void) fprintf(stderr, "\nValid <type> names:");
+ for (x = 0; msgtypes[x].mt_name; ++x)
+ (void) fprintf(stderr, " %s", msgtypes[x].mt_name);
+
+ (void) fprintf(stderr, "\n");
+}
+
+/*
+ * Print enabled message logging info
+ */
+extern void msgprconfig()
+{
+ register int i, x;
+ static char buf[MSGBUFSIZ];
+
+ debugmsg(DM_MISC, "Current message logging config:");
+ for (i = 0; msgfacility[i].mf_name; ++i) {
+ (void) sprintf(buf, " %s=", msgfacility[i].mf_name);
+ for (x = 0; msgtypes[x].mt_name; ++x)
+ if (IS_ON(msgfacility[i].mf_msgtypes,
+ msgtypes[x].mt_type)) {
+ if (x > 0)
+ (void) strcat(buf, ",");
+ (void) strcat(buf, msgtypes[x].mt_name);
+ }
+ debugmsg(DM_MISC, "%s", buf);
+ }
+
+}
+
+/*
+ * Get the Message Facility entry "name"
+ */
+static MSGFACILITY *getmsgfac(name)
+ char *name;
+{
+ register int i;
+
+ for (i = 0; msgfacility[i].mf_name; ++i)
+ if (strcasecmp(name, msgfacility[i].mf_name) == 0)
+ return(&msgfacility[i]);
+
+ return((MSGFACILITY *) NULL);
+}
+
+/*
+ * Get the Message Type entry named "name"
+ */
+static MSGTYPE *getmsgtype(name)
+ char *name;
+{
+ register int i;
+
+ for (i = 0; msgtypes[i].mt_name; ++i)
+ if (strcasecmp(name, msgtypes[i].mt_name) == 0)
+ return(&msgtypes[i]);
+
+ return((MSGTYPE *) NULL);
+}
+
+/*
+ * Set Message Type information for Message Facility "msgfac" as
+ * indicated by string "str".
+ */
+static char *setmsgtypes(msgfac, str)
+ MSGFACILITY *msgfac;
+ char *str;
+{
+ static char ebuf[BUFSIZ];
+ register char *cp;
+ register char *strptr, *word;
+ register MSGTYPE *mtp;
+
+ /*
+ * MF_SYSLOG is the only supported message facility for the server
+ */
+ if (isserver && (msgfac->mf_msgfac != MF_SYSLOG &&
+ msgfac->mf_msgfac != MF_FILE)) {
+ (void) sprintf(ebuf,
+ "The \"%s\" message facility cannot be used by the server.",
+ msgfac->mf_name);
+ return(ebuf);
+ }
+
+ strptr = str;
+
+ /*
+ * Do any necessary Message Facility preparation
+ */
+ switch(msgfac->mf_msgfac) {
+ case MF_FILE:
+ /*
+ * The MF_FILE string should look like "<file>=<types>".
+ */
+ if ((cp = strchr(strptr, '=')) == NULL)
+ return(
+ "No file name found for \"file\" message facility");
+ *cp++ = CNULL;
+
+ if ((msgfac->mf_fptr = fopen(strptr, "w")) == NULL)
+ fatalerr("Cannot open log file for writing: %s: %s.",
+ strptr, SYSERR);
+ msgfac->mf_filename = strdup(strptr);
+
+ strptr = cp;
+ break;
+
+ case MF_NOTIFY:
+ break;
+
+ case MF_STDOUT:
+ msgfac->mf_fptr = stdout;
+ break;
+
+ case MF_SYSLOG:
+#if defined(LOG_OPTS)
+#if defined(LOG_FACILITY)
+ openlog(progname, LOG_OPTS, LOG_FACILITY);
+#else
+ openlog(progname, LOG_OPTS);
+#endif /* LOG_FACILITY */
+#endif /* LOG_OPTS */
+ break;
+ }
+
+ /*
+ * Parse each type word
+ */
+ msgfac->mf_msgtypes = 0; /* Start from scratch */
+ while (strptr) {
+ word = strptr;
+ if (cp = strchr(strptr, ','))
+ *cp++ = CNULL;
+ strptr = cp;
+
+ if (mtp = getmsgtype(word)) {
+ msgfac->mf_msgtypes |= mtp->mt_type;
+ /*
+ * XXX This is really a kludge until we add real
+ * control over debugging.
+ */
+ if (!debug && isserver &&
+ strcasecmp(word, "debug") == 0)
+ debug = DM_ALL;
+ } else {
+ (void) sprintf(ebuf, "Message type \"%s\" is invalid.",
+ word);
+ return(ebuf);
+ }
+ }
+
+ return((char *) NULL);
+}
+
+/*
+ * Parse a message logging option string
+ */
+extern char *msgparseopts(msgstr, doset)
+ char *msgstr;
+ int doset;
+{
+ static char ebuf[BUFSIZ], msgbuf[MSGBUFSIZ];
+ register char *cp, *optstr;
+ register char *word;
+ MSGFACILITY *msgfac;
+
+ if (msgstr == NULL)
+ return("NULL message string");
+
+ /* strtok() is harmful */
+ (void) strcpy(msgbuf, msgstr);
+
+ /*
+ * Each <facility>=<types> list is seperated by ":".
+ */
+ for (optstr = strtok(msgbuf, ":"); optstr;
+ optstr = strtok((char *)NULL, ":")) {
+
+ if ((cp = strchr(optstr, '=')) == NULL)
+ return("No '=' found");
+
+ *cp++ = CNULL;
+ word = optstr;
+ if ((int)strlen(word) <= 0)
+ return("No message facility specified");
+ if ((int)strlen(cp) <= 0)
+ return("No message type specified");
+
+ if ((msgfac = getmsgfac(word)) == NULL) {
+ (void) sprintf(ebuf,
+ "%s is not a valid message facility",
+ word);
+ return(ebuf);
+ }
+
+ if (doset) {
+ char *mcp;
+
+ if (mcp = setmsgtypes(msgfac, cp))
+ return(mcp);
+ }
+ }
+
+ if (isserver && debug) {
+ debugmsg(DM_MISC, "%s", getversion());
+ msgprconfig();
+ }
+
+ return((char *) NULL);
+}
+
+/*
+ * Send a message to facility "stdout".
+ * For rdistd, this is really the rdist client.
+ */
+static void msgsendstdout(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ char cmd;
+
+ if (isserver) {
+ if (rem_w < 0 || IS_ON(flags, MT_NOREMOTE))
+ return;
+
+ cmd = CNULL;
+
+ switch(mtype) {
+ case MT_NERROR: cmd = C_ERRMSG; break;
+ case MT_FERROR: cmd = C_FERRMSG; break;
+ case MT_NOTICE: cmd = C_NOTEMSG; break;
+ case MT_REMOTE: cmd = C_LOGMSG; break;
+ }
+
+ if (cmd != CNULL)
+ (void) sendcmd(cmd, "%s", msgbuf);
+ } else {
+ switch(mtype) {
+ case MT_FERROR:
+ case MT_NERROR:
+ if (msgbuf && *msgbuf) {
+ (void) fprintf(stderr, "%s\n", msgbuf);
+ (void) fflush(stderr);
+ }
+ break;
+
+ case MT_DEBUG:
+ /*
+ * Only things that are strictly MT_DEBUG should
+ * be shown.
+ */
+ if (flags != MT_DEBUG)
+ return;
+ case MT_NOTICE:
+ case MT_CHANGE:
+ case MT_INFO:
+ case MT_VERBOSE:
+ case MT_WARNING:
+ if (msgbuf && *msgbuf) {
+ (void) printf("%s\n", msgbuf);
+ (void) fflush(stdout);
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Send a message to facility "syslog"
+ */
+static void msgsendsyslog(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ int syslvl = 0;
+
+ if (!msgbuf || !*msgbuf)
+ return;
+
+ switch(mtype) {
+#if defined(SL_NERROR)
+ case MT_NERROR: syslvl = SL_NERROR; break;
+#endif
+#if defined(SL_FERROR)
+ case MT_FERROR: syslvl = SL_FERROR; break;
+#endif
+#if defined(SL_WARNING)
+ case MT_WARNING: syslvl = SL_WARNING; break;
+#endif
+#if defined(SL_CHANGE)
+ case MT_CHANGE: syslvl = SL_CHANGE; break;
+#endif
+#if defined(SL_INFO)
+ case MT_SYSLOG:
+ case MT_VERBOSE:
+ case MT_INFO: syslvl = SL_INFO; break;
+#endif
+#if defined(SL_NOTICE)
+ case MT_NOTICE: syslvl = SL_NOTICE; break;
+#endif
+#if defined(SL_DEBUG)
+ case MT_DEBUG: syslvl = SL_DEBUG; break;
+#endif
+ }
+
+ if (syslvl)
+ syslog(syslvl, "%s", msgbuf);
+}
+
+/*
+ * Send a message to a "file" facility.
+ */
+static void msgsendfile(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ if (msgfac->mf_fptr == NULL)
+ return;
+
+ if (!msgbuf || !*msgbuf)
+ return;
+
+ (void) fprintf(msgfac->mf_fptr, "%s\n", msgbuf);
+ (void) fflush(msgfac->mf_fptr);
+}
+
+/*
+ * Same method as msgsendfile()
+ */
+static void msgsendnotify(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ if (IS_ON(flags, MT_DEBUG))
+ return;
+
+ if (!msgbuf || !*msgbuf)
+ return;
+
+ if (!msgfac->mf_fptr) {
+ register char *cp;
+ char *getenv();
+
+ /*
+ * Create and open a new temporary file
+ */
+ if ((cp = getenv("TMPDIR")) == (char *) NULL)
+ cp = _PATH_TMP;
+ tempfile = (char *) xmalloc(strlen(cp) + 1 +
+ strlen(_RDIST_TMP) + 2);
+ (void) sprintf(tempfile, "%s/%s", cp, _RDIST_TMP);
+
+ msgfac->mf_filename = tempfile;
+ (void) mktemp(msgfac->mf_filename);
+ if ((msgfac->mf_fptr = fopen(msgfac->mf_filename, "w"))==NULL)
+ fatalerr("Cannot open notify file for writing: %s: %s.",
+ msgfac->mf_filename, SYSERR);
+ debugmsg(DM_MISC, "Created notify temp file '%s'",
+ msgfac->mf_filename);
+ }
+
+ if (msgfac->mf_fptr == NULL)
+ return;
+
+ (void) fprintf(msgfac->mf_fptr, "%s\n", msgbuf);
+ (void) fflush(msgfac->mf_fptr);
+}
+
+/*
+ * Insure currenthost is set to something reasonable.
+ */
+extern void checkhostname()
+{
+ static char mbuf[MAXHOSTNAMELEN];
+ char *cp;
+
+ if (!currenthost) {
+ if (gethostname(mbuf, sizeof(mbuf)) == 0) {
+ if ((cp = strchr(mbuf, '.')) != NULL)
+ *cp = CNULL;
+ currenthost = strdup(mbuf);
+ } else
+ currenthost = "(unknown)";
+ }
+}
+
+/*
+ * Print a message contained in "msgbuf" if a level "lvl" is set.
+ */
+static void _message(flags, msgbuf)
+ int flags;
+ char *msgbuf;
+{
+ register int i, x;
+ register char *cp;
+ static char mbuf[2048];
+
+ if (msgbuf && *msgbuf) {
+ /*
+ * Ensure no stray newlines are present
+ */
+ if (cp = strchr(msgbuf, '\n'))
+ *cp = CNULL;
+
+ checkhostname();
+ if (strncmp(currenthost, msgbuf, strlen(currenthost)) == 0)
+ (void) strcpy(mbuf, msgbuf);
+ else
+ (void) sprintf(mbuf, "%s: %s", currenthost, msgbuf);
+ } else
+ (void) strcpy(mbuf, "");
+
+ /*
+ * Special case for messages that only get
+ * logged to the system log facility
+ */
+ if (IS_ON(flags, MT_SYSLOG)) {
+ msgsendsyslog((MSGFACILITY *)NULL, MT_SYSLOG, flags, mbuf);
+ return;
+ }
+
+ /*
+ * Special cases
+ */
+ if (isserver && IS_ON(flags, MT_REMOTE))
+ msgsendstdout((MSGFACILITY *)NULL, MT_REMOTE, flags, mbuf);
+ else if (isserver && IS_ON(flags, MT_NERROR))
+ msgsendstdout((MSGFACILITY *)NULL, MT_NERROR, flags, mbuf);
+ else if (isserver && IS_ON(flags, MT_FERROR))
+ msgsendstdout((MSGFACILITY *)NULL, MT_FERROR, flags, mbuf);
+ else if (isserver && IS_ON(flags, MT_NOTICE)) {
+ msgsendstdout((MSGFACILITY *)NULL, MT_NOTICE, flags, mbuf);
+ return;
+ }
+
+ /*
+ * For each Message Facility, check each Message Type to see
+ * if the bits in "flags" are set. If so, call the appropriate
+ * Message Facility to dispatch the message.
+ */
+ for (i = 0; msgfacility[i].mf_name; ++i)
+ for (x = 0; msgtypes[x].mt_name; ++x)
+ /*
+ * XXX MT_ALL should not be used directly
+ */
+ if (msgtypes[x].mt_type != MT_ALL &&
+ IS_ON(flags, msgtypes[x].mt_type) &&
+ IS_ON(msgfacility[i].mf_msgtypes,
+ msgtypes[x].mt_type))
+ (*msgfacility[i].mf_sendfunc)(&msgfacility[i],
+ msgtypes[x].mt_type,
+ flags,
+ mbuf);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs front-end to _message()
+ */
+extern void message(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+ int lvl;
+
+ va_start(args);
+ lvl = (int) va_arg(args, int);
+ fmt = (char *) va_arg(args, char *);
+ va_end(args);
+
+ (void) vsprintf(buf, fmt, args);
+
+ _message(lvl, buf);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg front-end to _message()
+ */
+extern void message(int lvl, char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _message(lvl, buf);
+}
+#endif /* ARG_STDARG */
+
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple front-end to _message()
+ */
+/*VARARGS2*/
+extern void message(lvl, fmt, a1, a2, a3, a4, a5)
+ int lvl;
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5);
+
+ _message(lvl, buf);
+}
+#endif /* !ARG_TYPE */
+
+/*
+ * Display a debugging message
+ */
+static void _debugmsg(lvl, buf)
+ int lvl;
+ char *buf;
+{
+ if (IS_ON(debug, lvl))
+ _message(MT_DEBUG, buf);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs front-end to _debugmsg()
+ */
+extern void debugmsg(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+ int lvl;
+
+ va_start(args);
+ lvl = (int) va_arg(args, int);
+ fmt = (char *) va_arg(args, char *);
+ va_end(args);
+
+ (void) vsprintf(buf, fmt, args);
+
+ _debugmsg(lvl, buf);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg front-end to _debugmsg()
+ */
+extern void debugmsg(int lvl, char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _debugmsg(lvl, buf);
+}
+#endif /* ARG_STDARG */
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple front-end to _debugmsg()
+ */
+/*VARARGS2*/
+extern void debugmsg(lvl, fmt, a1, a2, a3, a4, a5)
+ int lvl;
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5);
+
+ _debugmsg(lvl, buf);
+}
+#endif /* ARG_TYPE */
+
+/*
+ * Print an error message
+ */
+static void _error(msg)
+ char *msg;
+{
+ static char buf[MSGBUFSIZ];
+
+ nerrs++;
+ buf[0] = CNULL;
+
+ if (msg) {
+ if (isserver)
+ (void) sprintf(buf, "REMOTE ERROR: %s", msg);
+ else
+ (void) sprintf(buf, "LOCAL ERROR: %s", msg);
+ }
+
+ _message(MT_NERROR, (buf[0]) ? buf : NULL);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs frontend to _error()
+ */
+extern void error(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+
+ buf[0] = CNULL;
+ va_start(args);
+ fmt = (char *) va_arg(args, char *);
+ if (fmt)
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _error((buf[0]) ? buf : NULL);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg frontend to _error()
+ */
+extern void error(char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ buf[0] = CNULL;
+ va_start(args, fmt);
+ if (fmt)
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _error((buf[0]) ? buf : NULL);
+}
+#endif /* ARG_STDARG */
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple frontend to _error()
+ */
+/*VARARGS1*/
+extern void error(fmt, a1, a2, a3, a4, a5, a6)
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ buf[0] = CNULL;
+ if (fmt)
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5, a6);
+
+ _error((buf[0]) ? buf : NULL);
+}
+#endif /* ARG_TYPE */
+
+/*
+ * Display a fatal message
+ */
+static void _fatalerr(msg)
+ char *msg;
+{
+ static char buf[MSGBUFSIZ];
+
+ ++nerrs;
+
+ if (isserver)
+ (void) sprintf(buf, "REMOTE ERROR: %s", msg);
+ else
+ (void) sprintf(buf, "LOCAL ERROR: %s", msg);
+
+ _message(MT_FERROR, buf);
+
+ exit(nerrs);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs front-end to _fatalerr()
+ */
+extern void fatalerr(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+
+ va_start(args);
+ fmt = (char *) va_arg(args, char *);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _fatalerr(buf);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg front-end to _fatalerr()
+ */
+extern void fatalerr(char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _fatalerr(buf);
+}
+#endif /* ARG_STDARG */
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple front-end to _fatalerr()
+ */
+/*VARARGS1*/
+extern void fatalerr(fmt, a1, a2, a3, a4, a5)
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5);
+
+ _fatalerr(buf);
+}
+#endif /* !ARG_TYPE */
+
+/*
+ * Get the name of the file used for notify.
+ * A side effect is that the file pointer to the file
+ * is closed. We assume this function is only called when
+ * we are ready to read the file.
+ */
+extern char *getnotifyfile()
+{
+ register int i;
+
+ for (i = 0; msgfacility[i].mf_name; i++)
+ if (msgfacility[i].mf_msgfac == MF_NOTIFY &&
+ msgfacility[i].mf_fptr) {
+ (void) fclose(msgfacility[i].mf_fptr);
+ msgfacility[i].mf_fptr = NULL;
+ return(msgfacility[i].mf_filename);
+ }
+
+ return((char *) NULL);
+}
diff --git a/usr.bin/rdist/os-openbsd.h b/usr.bin/rdist/os-openbsd.h
new file mode 100644
index 00000000000..8abf35f43e0
--- /dev/null
+++ b/usr.bin/rdist/os-openbsd.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1993 Michael A. Cooper
+ * Copyright (c) 1993 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.
+ */
+
+/*
+ * $Id: os-openbsd.h,v 1.1 1996/02/03 12:12:33 dm Exp $
+ */
+
+/*
+ * TEMPLATE os-*.h file
+ */
+
+/*
+ * Define the following name for use in #ifdef's.
+ * The value should be all upper-case with no periods (.).
+ */
+#if !defined(FREEBSD)
+#define FREEBSD
+#endif
+
+/*
+ * NOTE: FreeBSD uses 64-bit file size semantics, and so you
+ * must be careful when using varargs-type functions
+ * like the *printf family when printing elements which
+ * might be 64-bits (such as stat->st_size from stat.h).
+ */
+
+/*
+ * Set process args to messages that show up when running ps(1)
+ *
+ * Under some OS's, the SETARGS code will cause ": is not an identifier"
+ * errors for "special" commands.
+ */
+#define SETARGS
+
+/*
+ * Define the type of directory routines your system has.
+ */
+#define DIR_TYPE DIR_DIRENT
+
+/*
+ * Determine what routines we have to get filesystem info.
+ */
+#define FSI_TYPE FSI_GETFSSTAT
+#define FSTYPENAME 1
+
+/*
+ * Type of non-blocking I/O.
+ */
+#define NBIO_TYPE NBIO_FCNTL
+
+/*
+ * Type of wait() function to use.
+ */
+#define WAIT_TYPE WAIT_WAIT3
+
+/*
+ * Type of argument passed to wait() (above).
+ */
+#define WAIT_ARG_TYPE int
+
+/*
+ * Select the type of executable file format.
+ */
+#define EXE_TYPE EXE_AOUT
+
+/*
+ * Select the type of statfs() system call (if any).
+ */
+#define STATFS_TYPE STATFS_BSD
+
+/*
+ * Type of arg functions we have.
+ */
+#define ARG_TYPE ARG_STDARG
+
+/*
+ * UID argument type for chown()
+ */
+typedef uid_t CHOWN_UID_T;
+
+/*
+ * GID argument type for chown()
+ */
+typedef gid_t CHOWN_GID_T;
+
+/*
+ * Our types, usually these are uid_t and gid_t.
+ */
+typedef uid_t UID_T; /* Must be signed */
+typedef gid_t GID_T; /* Must be signed */
+
+/*
+ * Generic pointer, used by memcpy, malloc, etc. Usually char or void.
+ */
+typedef void POINTER;
+
+/*
+ * Type of set file time function available
+ */
+#define SETFTIME_TYPE SETFTIME_UTIMES
+
+/*
+ * Type of set line buffering function available
+ */
+#define SETBUF_TYPE SETLINEBUF
+
+/*
+ * Things we have
+ */
+#define HAVE_FCHOWN /* Have fchown() */
+#define HAVE_FCHMOD /* Have fchmod() */
+#define HAVE_SELECT /* Have select() */
+#define HAVE_SAVED_IDS /* Have POSIX style saved [ug]id's */
+#define POSIX_SIGNALS /* Have POSIX signals */
+
+/*
+ * Things we need
+ */
+#define NEED_UNISTD_H /* Need <unistd.h> */
+
+/*
+ * Path to the remote shell command.
+ * Define this only if the pathname is different than
+ * that which appears in "include/paths.h".
+ */
+#define _PATH_REMSH "/usr/bin/rsh" /**/
diff --git a/usr.bin/rdist/pathnames.h b/usr.bin/rdist/pathnames.h
index c81b7d5fa62..5aeefd1f0f7 100644
--- a/usr.bin/rdist/pathnames.h
+++ b/usr.bin/rdist/pathnames.h
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1989, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 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
@@ -29,11 +29,19 @@
* 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>
+/*
+ * $Id: pathnames.h,v 1.2 1996/02/03 12:12:35 dm Exp $
+ * @(#)pathnames.h 5.4 (Berkeley) 8/27/90
+ */
+
+#include "config.h"
+
+#if !defined(_RDIST_TMP)
+# define _RDIST_TMP "rdistXXXXXX" /* Temporary file */
+#endif /* _RDIST_TMP */
-#define _PATH_RDIST "rdist"
+#if !defined(_PATH_RDISTD)
+# define _PATH_RDISTD "rdistd" /* Rdist server */
+#endif /* _PATH_RDISTD */
diff --git a/usr.bin/rdist/rdist.1 b/usr.bin/rdist/rdist.1
index 3a0287ddeee..e5d3e423924 100644
--- a/usr.bin/rdist/rdist.1
+++ b/usr.bin/rdist/rdist.1
@@ -1,5 +1,6 @@
-.\" Copyright (c) 1985, 1990, 1993
-.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Copyright (c) 1983 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
@@ -29,385 +30,843 @@
.\" 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 $
+.\" $Id: rdist.1,v 1.2 1996/02/03 12:12:37 dm Exp $
+.\" @(#)rdist.1 6.6 (Berkeley) 5/13/86
.\"
-.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.
+.TH RDIST 1 "March 14, 1994"
+.UC 6
+.SH NAME
+rdist \- remote file distribution client program
+.SH SYNOPSIS
+.B rdist
+[
+.B \-DFn
+]
+[
+.B \-A
+.I num
+]
+[
+.B \-a
+.I num
+]
+[
+.B \-d
+.I var=value
+]
+[
+.B \-l
+.I <local logopts>
+]
+[
+.B \-L
+.I <remote logopts>
+]
+[
+.B \-f
+.I distfile
+]
+[
+.B \-M
+.I maxproc
+]
+[
+.B \-m
+.I host
+]
+[
+.B \-o
+.I distopts
+]
+[
+.B \-t
+.I timeout
+]
+[
+.B \-p
+.I <rdistd-path>
+]
+[
+.B \-P
+.I <rsh-path>
+]
+[
+.I name ...
+]
+.PP
+.B rdist
+.B \-DFn
+.B -c
+.I name ...
+.I [login@]host[:dest]
+.PP
+.B rdist
+.B \-Server
+.PP
+.B rdist
+.B \-V
+.SH DESCRIPTION
+.I 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
+.I Rdist
reads commands from
-.Ar distfile
+.I 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.
+.I distfile
+is `\-', the standard input is used.
+If no
+.B \-f
+option is present, the program looks first for `distfile',
+then `Distfile' to use as the input.
If no names are specified on the command line,
-.Nm rdist
+.I rdist
will update all of the files and directories listed in
-.Ar distfile .
+.IR 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
+.PP
+The
+.B \-c
+option forces
+.I rdist
to interpret the remaining arguments as a small
-.Ar distfile .
-.Pp
+.IR distfile .
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
+.nf
+
+.ti +.5i
+( \fIname\fP ... ) -> [\fIlogin\fP@]\fIhost\fP
+.ti +1i
+install [\fIdest\fP] ;
+
+.fi
+.PP
+The
+.B \-Server
+option is recognized to provide partial backward compatible support
+for older versions of
+.I rdist
+which used this option to put
+.I rdist
+into server mode.
+If
+.I rdist
+is started with the
+.B \-Server
+command line option, it will attempt to exec (run) the old version of
+.I rdist.
+This option will only work if
+.I rdist
+was compiled with the location of the old rdist
+(usually either
+.I /usr/ucb/oldrdist
+or
+.I /usr/old/rdist)
+and that program is available at run time.
+.PP
+.I Rdist
+can use either the
+.I rcmd(3)
+function call or the
+.I rsh(1c),
+remote shell, command to access each target host.
+The method used is selected at compile-time.
+If the
+.I rsh(1c)
+method is used and
+the target host is the string
+.B localhost
+and
+the remote user name is the same as the local user name,
+.I rdist
+will run the command
+.nf
+.sp
+.RS
+.B "/bin/sh -c rdistd -S"
+.RE
+.sp
+.fi
+Otherwise
+.I rdist
+run will run the command
+.nf
+.sp
+.RS
+\fBrsh \fIhost\fB -l \fIremuser \fBrdistd -S\fR
+.RE
+.sp
+.fi
+where
+.I host
+is the name of the target host,
+.I remuser
+is the name of the user to make the connection as and,
+.I rdistd
+is the rdist server command on the target host as shown below.
+.PP
+If the
+.I rcmd(3)
+method is used, then
+.I rdist
+makes the connection to the target host itself and runs
+the
+.I rdistd
+server program as shown below.
+The default, and preferred method, is to use
+.I rsh(1c)
+to make the connection to target hosts. This allows
+.I rdist
+to be run without being setuid to ``root''.
+.PP
+On each target host
+.I Rdist
+will attempt to run the command
+.nf
+.sp
+.RS
+.I "rdistd -S"
+.RE
+.sp
+.fi
+or
+.nf
+.sp
+.RS
+.I "<rdistd path> -S"
+.RE
+.sp
+.fi
+if the
+.I \-p
+option was specified.
+If no
+.B \-p
+option is included,
+or the
+.I <rdistd path>
+is a simple filename,
+.I rdistd
+or
+.I <rdistd path>
+must be somewhere in the
+.B $PATH
+of the user running
+.B rdist
+on the remote (target) host.
+.SH OPTIONS
+.TP
+.B "\-A \fInum\fR"
+Set the minimum number of free files (inodes) on a filesystem that must exist
+for
+.I rdist
+to update or install a file.
+.TP
+.B "\-a \fInum\fR"
+Set the minimum amount of free space (in bytes) on a filesystem that must exist
+for
+.I rdist
+to update or install a file.
+.TP
+.B \-D
+Enable copious debugging messages.
+.TP
+.B "\-d \fIvar=value\fR"
Define
-.Ar var
+.I var
to have
-.Ar value .
-The
-.Fl d
+.IR value .
+This
option is used to define or override variable definitions in the
-.Ar distfile .
-.Ar Value
+.IR distfile .
+.I 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
+.TP
+.B \-F
+Do not fork any child
+.I rdist
+processes.
+All clients are updated sequentially.
+.TP
+.B "\-f \fIdistfile\fR"
+Set the name of the distfile to use to be
+.I distfile .
+If
+.I distfile
+is specified as
+``\-'' (dash)
+then read from standard input (stdin).
+.TP
+.B "\-l \fIlogopts\fR"
+Set local logging options.
+See the section
+.B "MESSAGE LOGGING"
+for details on the syntax for
+.I logopts.
+.TP
+.B "\-L \fIlogopts\fR"
+Set remote logging options.
+.I logopts
+is the same as for local logging
+except the values are passed to the remote
+server (\fIrdistd\fR).
+See the section
+.B "MESSAGE LOGGING"
+for details on the syntax for
+.I logopts.
+.TP
+.B "\-M \fInum\fR"
+Set the maximum number of simultaneously
+running child
+.I rdist
+processes to
+.I num.
+The default is 4.
+.TP
+.B "\-m \fImachine\fR"
Limit which machines are to be updated. Multiple
-.Fl m
+.B \-m
arguments can be given to limit updates to a subset of the hosts listed in the
-.Ar distfile .
-.It Fl n
+.IR distfile .
+.TP
+.B \-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
+.IR distfile .
+.TP
+.B "\-o\fIdistopts\fR"
+Specify the dist options to enable.
+.I distopts
+is a comma separated list of options which are listed below.
+The valid values for
+.I distopts
+are:
+.RS
+.IP \fBverify\fR
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
+.IP \fBwhole\fR
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.
+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
+rdisting a list of files such as
+.I /path/dir1/f1
+and
+.I /path/dir2/f2
+to
+.I /tmp/dir
+would create
+files
+.I /tmp/dir/path/dir1/f1
+and
+.I /tmp/dir/path/dir2/f2
+instead of
+.I /tmp/dir/dir1/f1
+and
+.I /tmp/dir/dir2/f2.
+.IP \fBnoexec\fR
+Automatically exclude executable files that are in
+.I a.out(5)
+format from being checked or updated.
+.IP \fByounger\fR
Younger mode. Files are normally updated if their
-.Ar mtime
+.I mtime
and
-.Ar size
+.I size
(see
-.Xr stat 2 )
-disagree. The
-.Fl y
+.IR stat (2))
+disagree. This
option causes
-.Nm rdist
+.I 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
+.IP \fBcompare\fR
+Binary comparison. Perform a binary comparison and update files if they differ
+rather than comparing dates and sizes.
+.IP \fBfollow\fR
+Follow symbolic links. Copy the file that the link points to rather than the
+link itself.
+.IP \fBignlnks\fR
+Ignore unresolved links.
+.I Rdist
+will normally try to maintain the link structure of files being transferred
+and warn the user if all the links cannot be found.
+.IP \fBchknfs\fR
+Do not check or update files on target host that
+reside on NFS filesystems.
+.IP \fBchkreadonly\fR
+Enable check on target host
+to see if a file resides on a read-only filesystem.
+If a file does, then no checking or updating of the file is attempted.
+.IP \fBchksym\fR
+If the target on the remote host is a symbolic link, but is not on the
+master host, the remote target will be left a symbolic link.
+This behavior is generally considered a bug in the original version of
+.I rdist,
+but is present to allow compatibility with older versions.
+.IP \fBquiet\fR
+Quiet mode. Files that are being modified are normally
+printed on standard output. This
+option suppresses this.
+.IP \fBremove\fR
+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.
+.IP \fBnochkowner\fR
+Do not check user ownership of files that already exist.
+The file ownership is only set when the file is updated.
+.IP \fBnochkgroup\fR
+Do not check group ownership of files that already exist.
+The file ownership is only set when the file is updated.
+.IP \fBnochkmode\fR
+Do not check file and directory permission modes.
+The permission mode is only set when the file is updated.
+.IP \fBnodescend\fR
+Do not descend into a directory.
+Normally
+.I rdist
+will recursively check directories.
+If this option is enabled, then any files listed in the
+file list in the distfile that are directories are not recursively scanned.
+Only the existence, ownership, and mode of the directory are checked.
+.IP \fBnumchkgroup\fR
+Use the numeric group id (gid) to check group ownership instead of
+the group name.
+.IP \fBnumchkowner\fR
+Use the numeric user id (uid) to check user ownership instead of
+the user name.
+.IP \fBsavetargets\fR
+Save files that are updated instead of removing them.
+Any target file that is updates is first rename from
+.B file
+to
+.B file.OLD.
+.RE
+.TP
+.B "\-p \fI<rdistd-path>\fR"
+Set the path where the rdistd server is searched for on the target host.
+.TP
+.B "\-P \fI<rsh-path>\fR"
+Set the path to the
+.I rsh(1c)
+command.
+The
+.I rsh-path
+may be a colon seperated list of possible pathnames.
+In this case, the first component of the path to exist is used.
+i.e.
+.B "/usr/ucb/rsh:/usr/bin/remsh",
+.B /usr/bsd/rsh.
+.TP
+.B "\-t \fItimeout\fR"
+Set the timeout period (in seconds) for waiting for responses from the remote
+.I rdist
+server.
+The default is 900 seconds.
+.TP
+.B \-V
+Print version information and exit.
+.SH "MESSAGE LOGGING"
+.I Rdist
+uses a collection of predefined message
+.B facilities
+that each contain a list of message
+.B types
+specifying which types of messages to send to that
+.I facility.
+The local client (\fIrdist\fR) and the remote server (\fIrdistd\fR) each
+maintain
+their own copy of what types of messages to log to what facilities.
+.LP
+The
+.B \-l
+.I logopts
+option to
+.I rdist
+tells
+.I rdist
+what logging options to use locally.
+The
+.B \-L
+.I logopts
+option to
+.I rdist
+tells
+.I rdist
+what logging options to pass to the remote
+.I rdistd
+server.
+.LP
+The form of
+.I logopts
+should be of form
+.sp
+.RS
+\fIfacility\fB=\fItypes\fB:\fIfacility\fB=\fItypes...
+.RE
+.sp
+The valid facility names are:
+.RS
+.IP \fBstdout\fR
+Messages to standard output.
+.IP \fBfile\fR
+Log to a file. To specify the file name, use the format
+``\fBfile=\fIfilename\fB=\fItypes\fR''.
+e.g.
+.B "``file=/tmp/rdist.log=all,debug''.
+.IP \fBsyslog\fR
+Use the
+.I syslogd(8)
+facility.
+.IP \fBnotify\fR
+Use the internal
+.I rdist
+.B notify
+facility.
+This facility is used in conjunction with the
+.B notify
+keyword in a
+.I distfile
+to specify what messages are mailed to the
+.B notify
+address.
+.RE
+.LP
+.I types
+should be a comma separated list of message types. Each message type
+specified enables that message level. This is unlike the
+.I syslog(3)
+system facility which uses an ascending order scheme.
+The following
+are the valid
+.I types:
+.RS
+.IP \fBchange\fR
+Things that change.
+This includes files that are installed or updated in some way.
+.IP \fBinfo\fR
+General information.
+.IP \fBnotice\fR
+General info about things that change.
+This includes things like making directories which are needed in order
+to install a specific target, but which are not explicitly specified in
+the
+.I distfile.
+.IP \fBnerror\fR
+Normal errors that are not fatal.
+.IP \fBferror\fR
+Fatal errors.
+.IP \fBwarning\fR
+Warnings about errors which are not as serious as
+.B nerror
+type messages.
+.IP \fBdebug\fR
+Debugging information.
+.IP \fBall\fR
+All but debug messages.
+.RE
+.LP
+Here is a sample command line option:
+.nf
+.sp
+.RS
+\-l stdout=all:syslog=change,notice:file=/tmp/rdist.log=all
+.RE
+.sp
+.fi
+This entry will set local message logging to have all but debug
+messages sent to standard output, change and notice messages will
+be sent to
+.I syslog(3),
+and all messages will be written to the file
+.B /tmp/rdist.log.
+.SH DISTFILES
+.PP
+The
+.I 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
+.nf
+
+.RS
<variable name> `=' <name list>
-[label:]<source list> `\->' <destination list> <command list>
-[label:]<source list> `::' <time_stamp file> <command list>
-.Ed
-.Pp
+[ label: ] <source list> `\->' <destination list> <command list>
+[ label: ] <source list> `::' <time_stamp file> <command list>
+.RE
+
+.fi
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
+The \fIsource list\fP 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
+The \fIdestination list\fP 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
+.PP
Labels are optional. They are used to identify a command for partial updates.
-.Pp
+.PP
Newlines, tabs, and blanks are only used as separators and are
otherwise ignored. Comments begin with `#' and end with a newline.
-.Pp
+.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
+.PP
The source and destination lists have the following format:
-.Bd -literal -offset indent
+.nf
+
+.ti +.5i
<name>
-.Ed
or
-.Bd -literal -offset indent -compact
+.ti +.5i
`(' <zero or more names separated by white-space> `)'
-.Ed
-.Pp
+
+.fi
+These simple lists can be modified by using one level of set addition,
+subtraction, or intersection like this:
+.nf
+
+.ti +.5i
+list '-' list
+or
+.ti +.5i
+list '+' list
+or
+.ti +.5i
+list '&' list
+
+.fi
+If additional modifications are needed (e.g., ``all servers and client
+machines except for the OSF/1 machines'') then the list will have
+to be explicitly constructed in steps using "temporary" variables.
+.PP
The shell meta-characters `[', `]', `{', `}', `*', and `?'
are recognized and expanded (on the local host only) in the same way as
-.Xr csh 1 .
+.IR csh (1).
They can be escaped with a backslash.
The `~' character is also expanded in the same way as
-.Xr csh 1
+.IR csh
but is expanded separately on the local and destination hosts.
When the
-.Fl w
+.B \-o\fIwhole\fR
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
+.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
+.nf
+
+.RS
+.ta \w'cmdspecial 'u +\w'name list 'u
+`install' <options> opt_dest_name `;'
+`notify' <name list> `;'
+`except' <name list> `;'
+`except_pat' <pattern list> `;'
+`special' <name list> string `;'
+`cmdspecial' <name list> string `;'
+.RE
+
+.fi
+.PP
The
-.Ic install
+.I 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
+.I Opt_dest_name
is an optional parameter to rename files.
If no
-.Ic install
+.I 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
+\fB\-o \fIdistopts\fR
+option
+as specified above under
+.B OPTIONS,
+has the same semantics as
+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
+.PP
The
-.Ic notify
+.I 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
+.PP
The
-.Ic except
+.I except
command is used to update all of the files in the source list
-.Ic except
-for the files listed in
-.Ar name list .
+.B except
+for the files listed in \fIname list\fP.
This is usually used to copy everything in a directory except certain files.
-.Pp
+.PP
The
-.Ic except_pat
+.I except_pat
command is like the
-.Ic except
-command except that
-.Ar pattern list
-is a list of regular expressions
+.I except
+command except that \fIpattern list\fP is a list of regular expressions
(see
-.Xr ed 1
+.IR 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
+part of the regular expression. Variables are expanded in \fIpattern list\fP
but not shell file pattern matching characters. To include a `$', it
must be escaped with `\e'.
-.Pp
+.PP
The
-.Ic special
+.I special
command is used to specify
-.Xr sh 1
+.IR 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
+remote host after the file in \fIname list\fP is updated or installed.
+If the \fIname list\fP is omitted then the shell commands will be executed
+for every file updated or installed.
+.I String
starts and ends with `"' and can cross multiple lines in
-.Ar distfile .
+.I 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
+.I 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 )
+The following environment variables are set for each
+.I special
+command:
+.IP \fBFILE\fR
+The full pathname of the local file that was just updated.
+.IP \fBREMFILE\fR
+The full pathname of the remote file that was just updated.
+.IP \fBBASEFILE\fR
+The basename of the remote file that was just updated.
+.PP
+The
+.I cmdspecial
+command is similar to the
+.I special
+command, except it is executed only when the entire command is completed
+instead of after each file is updated.
+The list of files is placed in the environment variable
+.B $FILES.
+Each file name in
+.B $FILES
+is separated by a `:' (colon).
+.PP
+If a hostname ends in a ``+'' (plus sign), then the plus
+is stripped off and NFS checks are disabled.
+This is equivalent to disabling the
+.B \-o\fIchknfs\fR
+option just for this one host.
+.PP
+The following is a small example.
+.nf
+
+.RS
+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 )
+ /usr/include/{*.h,{stand,sys,vax*,pascal,machine}/*.h}
+ /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 )
+ sendmail.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" ;
+ install -oremove,chknfs ;
+ except /usr/lib/${EXLIB} ;
+ except /usr/games/lib ;
+ special /usr/lib/sendmail "/usr/lib/sendmail -bz" ;
srcs:
/usr/src/bin -> arpa
-\texcept_pat ( \e\e.o\e$ /SCCS\e$ ) ;
+ except_pat ( \e\e.o\e$ /SCCS\e$ ) ;
IMAGEN = (ips dviimp catdvi)
imagen:
/usr/local/${IMAGEN} -> arpa
-\tinstall /usr/local/lib ;
-\tnotify ralph ;
+ install /usr/local/lib ;
+ notify 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
+ notify root@cory ;
+.RE
+
+.fi
+.SH ENVIRONMENT
+.IP TMPDIR
+Name of temporary directory to use. Default is
+.B /tmp.
+.SH FILES
+.nf
+.ta \w'/tmp/rdist* 'u
+distfile \- input command file
+$TMPDIR/rdist* \- temporary file for update lists
+.fi
+.SH "SEE ALSO"
+.B sh(1),
+.B csh(1),
+.B stat(2),
+.B rsh(1c),
+.B rcmd(3)
+.SH DIAGNOSTICS
+.SH NOTES
+.LP
+If the basename of a file (the last component in the pathname)
+is ".", then
+.B rdist
+assumes the remote (destination) name is a directory.
+i.e.
+.B /tmp/.
+means that
+.B /tmp
+should be a directory on the remote host.
+.LP
+The following options are still recognized for backwards compatibility:
+.sp
+.RS
+\-v \-N \-O \-q \-b \-r \-R \-s \-w \-y \-h \-i \-x
+.RE
+.sp
+.SH BUGS
+Source files must reside on the local host where rdist is executed.
+.PP
Variable expansion only works for name lists; there should be a general macro
facility.
-.Pp
-.Nm Rdist
+.PP
+.I 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.
+.PP
+If a hardlinked file is listed more than once in the same target,
+then
+.I rdist
+will report missing links.
+Only one instance of a link should be listed in each target.
diff --git a/usr.bin/rdist/rdist.c b/usr.bin/rdist/rdist.c
new file mode 100644
index 00000000000..c07386ebb0d
--- /dev/null
+++ b/usr.bin/rdist/rdist.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: rdist.c,v 1.1 1996/02/03 12:12:38 dm Exp $";
+
+static char sccsid[] = "@(#)main.c 5.1 (Berkeley) 6/6/85";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+
+#include "defs.h"
+#include "y.tab.h"
+#include <netdb.h>
+#include <sys/ioctl.h>
+
+/*
+ * Remote distribution program.
+ */
+
+char *distfile = NULL; /* Name of distfile to use */
+int maxchildren = MAXCHILDREN; /* Max no of concurrent PIDs */
+int nflag = 0; /* Say without doing */
+long min_freespace = 0; /* Min filesys free space */
+long min_freefiles = 0; /* Min filesys free # files */
+FILE *fin = NULL; /* Input file pointer */
+struct group *gr = NULL; /* Static area for getgrent */
+char localmsglist[] = "stdout=all:notify=all:syslog=nerror,ferror";
+char *remotemsglist = NULL;
+char optchars[] = "A:a:bcd:DFf:hil:L:M:m:NnOo:p:P:qRrst:Vvwxy";
+FILE *opendist();
+char *path_rdistd = _PATH_RDISTD;
+char *path_remsh = _PATH_REMSH;
+
+/*
+ * Add a hostname to the host list
+ */
+static void addhostlist(name, hostlist)
+ char *name;
+ struct namelist **hostlist;
+{
+ register struct namelist *ptr, *new;
+
+ if (!name || !hostlist)
+ return;
+
+ new = (struct namelist *) xmalloc(sizeof(struct namelist));
+ new->n_name = strdup(name);
+ new->n_next = NULL;
+
+ if (*hostlist) {
+ for (ptr = *hostlist; ptr && ptr->n_next; ptr = ptr->n_next)
+ ;
+ ptr->n_next = new;
+ } else
+ *hostlist = new;
+}
+
+main(argc, argv, envp)
+ int argc;
+ char *argv[];
+ char **envp;
+{
+ struct namelist *hostlist = NULL;
+ register int x;
+ register char *cp;
+ int cmdargs = 0;
+ int c;
+
+ /*
+ * We initialize progname here instead of init() because
+ * things in msgparseopts() need progname set.
+ */
+ setprogname(argv);
+
+ if (cp = msgparseopts(localmsglist, TRUE)) {
+ error("Bad builtin log option (%s): %s.",
+ localmsglist, cp);
+ usage();
+ }
+
+ if (init(argc, argv, envp) < 0)
+ exit(1);
+
+ /*
+ * Be backwards compatible.
+ */
+ for (x = 1; x <= argc && argv[x]; x++) {
+ if (strcmp(argv[x], "-Server") != 0)
+ continue;
+#if defined(_PATH_OLDRDIST)
+ message(MT_SYSLOG,
+ "Old rdist (-Server) requested; running %s",
+ _PATH_OLDRDIST);
+ (void) execl(_PATH_OLDRDIST, xbasename(_PATH_OLDRDIST),
+ "-Server", (char *)NULL);
+ fatalerr("Exec old rdist failed: %s: %s.",
+ _PATH_OLDRDIST, SYSERR);
+#else /* !_PATH_OLDRDIST */
+ fatalerr("Old rdist not available.");
+#endif /* _PATH_OLDRDIST */
+ exit(1);
+ }
+
+#if defined(DIRECT_RCMD)
+ if (becomeuser() != 0)
+ exit(1);
+#else /* !DIRECT_RCMD */
+ /*
+ * Perform check to make sure we are not incorrectly installed
+ * setuid to root or anybody else.
+ */
+ if (getuid() != geteuid())
+ fatalerr("This version of rdist should not be installed setuid.");
+#endif /* DIRECT_RCMD */
+
+ while ((c = getopt(argc, argv, optchars)) != -1)
+ switch (c) {
+ case 'l':
+ if (cp = msgparseopts(optarg, TRUE)) {
+ error("Bad log option \"%s\": %s.", optarg,cp);
+ usage();
+ }
+ break;
+
+ case 'L':
+ remotemsglist = strdup(optarg);
+ break;
+
+ case 'A':
+ case 'a':
+ case 'M':
+ case 't':
+ if (!isdigit(*optarg)) {
+ error("\"%s\" is not a number.", optarg);
+ usage();
+ }
+ if (c == 'a')
+ min_freespace = atoi(optarg);
+ else if (c == 'A')
+ min_freefiles = atoi(optarg);
+ else if (c == 'M')
+ maxchildren = atoi(optarg);
+ else if (c == 't')
+ rtimeout = atoi(optarg);
+ break;
+
+ case 'F':
+ do_fork = FALSE;
+ break;
+
+ case 'f':
+ distfile = strdup(optarg);
+ if (distfile[0] == '-' && distfile[1] == CNULL)
+ fin = stdin;
+ break;
+
+ case 'm':
+ addhostlist(optarg, &hostlist);
+ break;
+
+ case 'd':
+ define(optarg);
+ break;
+
+ case 'D':
+ debug = DM_ALL;
+ if (cp = msgparseopts("stdout=all,debug", TRUE)) {
+ error("Enable debug messages failed: %s.", cp);
+ usage();
+ }
+ break;
+
+ case 'c':
+ cmdargs++;
+ break;
+
+ case 'n':
+ nflag++;
+ break;
+
+ case 'V':
+ printf("%s\n", getversion());
+ exit(0);
+
+ case 'o':
+ if (parsedistopts(optarg, &options, TRUE)) {
+ error("Bad dist option string \"%s\".",
+ optarg);
+ usage();
+ }
+ break;
+
+ case 'p':
+ if (!optarg) {
+ error("No path specified to \"-p\".");
+ usage();
+ }
+ path_rdistd = strdup(optarg);
+ break;
+
+ case 'P':
+ if (!optarg) {
+ error("No path specified to \"-P\".");
+ usage();
+ }
+ if (cp = searchpath(optarg))
+ path_remsh = strdup(cp);
+ else {
+ error("No component of path \"%s\" exists.",
+ optarg);
+ usage();
+ }
+ break;
+
+ /*
+ * These options are obsoleted by -o. They are
+ * provided only for backwards compatibility
+ */
+ case 'v': FLAG_ON(options, DO_VERIFY); break;
+ case 'N': FLAG_ON(options, DO_CHKNFS); break;
+ case 'O': FLAG_ON(options, DO_CHKREADONLY); break;
+ case 'q': FLAG_ON(options, DO_QUIET); break;
+ case 'b': FLAG_ON(options, DO_COMPARE); break;
+ case 'r': FLAG_ON(options, DO_NODESCEND); break;
+ case 'R': FLAG_ON(options, DO_REMOVE); break;
+ case 's': FLAG_ON(options, DO_SAVETARGETS); break;
+ case 'w': FLAG_ON(options, DO_WHOLE); break;
+ case 'y': FLAG_ON(options, DO_YOUNGER); break;
+ case 'h': FLAG_ON(options, DO_FOLLOW); break;
+ case 'i': FLAG_ON(options, DO_IGNLNKS); break;
+ case 'x': FLAG_ON(options, DO_NOEXEC); break;
+
+ case '?':
+ default:
+ usage();
+ }
+
+ if (debug) {
+ printf("%s\n", getversion());
+ msgprconfig();
+ }
+
+ if (nflag && IS_ON(options, DO_VERIFY))
+ fatalerr(
+ "The -n flag and \"verify\" mode may not both be used.");
+
+ /*
+ * Don't fork children for nflag
+ */
+ if (nflag)
+ do_fork = 0;
+
+ if (cmdargs)
+ docmdargs(realargc - optind, &realargv[optind]);
+ else {
+ if (fin == NULL)
+ fin = opendist(distfile);
+ (void) yyparse();
+ /*
+ * Need to keep stdin open for child processing later
+ */
+ if (fin != stdin)
+ (void) fclose(fin);
+ if (nerrs == 0)
+ docmds(hostlist, realargc-optind, &realargv[optind]);
+ }
+
+ exit(nerrs != 0);
+}
+
+/*
+ * Open a distfile
+ */
+FILE *opendist(distfile)
+ char *distfile;
+{
+ char *file = NULL;
+ FILE *fp;
+
+ if (distfile == NULL) {
+ if (access("distfile", R_OK) == 0)
+ file = "distfile";
+ else if (access("Distfile", R_OK) == 0)
+ file = "Distfile";
+ } else {
+ /*
+ * Try to test to see if file is readable before running m4.
+ */
+ if (access(distfile, R_OK) != 0)
+ fatalerr("%s: Cannot access file: %s.",
+ distfile, SYSERR);
+ file = distfile;
+ }
+
+ if (file == NULL)
+ fatalerr("No distfile found.");
+
+ fp = fopen(file, "r");
+
+ if (fp == NULL)
+ fatalerr("%s: open failed: %s.", file, SYSERR);
+
+ return(fp);
+}
+
+/*
+ * Print usage message and exit.
+ */
+usage()
+{
+ char *sopts = "cDFnv";
+
+ (void) fprintf(stderr,
+ "Usage: %s [-%s] [-A <num>] [-a <num>] [-d var=value]\n",
+ progname, sopts);
+ (void) fprintf(stderr,
+ "\t[-f distfile] [-l <msgopt>] [-L <msgopt>] [-M <maxproc>]\n");
+ (void) fprintf(stderr,
+ "\t[-m host] [-o <distopts>] [-p <rdistd-cmd>] [-P <rsh-path>]\n");
+ (void) fprintf(stderr,
+ "\t[-t <timeout>] [target ...]\n");
+
+ (void) fprintf(stderr,
+ "OR: %s [-%s] -c source [...] machine[:dest]\n",
+ progname, sopts);
+
+ (void) fprintf(stderr, "OR: %s -V\n", progname);
+
+ (void) fprintf(stderr, "\nThe values for <distopts> are:\n\t%s\n",
+ getdistoptlist());
+
+ msgprusage();
+
+ exit(1);
+}
+
+/*
+ * rcp like interface for distributing files.
+ */
+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;
+ files = 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 = strchr(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;
+ }
+
+ debugmsg(DM_MISC, "docmdargs()\nfiles = %s", getnlstr(files));
+ debugmsg(DM_MISC, "host = %s", getnlstr(hosts));
+
+ insert((char *)NULL, files, hosts, cmds);
+ docmds(0, (char **)NULL, 0, (char **)NULL);
+}
+
+/*
+ * Get a list of NAME blocks (mostly for debugging).
+ */
+extern char *getnlstr(nl)
+ register struct namelist *nl;
+{
+ static char buf[16384];
+ register int count = 0, len = 0;
+
+ (void) sprintf(buf, "(");
+
+ while (nl != NULL) {
+ if (nl->n_name == NULL)
+ continue;
+ len += strlen(nl->n_name) + 2;
+ if (len >= sizeof(buf)) {
+ (void) strcpy(buf,
+ "getnlstr() Buffer not large enough");
+ return(buf);
+ }
+ ++count;
+ (void) strcat(buf, " ");
+ (void) strcat(buf, nl->n_name);
+ nl = nl->n_next;
+ }
+
+ (void) strcat(buf, " )");
+
+ return(buf);
+}
diff --git a/usr.bin/rdist/rshrcmd.c b/usr.bin/rdist/rshrcmd.c
new file mode 100644
index 00000000000..3383edc6fbe
--- /dev/null
+++ b/usr.bin/rdist/rshrcmd.c
@@ -0,0 +1,106 @@
+
+/*
+ * This is an rcmd() replacement originally by
+ * Chris Siebenmann <cks@utcc.utoronto.ca>.
+ */
+
+#ifndef lint
+static char RCSid[] =
+"$Id: rshrcmd.c,v 1.1 1996/02/03 12:12:40 dm Exp $";
+#endif
+
+#include "defs.h"
+
+#if !defined(DIRECT_RCMD)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+
+/*
+ * This is a replacement rcmd() function that uses the rsh(1c)
+ * program in place of a direct rcmd() function call so as to
+ * avoid having to be root.
+ */
+rshrcmd(ahost, port, luser, ruser, cmd, fd2p)
+ char **ahost;
+ u_short port;
+ char *luser, *ruser, *cmd;
+ int *fd2p;
+{
+ int cpid;
+ struct hostent *hp;
+ int sp[2];
+
+ /* insure that we are indeed being used as we thought. */
+ if (fd2p != 0)
+ return -1;
+ /* validate remote hostname. */
+ hp = gethostbyname(*ahost);
+ if (hp == 0) {
+ error("%s: unknown host", *ahost);
+ return -1;
+ }
+ /* *ahost = hp->h_name; /* This makes me nervous. */
+
+ /* get a socketpair we'll use for stdin and stdout. */
+ if (getsocketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) {
+ error("socketpair(AF_UNIX, SOCK_STREAM, 0) failed: %s.",
+ SYSERR);
+ return -1;
+ }
+
+ cpid = fork();
+ if (cpid < 0) {
+ error("fork failed: %s.", SYSERR);
+ return -1; /* error. */
+ }
+ if (cpid == 0) {
+ /* child. we use sp[1] to be stdin/stdout, and close
+ sp[0]. */
+ (void) close(sp[0]);
+ if (dup2(sp[1], 0) < 0 || dup2(0,1) < 0 || dup2(0, 2) < 0) {
+ error("dup2 failed: %s.", SYSERR);
+ _exit(255);
+ }
+ /* fork again to lose parent. */
+ cpid = fork();
+ if (cpid < 0) {
+ error("fork to lose parent failed: %s.", SYSERR);
+ _exit(255);
+ }
+ if (cpid > 0)
+ _exit(0);
+ /* in grandchild here. */
+
+ /*
+ * If we are rdist'ing to "localhost" as the same user
+ * as we are, then avoid running remote shell for efficiency.
+ */
+ if (strcmp(*ahost, "localhost") == 0 &&
+ strcmp(luser, ruser) == 0) {
+ execlp(_PATH_BSHELL, xbasename(_PATH_BSHELL), "-c",
+ cmd, (char *) NULL);
+ error("execlp %s failed: %s.", _PATH_BSHELL, SYSERR);
+ } else {
+ execlp(path_remsh, xbasename(path_remsh),
+ *ahost, "-l", ruser, cmd, (char *) NULL);
+ error("execlp %s failed: %s.", path_remsh, SYSERR);
+ }
+ _exit(255);
+ }
+ if (cpid > 0) {
+ /* parent. close sp[1], return sp[0]. */
+ (void) close(sp[1]);
+ /* reap child. */
+ (void) wait(0);
+ return sp[0];
+ }
+ /*NOTREACHED*/
+}
+
+#endif /* !DIRECT_RCMD */
diff --git a/usr.bin/rdist/setargs.c b/usr.bin/rdist/setargs.c
new file mode 100644
index 00000000000..815ffe3de81
--- /dev/null
+++ b/usr.bin/rdist/setargs.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: setargs.c,v 1.1 1996/02/03 12:12:43 dm Exp $";
+
+static char sccsid[] = "@(#)setargs.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#include "defs.h"
+
+#if defined(SETARGS)
+
+/*
+ * Set process argument functions
+ */
+
+#define MAXUSERENVIRON 40
+char **Argv = NULL;
+char *LastArgv = NULL;
+char *UserEnviron[MAXUSERENVIRON+1];
+
+/*
+ * Settup things for using setproctitle()
+ */
+setargs_settup(argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register int i;
+ extern char **environ;
+
+ /* Remember the User Environment */
+
+ for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++)
+ UserEnviron[i] = strdup(envp[i]);
+ UserEnviron[i] = NULL;
+ environ = UserEnviron;
+
+ /* Save start and extent of argv for setproctitle */
+ Argv = argv;
+ if (i > 0)
+ LastArgv = envp[i-1] + strlen(envp[i-1]);
+ else
+ LastArgv = argv[argc-1] + strlen(argv[argc-1]);
+}
+
+/*
+ * Set process title
+ */
+extern void _setproctitle(msg)
+ char *msg;
+{
+ register int i;
+ char *p;
+
+ p = Argv[0];
+
+ /* Make ps print "(program)" */
+ *p++ = '-';
+
+ i = strlen(msg);
+ if (i > LastArgv - p - 2) {
+ i = LastArgv - p - 2;
+ msg[i] = '\0';
+ }
+ (void) strcpy(p, msg);
+ p += i;
+ while (p < LastArgv) {
+ *p++ = ' ';
+ }
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs front-end to _setproctitle()
+ */
+extern void setproctitle(va_alist)
+ va_dcl
+{
+ static char buf[BUFSIZ];
+ va_list args;
+ char *fmt;
+
+ va_start(args);
+ fmt = va_arg(args, char *);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _setproctitle(buf);
+}
+#endif /* ARG_VARARGS */
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg front-end to _setproctitle()
+ */
+extern void setproctitle(char *fmt, ...)
+{
+ static char buf[BUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _setproctitle(buf);
+}
+#endif /* ARG_STDARG */
+#if !defined(ARG_TYPE)
+/*
+ * Non-Varargs front-end to _setproctitle()
+ */
+/*VARARGS1*/
+extern void setproctitle(fmt, a1, a2, a3, a4, a5, a6)
+ char *fmt;
+{
+ static char buf[BUFSIZ];
+
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5, a6);
+
+ _setproctitle(buf);
+}
+#endif /* !ARG_TYPE */
+
+#endif /* SETARGS */
diff --git a/usr.bin/rdist/signal.c b/usr.bin/rdist/signal.c
new file mode 100644
index 00000000000..71382714cd2
--- /dev/null
+++ b/usr.bin/rdist/signal.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1993 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 RCSid[] =
+"$Id: signal.c,v 1.1 1996/02/03 12:12:44 dm Exp $";
+
+static char sccsid[] = "@(#)signal.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1993 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#include "defs.h"
+
+#if defined(NEED_SIGBLOCK)
+static int current_mask = 0;
+
+int sigblock(mask)
+int mask;
+{
+ int sig;
+ int m;
+ int oldmask;
+
+ oldmask = current_mask;
+ for ( sig = 1, m = 1; sig <= MAXSIG; sig++, m <<= 1 ) {
+ if (mask & m) {
+ sighold(sig);
+ current_mask |= m;
+ }
+ }
+ return oldmask;
+}
+#endif /* NEED_SIGBLOCK */
+
+#if defined(NEED_SIGSETMASK)
+int sigsetmask(mask)
+int mask;
+{
+ int sig;
+ int m;
+ int oldmask;
+
+ oldmask = current_mask;
+ for ( sig = 1, m = 1; sig <= MAXSIG; sig++, m <<= 1 ) {
+ if (mask & m) {
+ sighold(sig);
+ current_mask |= m;
+ }
+ else {
+ sigrelse(sig);
+ current_mask &= ~m;
+ }
+ }
+ return oldmask;
+}
+#endif /* NEED_SIGSETMASK */
diff --git a/usr.bin/rdist/types.h b/usr.bin/rdist/types.h
new file mode 100644
index 00000000000..63483c06098
--- /dev/null
+++ b/usr.bin/rdist/types.h
@@ -0,0 +1,101 @@
+#ifndef __myTYPES_H__
+#define __myTYPES_H__
+
+/*
+ * $Id: types.h,v 1.1 1996/02/03 12:12:45 dm Exp $
+ */
+
+/*
+ * Dist Options.
+ *
+ * WARNING: This values are used by the server (rdistd)
+ */
+#define DO_VERIFY 0x000001
+#define DO_WHOLE 0x000002
+#define DO_YOUNGER 0x000004
+#define DO_COMPARE 0x000008
+#define DO_REMOVE 0x000010
+#define DO_FOLLOW 0x000020
+#define DO_IGNLNKS 0x000040
+#define DO_QUIET 0x000100
+#define DO_CHKNFS 0x000200
+#define DO_CHKREADONLY 0x000400
+#define DO_NOEXEC 0x000800
+#define DO_SAVETARGETS 0x001000
+#define DO_NODESCEND 0x002000
+#define DO_NOCHKOWNER 0x004000
+#define DO_NOCHKMODE 0x008000
+#define DO_NOCHKGROUP 0x010000
+#define DO_CHKSYM 0x020000
+#define DO_NUMCHKGROUP 0x040000
+#define DO_NUMCHKOWNER 0x080000
+
+/*
+ * Dist option information
+ */
+typedef long opt_t;
+struct _distoptinfo {
+ opt_t do_value;
+ char *do_name;
+};
+typedef struct _distoptinfo DISTOPTINFO;
+
+ /* Debug Message types */
+#define DM_CALL 0x01
+#define DM_PROTO 0x02
+#define DM_CHILD 0x04
+#define DM_MISC 0x10
+#define DM_ALL 0x17
+
+/*
+ * Description of a message type
+ */
+struct _msgtype {
+ int mt_type; /* Type (bit) */
+ char *mt_name; /* Name of message type */
+};
+typedef struct _msgtype MSGTYPE;
+
+/*
+ * Message Type definitions
+ */
+#define MT_DEBUG 0x0001 /* Debugging messages */
+#define MT_NERROR 0x0002 /* Normal errors */
+#define MT_FERROR 0x0004 /* Fatal errors */
+#define MT_WARNING 0x0010 /* Warning messages */
+#define MT_CHANGE 0x0020 /* Something changed */
+#define MT_INFO 0x0040 /* General information */
+#define MT_NOTICE 0x0100 /* Notice's */
+#define MT_SYSLOG 0x0200 /* System log, but not user */
+#define MT_REMOTE 0x0400 /* Ensure msg to remote */
+#define MT_NOREMOTE 0x1000 /* Don't log to remote host */
+#define MT_VERBOSE 0x2000 /* Verbose messages */
+#define MT_ALL (MT_NERROR|MT_FERROR|\
+ MT_WARNING|MT_CHANGE|\
+ MT_INFO|MT_NOTICE|\
+ MT_SYSLOG|MT_VERBOSE)
+
+/*
+ * Description of message facilities
+ */
+struct _msgfacility {
+ /* compile time initialized data */
+ int mf_msgfac; /* One of MF_* from below */
+ char *mf_name; /* Name of this facility */
+ void (*mf_sendfunc)(); /* Function to send msg */
+ /* run time initialized data */
+ int mf_msgtypes; /* Bitmask of MT_* from above*/
+ char *mf_filename; /* Name of file */
+ FILE *mf_fptr; /* File pointer to output to */
+};
+typedef struct _msgfacility MSGFACILITY;
+
+/*
+ * Message Facilities
+ */
+#define MF_STDOUT 1 /* Standard Output */
+#define MF_NOTIFY 2 /* Notify mail service */
+#define MF_FILE 3 /* A normal file */
+#define MF_SYSLOG 4 /* syslog() */
+
+#endif /* __myTYPES_H__ */
diff --git a/usr.bin/rdist/version.h b/usr.bin/rdist/version.h
new file mode 100644
index 00000000000..7f30f9b676c
--- /dev/null
+++ b/usr.bin/rdist/version.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1983 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.
+ */
+
+/*
+ * $Id: version.h,v 1.1 1996/02/03 12:12:47 dm Exp $
+ */
+
+/*
+ * The rdist protocol version number. This should be changed
+ * whenever the protocol changes.
+ */
+#define VERSION 6
+
+/*
+ * Distribution version. The major distribution number should match
+ * the protocol version number (VERSION) defined above.
+ */
+#define DISTVERSION "6.1"
+
+/*
+ * Patch level
+ */
+#define PATCHLEVEL 1
+
+/*
+ * Distribution status
+ */
+#define DISTSTATUS "RELEASE"
diff --git a/usr.bin/rdistd/Makefile b/usr.bin/rdistd/Makefile
new file mode 100644
index 00000000000..5f57de49e78
--- /dev/null
+++ b/usr.bin/rdistd/Makefile
@@ -0,0 +1,9 @@
+PROG= rdistd
+SRCS= common.c filesys-os.c filesys.c message.c rdistd.c server.c setargs.c
+.PATH: ${.CURDIR}/../rdist
+CFLAGS+=-I${.CURDIR}/../rdist -DOS_H=\"os-openbsd.h\"
+
+LDADD= -lcompat
+DPADD= ${LIBCOMPAT}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rdistd/filesys-os.c b/usr.bin/rdistd/filesys-os.c
new file mode 100644
index 00000000000..43226af378f
--- /dev/null
+++ b/usr.bin/rdistd/filesys-os.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: filesys-os.c,v 1.1 1996/02/03 12:12:55 dm Exp $";
+
+static char sccsid[] = "@(#)filesys-os.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * OS specific file system routines
+ */
+
+#include "defs.h"
+#include "filesys.h"
+
+#if FSI_TYPE == FSI_GETFSSTAT
+static struct statfs *mnt = NULL;
+typedef u_long ulong;
+#if FSTYPENAME
+#define f_type_eq(a, b) (! strcmp (((struct statfs *) (a))->f_fstypename, (b)))
+#else /* !FSTYPENAME */
+#define f_type_eq(a, b) (((struct statfs *) a)->f_type == (b))
+#endif /* !FSTYPENAME */
+#endif /* FSI_GETFSSTAT */
+
+#if FSI_TYPE == FSI_MNTCTL
+static struct vmount *mnt = NULL;
+#endif /* FSI_MNTCTL */
+
+#if (FSI_TYPE == FSI_MNTCTL) || (FSI_TYPE == FSI_GETFSSTAT)
+static char *mntbuf = NULL;
+static int entries_left;
+#endif /* FSI_MNTCTL || FSI_GETFSSTAT */
+
+#if FSI_TYPE == FSI_MNTCTL
+/*
+ * AIX version of setmountent()
+ */
+FILE *setmountent(file, mode)
+ /*ARGSUSED*/
+ char *file;
+ char *mode;
+{
+ ulong size;
+
+ if (mntbuf)
+ (void) free(mntbuf);
+
+ mntctl(MCTL_QUERY, sizeof(size), &size);
+ mntbuf = (char *) xmalloc(size);
+
+ entries_left = mntctl(MCTL_QUERY, size, mntbuf);
+ if (!entries_left)
+ return((FILE *)NULL);
+
+ mnt = (struct vmount *)mntbuf;
+ return((FILE *) 1);
+}
+#endif /* FSI_MNTCTL */
+
+#if FSI_TYPE == FSI_GETFSSTAT
+/*
+ * getfsstat() version of get mount info routines.
+ */
+FILE *setmountent(file, mode)
+ /*ARGSUSED*/
+ char *file;
+ char *mode;
+{
+ ulong size;
+
+ if (mntbuf)
+ (void) free(mntbuf);
+
+ size = getfsstat((struct statfs *) NULL, 0, MNT_WAIT);
+ size *= sizeof(struct statfs);
+ mntbuf = (char *) xmalloc(size);
+
+ entries_left = getfsstat((struct statfs *)mntbuf, size, MNT_WAIT);
+ if (entries_left == -1)
+ return((FILE *) NULL);
+
+ mnt = (struct statfs *) mntbuf;
+
+ return((FILE *) 1);
+}
+#endif /* FSI_GETFSSTAT */
+
+#if FSI_TYPE == FSI_MNTCTL
+/*
+ * AIX version of getmountent()
+ */
+/*
+ * Iterate over mount entries
+ */
+mntent_t *getmountent(fptr)
+ /*ARGSUSED*/
+ FILE *fptr;
+{
+ static mntent_t mntstruct;
+
+ if (!entries_left)
+ return((mntent_t*)0);
+
+ bzero((char *) &mntstruct, sizeof(mntstruct));
+
+ if (mnt->vmt_flags & MNT_READONLY)
+ mntstruct.me_flags |= MEFLAG_READONLY;
+
+ mntstruct.me_path = vmt2dataptr(mnt, VMT_STUB);
+ switch ((ulong)(struct vmount*)mnt->vmt_gfstype) {
+ case MNT_NFS:
+ mntstruct.me_type = METYPE_NFS;
+ break;
+ default:
+ mntstruct.me_type = METYPE_OTHER;
+ break;
+ }
+
+ mnt = (struct vmount*)((mnt->vmt_length)+(char *)mnt);
+ entries_left--;
+
+ return(&mntstruct);
+}
+#endif /* FSI_MNTCTL */
+
+#if FSI_TYPE == FSI_GETFSSTAT
+/*
+ * getfsstat() version of getmountent()
+ */
+mntent_t *getmountent(fptr)
+ /*ARGSUSED*/
+ FILE *fptr;
+{
+ static mntent_t mntstruct;
+ static char remote_dev[MAXHOSTNAMELEN+MAXPATHLEN+1];
+
+ if (!entries_left)
+ return((mntent_t*)0);
+
+ bzero((char *) &mntstruct, sizeof(mntstruct));
+
+#if defined(MNT_RDONLY)
+ if (mnt->f_flags & MNT_RDONLY)
+ mntstruct.me_flags |= MEFLAG_READONLY;
+#endif
+#if defined(M_RDONLY)
+ if (mnt->f_flags & M_RDONLY)
+ mntstruct.me_flags |= MEFLAG_READONLY;
+#endif
+ if (f_type_eq (mnt, MOUNT_NFS)) {
+ (void) sprintf(remote_dev, "%s", mnt->f_mntfromname);
+ mntstruct.me_path = remote_dev;
+ mntstruct.me_type = METYPE_NFS;
+ }
+ else {
+ mntstruct.me_path = mnt->f_mntonname;
+ mntstruct.me_type = METYPE_OTHER;
+ }
+
+ mnt++;
+ entries_left--;
+
+ return(&mntstruct);
+}
+#endif
+
+#if (FSI_TYPE == FSI_MNTCTL) || (FSI_TYPE == FSI_GETFSSTAT)
+/*
+ * Done with iterations
+ */
+void endmountent(fptr)
+ /*ARGSUSED*/
+ FILE *fptr;
+{
+ mnt = NULL;
+
+ if (mntbuf) {
+ (void) free(mntbuf);
+ mntbuf = (char *) NULL;
+ }
+}
+#endif /* FSI_MNTCTL || FSI_GETFSSTAT */
+
+#if FSI_TYPE == FSI_GETMNTENT2
+/*
+ * Prepare to iterate over mounted filesystem list
+ */
+FILE *setmountent(file, mode)
+ /*ARGSUSED*/
+ char *file;
+ char *mode;
+{
+ return(fopen(file, mode));
+}
+
+/*
+ * Done with iteration
+ */
+void endmountent(fptr)
+ /*ARGSUSED*/
+ FILE *fptr;
+{
+ fclose(fptr);
+}
+
+/*
+ * Iterate over mount entries
+ */
+mntent_t *getmountent(fptr)
+ FILE *fptr;
+{
+ static mntent_t me;
+ static struct mnttab mntent;
+
+ bzero((char *)&me, sizeof(mntent_t));
+
+#if defined(UNICOS)
+ if (getmntent(fptr, &mntent) != NULL) {
+#else
+ if (getmntent(fptr, &mntent) != -1) {
+#endif
+ me.me_path = mntent.mnt_mountp;
+ me.me_type = mntent.mnt_fstype;
+ if (mntent.mnt_mntopts && hasmntopt(&mntent, MNTOPT_RO))
+ me.me_flags |= MEFLAG_READONLY;
+
+#if defined(MNTTYPE_IGNORE)
+ if (strcmp(mntent.mnt_fstype, MNTTYPE_IGNORE) == 0)
+ me.me_flags |= MEFLAG_IGNORE;
+#endif /* MNTTYPE_IGNORE */
+#if defined(MNTTYPE_SWAP)
+ if (strcmp(mntent.mnt_fstype, MNTTYPE_SWAP) == 0)
+ me.me_flags |= MEFLAG_IGNORE;
+#endif /* MNTTYPE_SWAP */
+
+ return(&me);
+ } else
+ return((mntent_t *) NULL);
+}
+#endif /* FSI_GETMNTNET2 */
+
+#if FSI_TYPE == FSI_GETMNTENT
+/*
+ * Prepare to iterate over mounted filesystem list
+ */
+FILE *setmountent(file, mode)
+ /*ARGSUSED*/
+ char *file;
+ char *mode;
+{
+ return(setmntent(file, mode));
+}
+
+/*
+ * Done with iteration
+ */
+void endmountent(fptr)
+ /*ARGSUSED*/
+ FILE *fptr;
+{
+ endmntent(fptr);
+}
+
+/*
+ * Iterate over mount entries
+ */
+mntent_t *getmountent(fptr)
+ FILE *fptr;
+{
+ static mntent_t me;
+ struct mntent *mntent;
+
+ bzero((char *)&me, sizeof(mntent_t));
+
+ if (mntent = getmntent(fptr)) {
+ me.me_path = mntent->mnt_dir;
+ me.me_type = mntent->mnt_type;
+ if (mntent->mnt_opts && hasmntopt(mntent, MNTOPT_RO))
+ me.me_flags |= MEFLAG_READONLY;
+
+#if defined(MNTTYPE_IGNORE)
+ if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
+ me.me_flags |= MEFLAG_IGNORE;
+#endif /* MNTTYPE_IGNORE */
+#if defined(MNTTYPE_SWAP)
+ if (strcmp(mntent->mnt_type, MNTTYPE_SWAP) == 0)
+ me.me_flags |= MEFLAG_IGNORE;
+#endif /* MNTTYPE_SWAP */
+
+ return(&me);
+ } else
+ return((mntent_t *) NULL);
+}
+#endif /* FSI_GETMNTNET */
+
+#if FSI_TYPE == FSI_GETMNT
+/*
+ * getmnt() interface (Ultrix)
+ */
+
+#include <sys/fs_types.h>
+
+static int startmounts = 0;
+
+FILE *setmountent(file, mode)
+ /*ARGSUSED*/
+ char *file;
+ char *mode;
+{
+ startmounts = 0;
+}
+
+void endmountent(fptr)
+ /*ARGSUSED*/
+ FILE *fptr;
+{
+ /* NOOP */
+}
+
+/*
+ * Iterate over mounted filesystems using getmnt()
+ */
+mntent_t *getmountent(fptr)
+ /*ARGSUSED*/
+ FILE *fptr;
+{
+ struct fs_data fs_data;
+ static mntent_t me;
+
+ if (getmnt(&startmounts, &fs_data, sizeof(fs_data), NOSTAT_MANY,
+ (char *) NULL) <= 0)
+ return((mntent_t *) NULL);
+
+ bzero((char *)&me, sizeof(mntent_t));
+ me.me_path = fs_data.fd_path;
+ if (fs_data.fd_fstype == GT_NFS)
+ me.me_type = METYPE_NFS;
+ else
+ me.me_type = METYPE_OTHER;
+
+ if (fs_data.fd_flags & M_RONLY)
+ me.me_flags |= MEFLAG_READONLY;
+
+ return(&me);
+}
+#endif /* FSI_GETMNT */
+
+/*
+ * Make a new (copy) of a mntent structure.
+ */
+mntent_t *newmountent(old)
+ mntent_t *old;
+{
+ mntent_t *new;
+
+ if (!old)
+ return((mntent_t *) NULL);
+
+ new = (mntent_t *) xcalloc(1, sizeof(mntent_t));
+ new->me_path = strdup(old->me_path);
+ new->me_type = strdup(old->me_type);
+ new->me_flags = old->me_flags;
+
+ return(new);
+}
diff --git a/usr.bin/rdistd/filesys.c b/usr.bin/rdistd/filesys.c
new file mode 100644
index 00000000000..5f7586fd3f3
--- /dev/null
+++ b/usr.bin/rdistd/filesys.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: filesys.c,v 1.1 1996/02/03 12:12:57 dm Exp $";
+
+static char sccsid[] = "@(#)filesys.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * This file contains functions dealing with getting info
+ * about mounted filesystems.
+ */
+
+#include "defs.h"
+#include "filesys.h"
+
+jmp_buf env;
+
+/*
+ * Given a pathname, find the fullest component that exists.
+ * If statbuf is not NULL, set it to point at our stat buffer.
+ */
+char *find_file(pathname, statbuf, isvalid)
+ char *pathname;
+ struct stat *statbuf;
+ int *isvalid;
+{
+ static char last_pathname[MAXPATHLEN];
+ static char file[MAXPATHLEN + 3];
+ static struct stat filestat;
+ register char *p;
+
+ /*
+ * Mark the statbuf as invalid to start with.
+ */
+ *isvalid = 0;
+
+ /*
+ * If this is the same pathname as the last time, and
+ * the file buffer is valid and we're doing the same stat()
+ * or lstat(), then set statbuf to the last filestat and
+ * return the last file we found.
+ */
+ if (strcmp(pathname, last_pathname) == 0 && file[0]) {
+ if (statbuf)
+ statbuf = &filestat;
+ if (strcmp(pathname, file) == 0)
+ *isvalid = 1;
+ return(file);
+ }
+
+ if ((int)strlen(pathname) > sizeof(file)+3) {
+ error("%s: Name to large for buffer.", pathname);
+ return((char *) NULL);
+ }
+
+ /*
+ * Save for next time
+ */
+ (void) strcpy(last_pathname, pathname);
+
+ if (*pathname == '/')
+ (void) strcpy(file, pathname);
+ else {
+ /*
+ * Ensure we have a directory (".") in our path
+ * so we have something to stat in case the file
+ * does not exist.
+ */
+ (void) strcpy(file, "./");
+ (void) strcat(file, pathname);
+ }
+
+ while (lstat(file, &filestat) != 0) {
+ /*
+ * Trim the last part of the pathname to try next level up
+ */
+ if (errno == ENOENT) {
+ /*
+ * Trim file name to get directory name.
+ * Normally we want to change /dir1/dir2/file
+ * into "/dir1/dir2/."
+ */
+ if (p = (char *) strrchr(file, '/')) {
+ *++p = '.';
+ *++p = CNULL;
+ } else {
+ /*
+ * Couldn't find anything, so give up.
+ */
+ debugmsg(DM_MISC, "Cannot find dir of `%s'",
+ pathname);
+ return((char *) NULL);
+ }
+ continue;
+ } else {
+ error("%s: lstat failed: %s", pathname, SYSERR);
+ return((char *) NULL);
+ }
+ }
+
+ if (statbuf)
+ bcopy((char *) &filestat, (char *) statbuf, sizeof(filestat));
+
+ /*
+ * Trim the "/." that we added.
+ */
+ p = &file[strlen(file) - 1];
+ if (*p == '.')
+ *p-- = CNULL;
+ for ( ; p && *p && *p == '/' && p != file; --p)
+ *p = CNULL;
+
+ /*
+ * If this file is a symlink we really want the parent directory
+ * name in case the symlink points to another filesystem.
+ */
+ if (S_ISLNK(filestat.st_mode))
+ if ((p = (char *) strrchr(file, '/')) && *p+1) {
+ /* Is this / (root)? */
+ if (p == file)
+ file[1] = CNULL;
+ else
+ *p = CNULL;
+ }
+
+ if (strcmp(pathname, file) == 0)
+ *isvalid = 1;
+
+ return((file && *file) ? file : (char *)NULL);
+}
+
+#if defined(NFS_CHECK) || defined(RO_CHECK)
+
+/*
+ * Find the device that "filest" is on in the "mntinfo" linked list.
+ */
+mntent_t *findmnt(filest, mntinfo)
+ struct stat *filest;
+ struct mntinfo *mntinfo;
+{
+ register struct mntinfo *mi;
+
+ for (mi = mntinfo; mi; mi = mi->mi_nxt) {
+ if (mi->mi_mnt->me_flags & MEFLAG_IGNORE)
+ continue;
+ if (filest->st_dev == mi->mi_statb->st_dev)
+ return(mi->mi_mnt);
+ }
+
+ return((mntent_t *) NULL);
+}
+
+/*
+ * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements?
+ */
+int isdupmnt(mnt, mntinfo)
+ mntent_t *mnt;
+ struct mntinfo *mntinfo;
+{
+ register struct mntinfo *m;
+
+ for (m = mntinfo; m; m = m->mi_nxt)
+ if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0)
+ return(1);
+
+ return(0);
+}
+
+/*
+ * Alarm clock
+ */
+void wakeup()
+{
+ debugmsg(DM_CALL, "wakeup() in filesys.c called");
+ longjmp(env, 1);
+}
+
+/*
+ * Make a linked list of mntinfo structures.
+ * Use "mi" as the base of the list if it's non NULL.
+ */
+struct mntinfo *makemntinfo(mi)
+ struct mntinfo *mi;
+{
+ FILE *mfp;
+ static struct mntinfo *mntinfo, *newmi, *m;
+ struct stat mntstat;
+ mntent_t *mnt;
+ int timeo = 310;
+
+ if (!(mfp = setmountent(MOUNTED_FILE, "r"))) {
+ message(MT_NERROR, "%s: setmntent failed: %s",
+ MOUNTED_FILE, SYSERR);
+ return((struct mntinfo *) NULL);
+ }
+
+ (void) signal(SIGALRM, wakeup);
+ (void) alarm(timeo);
+ if (setjmp(env)) {
+ message(MT_NERROR, "Timeout getting mount info");
+ return((struct mntinfo *) NULL);
+ }
+
+ mntinfo = mi;
+ while (mnt = getmountent(mfp)) {
+ debugmsg(DM_MISC, "mountent = '%s' (%s)",
+ mnt->me_path, mnt->me_type);
+
+ /*
+ * Make sure we don't already have it for some reason
+ */
+ if (isdupmnt(mnt, mntinfo))
+ continue;
+
+ /*
+ * Get stat info
+ */
+ if (stat(mnt->me_path, &mntstat) != 0) {
+ message(MT_WARNING, "%s: Cannot stat filesystem: %s",
+ mnt->me_path, SYSERR);
+ continue;
+ }
+
+ /*
+ * Create new entry
+ */
+ newmi = (struct mntinfo *) xcalloc(1, sizeof(struct mntinfo));
+ newmi->mi_mnt = newmountent(mnt);
+ newmi->mi_statb =
+ (struct stat *) xcalloc(1, sizeof(struct stat));
+ bcopy((char *) &mntstat, (char *) newmi->mi_statb,
+ sizeof(struct stat));
+
+ /*
+ * Add entry to list
+ */
+ if (mntinfo) {
+ for (m = mntinfo; m && m->mi_nxt; m = m->mi_nxt);
+ m->mi_nxt = newmi;
+ } else
+ mntinfo = newmi;
+ }
+
+ (void) alarm(0);
+ (void) endmountent(mfp);
+
+ return(mntinfo);
+}
+
+/*
+ * Given a name like /usr/src/etc/foo.c returns the mntent
+ * structure for the file system it lives in.
+ *
+ * If "statbuf" is not NULL it is used as the stat buffer too avoid
+ * stat()'ing the file again back in server.c.
+ */
+mntent_t *getmntpt(pathname, statbuf, isvalid)
+ char *pathname;
+ struct stat *statbuf;
+ int *isvalid;
+{
+ static struct mntinfo *mntinfo = NULL;
+ static struct stat filestat;
+ struct stat *pstat;
+ struct mntinfo *tmpmi;
+ register mntent_t *mnt;
+
+ /*
+ * Use the supplied stat buffer if not NULL or our own.
+ */
+ if (statbuf)
+ pstat = statbuf;
+ else
+ pstat = &filestat;
+
+ if (!find_file(pathname, pstat, isvalid))
+ return((mntent_t *) NULL);
+
+ /*
+ * Make mntinfo if it doesn't exist.
+ */
+ if (!mntinfo)
+ mntinfo = makemntinfo((struct mntinfo *) NULL);
+
+ /*
+ * Find the mnt that pathname is on.
+ */
+ if (mnt = findmnt(pstat, mntinfo))
+ return(mnt);
+
+ /*
+ * We failed to find correct mnt, so maybe it's a newly
+ * mounted filesystem. We rebuild mntinfo and try again.
+ */
+ if (tmpmi = makemntinfo(mntinfo)) {
+ mntinfo = tmpmi;
+ if (mnt = findmnt(pstat, mntinfo))
+ return(mnt);
+ }
+
+ error("%s: Could not find mount point", pathname);
+ return((mntent_t *) NULL);
+}
+
+#endif /* NFS_CHECK || RO_CHECK */
+
+#if defined(NFS_CHECK)
+/*
+ * Is "path" NFS mounted? Return 1 if it is, 0 if not, or -1 on error.
+ */
+int is_nfs_mounted(path, statbuf, isvalid)
+ char *path;
+ struct stat *statbuf;
+ int *isvalid;
+{
+ mntent_t *mnt;
+
+ if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
+ return(-1);
+
+ if (strcmp(mnt->me_type, METYPE_NFS) == 0)
+ return(1);
+
+ return(0);
+}
+#endif /* NFS_CHECK */
+
+#if defined(RO_CHECK)
+/*
+ * Is "path" on a read-only mounted filesystem?
+ * Return 1 if it is, 0 if not, or -1 on error.
+ */
+int is_ro_mounted(path, statbuf, isvalid)
+ char *path;
+ struct stat *statbuf;
+ int *isvalid;
+{
+ mntent_t *mnt;
+
+ if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
+ return(-1);
+
+ if (mnt->me_flags & MEFLAG_READONLY)
+ return(1);
+
+ return(0);
+}
+#endif /* RO_CHECK */
+
+/*
+ * Is "path" a symlink?
+ * Return 1 if it is, 0 if not, or -1 on error.
+ */
+int is_symlinked(path, statbuf, isvalid)
+ /*ARGSUSED*/
+ char *path;
+ struct stat *statbuf;
+ int *isvalid;
+{
+ static struct stat stb;
+
+ if (!(*isvalid)) {
+ if (lstat(path, &stb) != 0)
+ return(-1);
+ statbuf = &stb;
+ }
+
+ if (S_ISLNK(statbuf->st_mode))
+ return(1);
+
+ return(0);
+}
+
+/*
+ * Get filesystem information for "file". Set freespace
+ * to the amount of free (available) space and number of free
+ * files (inodes) on the filesystem "file" resides on.
+ * Returns 0 on success or -1 on failure.
+ * Filesystem values < 0 indicate unsupported or unavailable
+ * information.
+ */
+int getfilesysinfo(file, freespace, freefiles)
+ char *file;
+ long *freespace;
+ long *freefiles;
+{
+#if defined(STATFS_TYPE)
+ static statfs_t statfsbuf;
+ char *mntpt;
+ int t, r;
+
+ /*
+ * Get the mount point of the file.
+ */
+ mntpt = find_file(file, NULL, &t);
+ if (!mntpt) {
+ debugmsg(DM_MISC, "unknown mount point for `%s'", file);
+ return(-1);
+ }
+
+ /*
+ * Stat the filesystem (system specific)
+ */
+#if STATFS_TYPE == STATFS_SYSV
+ r = statfs(mntpt, &statfsbuf, sizeof(statfs_t), 0);
+#endif
+#if STATFS_TYPE == STATFS_BSD
+ r = statfs(mntpt, &statfsbuf);
+#endif
+#if STATFS_TYPE == STATFS_OSF1
+ r = statfs(mntpt, &statfsbuf, sizeof(statfs_t));
+#endif
+
+ if (r < 0) {
+ error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR);
+ return(-1);
+ }
+
+ /*
+ * If values are < 0, then assume the value is unsupported
+ * or unavailable for that filesystem type.
+ */
+ if (statfsbuf.f_bavail >= 0)
+ *freespace = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512))
+ / 2;
+
+ /*
+ * BROKEN_STATFS means that statfs() does not set fields
+ * to < 0 if the field is unsupported for the filesystem type.
+ */
+#if defined(BROKEN_STATFS)
+ if (statfsbuf.f_ffree > 0)
+#else
+ if (statfsbuf.f_ffree >= 0)
+#endif /* BROKEN_STATFS */
+ *freefiles = statfsbuf.f_ffree;
+
+#else /* !STATFS_TYPE */
+
+ *freespace = *freefiles = -1;
+
+#endif /* STATFS_TYPE */
+
+ return(0);
+}
diff --git a/usr.bin/rdistd/filesys.h b/usr.bin/rdistd/filesys.h
new file mode 100644
index 00000000000..6416b36dbdd
--- /dev/null
+++ b/usr.bin/rdistd/filesys.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1983 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.
+ */
+
+/*
+ * $Id: filesys.h,v 1.1 1996/02/03 12:12:58 dm Exp $
+ * @(#)filesys.h
+ */
+
+#ifndef __filesys_h__
+#define __filesys_h__
+
+/*
+ * File System information
+ */
+
+/*
+ * Mount information
+ */
+#if FSI_TYPE == FSI_GETMNT
+# include <sys/types.h>
+# include <sys/param.h>
+# include <sys/mount.h>
+# define MOUNTED_FILE "<none>"
+#endif
+
+#if FSI_TYPE == FSI_GETFSSTAT
+# include <sys/types.h>
+# include <sys/mount.h>
+# define MOUNTED_FILE "<none>"
+#endif
+
+#if FSI_TYPE == FSI_MNTCTL
+# include <sys/mntctl.h>
+# define MOUNTED_FILE "<none>"
+#endif
+
+#if FSI_TYPE == FSI_GETMNTENT
+# include <mntent.h>
+# define MOUNTED_FILE MOUNTED
+#endif
+
+#if FSI_TYPE == FSI_GETMNTENT2
+#if defined(MNTTAB_H)
+# include MNTTAB_H
+#endif /* MNTTAB_H */
+#if defined(MNTENT_H)
+# include MNTENT_H
+#endif /* MNTENT_H */
+# define MOUNTED_FILE MNTTAB
+#endif /* FSI_GETMNTENT2 */
+
+#if !defined(MOUNTED_FILE) && defined(MNT_MNTTAB) /* HPUX */
+# define MOUNTED_FILE MNT_MNTTAB
+#endif /* MNT_MNTTAB */
+
+/*
+ * NCR OS defines bcopy and bzero
+ */
+#if defined(NCR)
+#undef bcopy
+#undef bzero
+#endif /* NCR */
+
+/*
+ * Stat Filesystem
+ */
+#if defined(STATFS_TYPE)
+#if defined(ultrix)
+ typedef struct fs_data statfs_t;
+# define f_bavail fd_req.bfreen
+# define f_bsize fd_req.bsize
+# define f_ffree fd_req.gfree
+#else
+#if defined(_AIX) || STATFS_TYPE == STATFS_SYSV
+# include <sys/statfs.h>
+ typedef struct statfs statfs_t;
+# define f_bavail f_bfree
+#else
+#if defined(SVR4)
+# include <sys/statvfs.h>
+ typedef struct statvfs statfs_t;
+# define statfs(mp,sb) statvfs(mp,sb)
+#else
+#if defined(BSD386) || defined(__bsdi__) || defined(FREEBSD) || STATFS_TYPE == STATFS_OSF1
+ typedef struct statfs statfs_t;
+#else
+# include <sys/vfs.h>
+ typedef struct statfs statfs_t;
+#endif /* BSD386 */
+#endif /* SVR4 */
+#endif /* _AIX */
+#endif /* ultrix */
+#endif /* STATFS_TYPE */
+
+/*
+ * Mount Entry definetions
+ */
+#ifndef METYPE_OTHER
+#define METYPE_OTHER "other"
+#endif
+#ifndef METYPE_NFS
+#define METYPE_NFS "nfs"
+#endif
+#ifndef MEFLAG_READONLY
+#define MEFLAG_READONLY 0x01
+#endif
+#ifndef MEFLAG_IGNORE
+#define MEFLAG_IGNORE 0x02
+#endif
+
+/*
+ * Our internal mount entry type
+ */
+struct _mntent {
+ char *me_path; /* Mounted path */
+ char *me_type; /* Type of mount */
+ int me_flags; /* Mount flags */
+};
+typedef struct _mntent mntent_t;
+
+/*
+ * Internal mount information type
+ */
+struct mntinfo {
+ mntent_t *mi_mnt;
+ struct stat *mi_statb;
+ struct mntinfo *mi_nxt;
+};
+
+/*
+ * Declarations
+ */
+FILE *setmountent();
+mntent_t *getmountent();
+mntent_t *newmountent();
+void endmountent();
+
+#endif /* __filesys_h__ */
diff --git a/usr.bin/rdistd/message.c b/usr.bin/rdistd/message.c
new file mode 100644
index 00000000000..4b753ade3c6
--- /dev/null
+++ b/usr.bin/rdistd/message.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: message.c,v 1.1 1996/02/03 12:12:59 dm Exp $";
+
+static char sccsid[] = "@(#)common.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* !lint */
+
+/*
+ * Message handling functions for both rdist and rdistd.
+ */
+
+#include "defs.h"
+
+#define MSGBUFSIZ 32*1024
+
+int debug = 0; /* Debugging level */
+int nerrs = 0; /* Number of errors */
+char *tempfile = NULL; /* Name of temporary file */
+
+/*
+ * Message Types
+ */
+MSGTYPE msgtypes[] = {
+ { MT_CHANGE, "change" },
+ { MT_INFO, "info" },
+ { MT_NOTICE, "notice" },
+ { MT_NERROR, "nerror" },
+ { MT_FERROR, "ferror" },
+ { MT_WARNING, "warning" },
+ { MT_VERBOSE, "verbose" },
+ { MT_ALL, "all" },
+ { MT_DEBUG, "debug" },
+ { 0 },
+};
+
+static void msgsendstdout(), msgsendfile(), msgsendsyslog(),
+ msgsendnotify();
+
+/*
+ * Message Facilities
+ */
+MSGFACILITY msgfacility[] = {
+ { MF_STDOUT, "stdout", msgsendstdout },
+ { MF_FILE, "file", msgsendfile },
+ { MF_SYSLOG, "syslog", msgsendsyslog },
+ { MF_NOTIFY, "notify", msgsendnotify },
+ { 0 },
+};
+
+/*
+ * Print message logging usage message
+ */
+extern void msgprusage()
+{
+ register int i, x;
+
+ (void) fprintf(stderr, "\nWhere <msgopt> is of form\n");
+ (void) fprintf(stderr,
+ "\t<facility1>=<type1>,<type2>,...:<facility2>=<type1>,<type2>...\n");
+
+ (void) fprintf(stderr, "Valid <facility> names:");
+
+ for (i = 0; msgfacility[i].mf_name; ++i)
+ (void) fprintf(stderr, " %s", msgfacility[i].mf_name);
+
+ (void) fprintf(stderr, "\nValid <type> names:");
+ for (x = 0; msgtypes[x].mt_name; ++x)
+ (void) fprintf(stderr, " %s", msgtypes[x].mt_name);
+
+ (void) fprintf(stderr, "\n");
+}
+
+/*
+ * Print enabled message logging info
+ */
+extern void msgprconfig()
+{
+ register int i, x;
+ static char buf[MSGBUFSIZ];
+
+ debugmsg(DM_MISC, "Current message logging config:");
+ for (i = 0; msgfacility[i].mf_name; ++i) {
+ (void) sprintf(buf, " %s=", msgfacility[i].mf_name);
+ for (x = 0; msgtypes[x].mt_name; ++x)
+ if (IS_ON(msgfacility[i].mf_msgtypes,
+ msgtypes[x].mt_type)) {
+ if (x > 0)
+ (void) strcat(buf, ",");
+ (void) strcat(buf, msgtypes[x].mt_name);
+ }
+ debugmsg(DM_MISC, "%s", buf);
+ }
+
+}
+
+/*
+ * Get the Message Facility entry "name"
+ */
+static MSGFACILITY *getmsgfac(name)
+ char *name;
+{
+ register int i;
+
+ for (i = 0; msgfacility[i].mf_name; ++i)
+ if (strcasecmp(name, msgfacility[i].mf_name) == 0)
+ return(&msgfacility[i]);
+
+ return((MSGFACILITY *) NULL);
+}
+
+/*
+ * Get the Message Type entry named "name"
+ */
+static MSGTYPE *getmsgtype(name)
+ char *name;
+{
+ register int i;
+
+ for (i = 0; msgtypes[i].mt_name; ++i)
+ if (strcasecmp(name, msgtypes[i].mt_name) == 0)
+ return(&msgtypes[i]);
+
+ return((MSGTYPE *) NULL);
+}
+
+/*
+ * Set Message Type information for Message Facility "msgfac" as
+ * indicated by string "str".
+ */
+static char *setmsgtypes(msgfac, str)
+ MSGFACILITY *msgfac;
+ char *str;
+{
+ static char ebuf[BUFSIZ];
+ register char *cp;
+ register char *strptr, *word;
+ register MSGTYPE *mtp;
+
+ /*
+ * MF_SYSLOG is the only supported message facility for the server
+ */
+ if (isserver && (msgfac->mf_msgfac != MF_SYSLOG &&
+ msgfac->mf_msgfac != MF_FILE)) {
+ (void) sprintf(ebuf,
+ "The \"%s\" message facility cannot be used by the server.",
+ msgfac->mf_name);
+ return(ebuf);
+ }
+
+ strptr = str;
+
+ /*
+ * Do any necessary Message Facility preparation
+ */
+ switch(msgfac->mf_msgfac) {
+ case MF_FILE:
+ /*
+ * The MF_FILE string should look like "<file>=<types>".
+ */
+ if ((cp = strchr(strptr, '=')) == NULL)
+ return(
+ "No file name found for \"file\" message facility");
+ *cp++ = CNULL;
+
+ if ((msgfac->mf_fptr = fopen(strptr, "w")) == NULL)
+ fatalerr("Cannot open log file for writing: %s: %s.",
+ strptr, SYSERR);
+ msgfac->mf_filename = strdup(strptr);
+
+ strptr = cp;
+ break;
+
+ case MF_NOTIFY:
+ break;
+
+ case MF_STDOUT:
+ msgfac->mf_fptr = stdout;
+ break;
+
+ case MF_SYSLOG:
+#if defined(LOG_OPTS)
+#if defined(LOG_FACILITY)
+ openlog(progname, LOG_OPTS, LOG_FACILITY);
+#else
+ openlog(progname, LOG_OPTS);
+#endif /* LOG_FACILITY */
+#endif /* LOG_OPTS */
+ break;
+ }
+
+ /*
+ * Parse each type word
+ */
+ msgfac->mf_msgtypes = 0; /* Start from scratch */
+ while (strptr) {
+ word = strptr;
+ if (cp = strchr(strptr, ','))
+ *cp++ = CNULL;
+ strptr = cp;
+
+ if (mtp = getmsgtype(word)) {
+ msgfac->mf_msgtypes |= mtp->mt_type;
+ /*
+ * XXX This is really a kludge until we add real
+ * control over debugging.
+ */
+ if (!debug && isserver &&
+ strcasecmp(word, "debug") == 0)
+ debug = DM_ALL;
+ } else {
+ (void) sprintf(ebuf, "Message type \"%s\" is invalid.",
+ word);
+ return(ebuf);
+ }
+ }
+
+ return((char *) NULL);
+}
+
+/*
+ * Parse a message logging option string
+ */
+extern char *msgparseopts(msgstr, doset)
+ char *msgstr;
+ int doset;
+{
+ static char ebuf[BUFSIZ], msgbuf[MSGBUFSIZ];
+ register char *cp, *optstr;
+ register char *word;
+ MSGFACILITY *msgfac;
+
+ if (msgstr == NULL)
+ return("NULL message string");
+
+ /* strtok() is harmful */
+ (void) strcpy(msgbuf, msgstr);
+
+ /*
+ * Each <facility>=<types> list is seperated by ":".
+ */
+ for (optstr = strtok(msgbuf, ":"); optstr;
+ optstr = strtok((char *)NULL, ":")) {
+
+ if ((cp = strchr(optstr, '=')) == NULL)
+ return("No '=' found");
+
+ *cp++ = CNULL;
+ word = optstr;
+ if ((int)strlen(word) <= 0)
+ return("No message facility specified");
+ if ((int)strlen(cp) <= 0)
+ return("No message type specified");
+
+ if ((msgfac = getmsgfac(word)) == NULL) {
+ (void) sprintf(ebuf,
+ "%s is not a valid message facility",
+ word);
+ return(ebuf);
+ }
+
+ if (doset) {
+ char *mcp;
+
+ if (mcp = setmsgtypes(msgfac, cp))
+ return(mcp);
+ }
+ }
+
+ if (isserver && debug) {
+ debugmsg(DM_MISC, "%s", getversion());
+ msgprconfig();
+ }
+
+ return((char *) NULL);
+}
+
+/*
+ * Send a message to facility "stdout".
+ * For rdistd, this is really the rdist client.
+ */
+static void msgsendstdout(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ char cmd;
+
+ if (isserver) {
+ if (rem_w < 0 || IS_ON(flags, MT_NOREMOTE))
+ return;
+
+ cmd = CNULL;
+
+ switch(mtype) {
+ case MT_NERROR: cmd = C_ERRMSG; break;
+ case MT_FERROR: cmd = C_FERRMSG; break;
+ case MT_NOTICE: cmd = C_NOTEMSG; break;
+ case MT_REMOTE: cmd = C_LOGMSG; break;
+ }
+
+ if (cmd != CNULL)
+ (void) sendcmd(cmd, "%s", msgbuf);
+ } else {
+ switch(mtype) {
+ case MT_FERROR:
+ case MT_NERROR:
+ if (msgbuf && *msgbuf) {
+ (void) fprintf(stderr, "%s\n", msgbuf);
+ (void) fflush(stderr);
+ }
+ break;
+
+ case MT_DEBUG:
+ /*
+ * Only things that are strictly MT_DEBUG should
+ * be shown.
+ */
+ if (flags != MT_DEBUG)
+ return;
+ case MT_NOTICE:
+ case MT_CHANGE:
+ case MT_INFO:
+ case MT_VERBOSE:
+ case MT_WARNING:
+ if (msgbuf && *msgbuf) {
+ (void) printf("%s\n", msgbuf);
+ (void) fflush(stdout);
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Send a message to facility "syslog"
+ */
+static void msgsendsyslog(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ int syslvl = 0;
+
+ if (!msgbuf || !*msgbuf)
+ return;
+
+ switch(mtype) {
+#if defined(SL_NERROR)
+ case MT_NERROR: syslvl = SL_NERROR; break;
+#endif
+#if defined(SL_FERROR)
+ case MT_FERROR: syslvl = SL_FERROR; break;
+#endif
+#if defined(SL_WARNING)
+ case MT_WARNING: syslvl = SL_WARNING; break;
+#endif
+#if defined(SL_CHANGE)
+ case MT_CHANGE: syslvl = SL_CHANGE; break;
+#endif
+#if defined(SL_INFO)
+ case MT_SYSLOG:
+ case MT_VERBOSE:
+ case MT_INFO: syslvl = SL_INFO; break;
+#endif
+#if defined(SL_NOTICE)
+ case MT_NOTICE: syslvl = SL_NOTICE; break;
+#endif
+#if defined(SL_DEBUG)
+ case MT_DEBUG: syslvl = SL_DEBUG; break;
+#endif
+ }
+
+ if (syslvl)
+ syslog(syslvl, "%s", msgbuf);
+}
+
+/*
+ * Send a message to a "file" facility.
+ */
+static void msgsendfile(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ if (msgfac->mf_fptr == NULL)
+ return;
+
+ if (!msgbuf || !*msgbuf)
+ return;
+
+ (void) fprintf(msgfac->mf_fptr, "%s\n", msgbuf);
+ (void) fflush(msgfac->mf_fptr);
+}
+
+/*
+ * Same method as msgsendfile()
+ */
+static void msgsendnotify(msgfac, mtype, flags, msgbuf)
+ /*ARGSUSED*/
+ MSGFACILITY *msgfac;
+ int mtype;
+ int flags;
+ char *msgbuf;
+{
+ if (IS_ON(flags, MT_DEBUG))
+ return;
+
+ if (!msgbuf || !*msgbuf)
+ return;
+
+ if (!msgfac->mf_fptr) {
+ register char *cp;
+ char *getenv();
+
+ /*
+ * Create and open a new temporary file
+ */
+ if ((cp = getenv("TMPDIR")) == (char *) NULL)
+ cp = _PATH_TMP;
+ tempfile = (char *) xmalloc(strlen(cp) + 1 +
+ strlen(_RDIST_TMP) + 2);
+ (void) sprintf(tempfile, "%s/%s", cp, _RDIST_TMP);
+
+ msgfac->mf_filename = tempfile;
+ (void) mktemp(msgfac->mf_filename);
+ if ((msgfac->mf_fptr = fopen(msgfac->mf_filename, "w"))==NULL)
+ fatalerr("Cannot open notify file for writing: %s: %s.",
+ msgfac->mf_filename, SYSERR);
+ debugmsg(DM_MISC, "Created notify temp file '%s'",
+ msgfac->mf_filename);
+ }
+
+ if (msgfac->mf_fptr == NULL)
+ return;
+
+ (void) fprintf(msgfac->mf_fptr, "%s\n", msgbuf);
+ (void) fflush(msgfac->mf_fptr);
+}
+
+/*
+ * Insure currenthost is set to something reasonable.
+ */
+extern void checkhostname()
+{
+ static char mbuf[MAXHOSTNAMELEN];
+ char *cp;
+
+ if (!currenthost) {
+ if (gethostname(mbuf, sizeof(mbuf)) == 0) {
+ if ((cp = strchr(mbuf, '.')) != NULL)
+ *cp = CNULL;
+ currenthost = strdup(mbuf);
+ } else
+ currenthost = "(unknown)";
+ }
+}
+
+/*
+ * Print a message contained in "msgbuf" if a level "lvl" is set.
+ */
+static void _message(flags, msgbuf)
+ int flags;
+ char *msgbuf;
+{
+ register int i, x;
+ register char *cp;
+ static char mbuf[2048];
+
+ if (msgbuf && *msgbuf) {
+ /*
+ * Ensure no stray newlines are present
+ */
+ if (cp = strchr(msgbuf, '\n'))
+ *cp = CNULL;
+
+ checkhostname();
+ if (strncmp(currenthost, msgbuf, strlen(currenthost)) == 0)
+ (void) strcpy(mbuf, msgbuf);
+ else
+ (void) sprintf(mbuf, "%s: %s", currenthost, msgbuf);
+ } else
+ (void) strcpy(mbuf, "");
+
+ /*
+ * Special case for messages that only get
+ * logged to the system log facility
+ */
+ if (IS_ON(flags, MT_SYSLOG)) {
+ msgsendsyslog((MSGFACILITY *)NULL, MT_SYSLOG, flags, mbuf);
+ return;
+ }
+
+ /*
+ * Special cases
+ */
+ if (isserver && IS_ON(flags, MT_REMOTE))
+ msgsendstdout((MSGFACILITY *)NULL, MT_REMOTE, flags, mbuf);
+ else if (isserver && IS_ON(flags, MT_NERROR))
+ msgsendstdout((MSGFACILITY *)NULL, MT_NERROR, flags, mbuf);
+ else if (isserver && IS_ON(flags, MT_FERROR))
+ msgsendstdout((MSGFACILITY *)NULL, MT_FERROR, flags, mbuf);
+ else if (isserver && IS_ON(flags, MT_NOTICE)) {
+ msgsendstdout((MSGFACILITY *)NULL, MT_NOTICE, flags, mbuf);
+ return;
+ }
+
+ /*
+ * For each Message Facility, check each Message Type to see
+ * if the bits in "flags" are set. If so, call the appropriate
+ * Message Facility to dispatch the message.
+ */
+ for (i = 0; msgfacility[i].mf_name; ++i)
+ for (x = 0; msgtypes[x].mt_name; ++x)
+ /*
+ * XXX MT_ALL should not be used directly
+ */
+ if (msgtypes[x].mt_type != MT_ALL &&
+ IS_ON(flags, msgtypes[x].mt_type) &&
+ IS_ON(msgfacility[i].mf_msgtypes,
+ msgtypes[x].mt_type))
+ (*msgfacility[i].mf_sendfunc)(&msgfacility[i],
+ msgtypes[x].mt_type,
+ flags,
+ mbuf);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs front-end to _message()
+ */
+extern void message(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+ int lvl;
+
+ va_start(args);
+ lvl = (int) va_arg(args, int);
+ fmt = (char *) va_arg(args, char *);
+ va_end(args);
+
+ (void) vsprintf(buf, fmt, args);
+
+ _message(lvl, buf);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg front-end to _message()
+ */
+extern void message(int lvl, char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _message(lvl, buf);
+}
+#endif /* ARG_STDARG */
+
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple front-end to _message()
+ */
+/*VARARGS2*/
+extern void message(lvl, fmt, a1, a2, a3, a4, a5)
+ int lvl;
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5);
+
+ _message(lvl, buf);
+}
+#endif /* !ARG_TYPE */
+
+/*
+ * Display a debugging message
+ */
+static void _debugmsg(lvl, buf)
+ int lvl;
+ char *buf;
+{
+ if (IS_ON(debug, lvl))
+ _message(MT_DEBUG, buf);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs front-end to _debugmsg()
+ */
+extern void debugmsg(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+ int lvl;
+
+ va_start(args);
+ lvl = (int) va_arg(args, int);
+ fmt = (char *) va_arg(args, char *);
+ va_end(args);
+
+ (void) vsprintf(buf, fmt, args);
+
+ _debugmsg(lvl, buf);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg front-end to _debugmsg()
+ */
+extern void debugmsg(int lvl, char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _debugmsg(lvl, buf);
+}
+#endif /* ARG_STDARG */
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple front-end to _debugmsg()
+ */
+/*VARARGS2*/
+extern void debugmsg(lvl, fmt, a1, a2, a3, a4, a5)
+ int lvl;
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5);
+
+ _debugmsg(lvl, buf);
+}
+#endif /* ARG_TYPE */
+
+/*
+ * Print an error message
+ */
+static void _error(msg)
+ char *msg;
+{
+ static char buf[MSGBUFSIZ];
+
+ nerrs++;
+ buf[0] = CNULL;
+
+ if (msg) {
+ if (isserver)
+ (void) sprintf(buf, "REMOTE ERROR: %s", msg);
+ else
+ (void) sprintf(buf, "LOCAL ERROR: %s", msg);
+ }
+
+ _message(MT_NERROR, (buf[0]) ? buf : NULL);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs frontend to _error()
+ */
+extern void error(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+
+ buf[0] = CNULL;
+ va_start(args);
+ fmt = (char *) va_arg(args, char *);
+ if (fmt)
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _error((buf[0]) ? buf : NULL);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg frontend to _error()
+ */
+extern void error(char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ buf[0] = CNULL;
+ va_start(args, fmt);
+ if (fmt)
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _error((buf[0]) ? buf : NULL);
+}
+#endif /* ARG_STDARG */
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple frontend to _error()
+ */
+/*VARARGS1*/
+extern void error(fmt, a1, a2, a3, a4, a5, a6)
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ buf[0] = CNULL;
+ if (fmt)
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5, a6);
+
+ _error((buf[0]) ? buf : NULL);
+}
+#endif /* ARG_TYPE */
+
+/*
+ * Display a fatal message
+ */
+static void _fatalerr(msg)
+ char *msg;
+{
+ static char buf[MSGBUFSIZ];
+
+ ++nerrs;
+
+ if (isserver)
+ (void) sprintf(buf, "REMOTE ERROR: %s", msg);
+ else
+ (void) sprintf(buf, "LOCAL ERROR: %s", msg);
+
+ _message(MT_FERROR, buf);
+
+ exit(nerrs);
+}
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs front-end to _fatalerr()
+ */
+extern void fatalerr(va_alist)
+ va_dcl
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+ char *fmt;
+
+ va_start(args);
+ fmt = (char *) va_arg(args, char *);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _fatalerr(buf);
+}
+#endif /* ARG_VARARGS */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg front-end to _fatalerr()
+ */
+extern void fatalerr(char *fmt, ...)
+{
+ static char buf[MSGBUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ _fatalerr(buf);
+}
+#endif /* ARG_STDARG */
+
+#if !defined(ARG_TYPE)
+/*
+ * Simple front-end to _fatalerr()
+ */
+/*VARARGS1*/
+extern void fatalerr(fmt, a1, a2, a3, a4, a5)
+ char *fmt;
+{
+ static char buf[MSGBUFSIZ];
+
+ (void) sprintf(buf, fmt, a1, a2, a3, a4, a5);
+
+ _fatalerr(buf);
+}
+#endif /* !ARG_TYPE */
+
+/*
+ * Get the name of the file used for notify.
+ * A side effect is that the file pointer to the file
+ * is closed. We assume this function is only called when
+ * we are ready to read the file.
+ */
+extern char *getnotifyfile()
+{
+ register int i;
+
+ for (i = 0; msgfacility[i].mf_name; i++)
+ if (msgfacility[i].mf_msgfac == MF_NOTIFY &&
+ msgfacility[i].mf_fptr) {
+ (void) fclose(msgfacility[i].mf_fptr);
+ msgfacility[i].mf_fptr = NULL;
+ return(msgfacility[i].mf_filename);
+ }
+
+ return((char *) NULL);
+}
diff --git a/usr.bin/rdistd/rdistd.1 b/usr.bin/rdistd/rdistd.1
new file mode 100644
index 00000000000..10eca5e588b
--- /dev/null
+++ b/usr.bin/rdistd/rdistd.1
@@ -0,0 +1,83 @@
+.\"
+.\" Copyright (c) 1983 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.
+.\"
+.\" $Id: rdistd.1,v 1.1 1996/02/03 12:13:01 dm Exp $
+.\" @(#)rdistd.8 6.6 (Berkeley) 5/13/86
+.\"
+.TH RDISTD 8 "June 21, 1992"
+.UC 6
+.SH NAME
+rdistd \- remote file distribution server program
+.SH SYNOPSIS
+.B rdistd
+.B \-S
+[
+.B \-D
+]
+.PP
+.B rdistd
+.B \-V
+.SH DESCRIPTION
+.I Rdistd
+is the server program for the
+.I rdist
+command.
+It is normally run by
+.I rdist
+via
+.I rsh(1).
+.PP
+The
+.B \-S
+argument must be specified.
+The option is required so that
+.I rdistd
+is not accidentally started
+since
+it
+normally resides somewhere in a normal user's
+.B $PATH.
+.SH OPTIONS
+.TP
+.B \-D
+Enable debugging messages.
+Messages are logged via
+.I syslog(3).
+.TP
+.B \-V
+Print version information and exit.
+.SH FILES
+.SH "SEE ALSO"
+rdist(1), rsh(1)
+.SH BUGS
+
+
diff --git a/usr.bin/rdistd/rdistd.c b/usr.bin/rdistd/rdistd.c
new file mode 100644
index 00000000000..db0452420f6
--- /dev/null
+++ b/usr.bin/rdistd/rdistd.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: rdistd.c,v 1.1 1996/02/03 12:13:02 dm Exp $";
+
+static char sccsid[] = "@(#)rdistd.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+
+#include "defs.h"
+
+/*
+ * Print usage message
+ */
+static void usage()
+{
+ fprintf(stderr, "usage: %s -S [ -DV ]\n", progname);
+ exit(1);
+}
+
+char localmsglist[] = "syslog=ferror";
+
+/*
+ * The Beginning
+ */
+main(argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ char *cp;
+ int c;
+
+ if (init(argc, argv, envp) < 0)
+ exit(1);
+
+ while ((c = getopt(argc, argv, "SDV")) != -1)
+ switch (c) {
+ case 'S':
+ isserver++;
+ break;
+
+ case 'D':
+ debug++;
+ break;
+
+ case 'V':
+ printf("%s\n", getversion());
+ exit(0);
+
+ case '?':
+ default:
+ error("Bad command line option.");
+ usage();
+ }
+
+ if (!isserver) {
+ error("Use the -S option to run this program in server mode.");
+ exit(1);
+ }
+
+ /* Use stdin and stdout for remote descriptors */
+ rem_r = fileno(stdin);
+ rem_w = fileno(stdout);
+
+ /* Set logging */
+ if (cp = msgparseopts(localmsglist, TRUE))
+ fatalerr("Bad message logging option (%s): %s",
+ localmsglist, cp);
+
+ /*
+ * Main processing function
+ */
+ server();
+
+ exit(nerrs != 0);
+}
diff --git a/usr.bin/rdistd/server.c b/usr.bin/rdistd/server.c
new file mode 100644
index 00000000000..7331068468a
--- /dev/null
+++ b/usr.bin/rdistd/server.c
@@ -0,0 +1,1642 @@
+/*
+ * Copyright (c) 1983 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 RCSid[] =
+"$Id: server.c,v 1.1 1996/02/03 12:13:03 dm Exp $";
+
+static char sccsid[] = "@(#)server.c 5.3 (Berkeley) 6/7/86";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * Server routines
+ */
+
+#include "defs.h"
+
+char tempname[sizeof _RDIST_TMP + 1]; /* Tmp file name */
+char buf[BUFSIZ]; /* general purpose buffer */
+char target[MAXPATHLEN]; /* target/source directory name */
+char *ptarget; /* pointer to end of target name */
+int catname = 0; /* cat name to target name */
+char *sptarget[32]; /* stack of saved ptarget's for directories */
+char *fromhost = NULL; /* Client hostname */
+static long min_freespace = 0; /* Minimium free space on a filesystem */
+static long min_freefiles = 0; /* Minimium free # files on a filesystem */
+int oumask; /* Old umask */
+
+/*
+ * Cat "string" onto the target buffer with error checking.
+ */
+static int cattarget(string)
+ char *string;
+{
+ if (strlen(string) + strlen(target) + 2 > sizeof(target)) {
+ message(MT_INFO, "target buffer is not large enough.");
+ return(-1);
+ }
+ if (!ptarget) {
+ message(MT_INFO, "NULL target pointer set.");
+ return(-10);
+ }
+
+ (void) sprintf(ptarget, "/%s", string);
+
+ return(0);
+}
+
+/*
+ * Set uid and gid ownership of a file.
+ */
+static int setownership(file, fd, uid, gid)
+ char *file;
+ int fd;
+ UID_T uid;
+ GID_T gid;
+{
+ int status = -1;
+
+ /*
+ * We assume only the Superuser can change uid ownership.
+ */
+ if (getuid() == 0) {
+#if defined(HAVE_FCHOWN)
+ if (fd != -1)
+ status = fchown(fd, (CHOWN_UID_T) uid,
+ (CHOWN_GID_T) gid);
+#endif
+ if (status < 0)
+ status = chown(file, (CHOWN_UID_T) uid,
+ (CHOWN_GID_T) gid);
+
+ if (status < 0) {
+ message(MT_NOTICE, "%s: chown %d.%d failed: %s",
+ target, (UID_T) uid, (GID_T) gid, SYSERR);
+ return(-1);
+ }
+ } else {
+#if defined(HAVE_FCHOWN)
+ if (fd != -1)
+ status = fchown(fd, (CHOWN_UID_T) -1,
+ (CHOWN_GID_T) gid);
+#endif
+ if (status < 0)
+ status = chown(file, (CHOWN_UID_T) -1,
+ (CHOWN_GID_T) gid);
+
+ if (status < 0) {
+ message(MT_NOTICE, "%s: chgrp %d failed: %s",
+ target, (GID_T) gid, SYSERR);
+ return(-1);
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Set mode of a file
+ */
+static int setfilemode(file, fd, mode)
+ char *file;
+ int fd;
+ int mode;
+{
+ int status = -1;
+
+ if (mode == -1)
+ return(0);
+
+#if defined(HAVE_FCHMOD)
+ if (fd != -1)
+ status = fchmod(fd, mode);
+#endif
+
+ if (status < 0)
+ status = chmod(file, mode);
+
+ if (status < 0) {
+ message(MT_NOTICE, "%s: chmod failed: %s", target, SYSERR);
+ return(-1);
+ }
+
+ return(0);
+}
+
+/*
+ * Get group entry. This routine takes a string argument (name).
+ * If name is of form ":N" a lookup for gid N is done.
+ * Otherwise a lookup by name is done.
+ */
+static struct group *mygetgroup(name)
+ char *name;
+{
+ struct group *gr;
+
+ if (*name == ':')
+ gr = getgrgid(atoi(name + 1));
+ else
+ gr = getgrnam(name);
+
+ return(gr);
+}
+
+/*
+ * Change owner, group and mode of file.
+ */
+static int fchog(fd, file, owner, group, mode)
+ int fd;
+ char *file, *owner, *group;
+ int mode;
+{
+ struct group *gr = NULL;
+ static char last_group[128];
+ static char last_owner[128];
+ static GID_T last_gid = (GID_T)-2;
+ extern char *locuser;
+ register int i;
+ UID_T uid;
+ GID_T gid;
+ GID_T primegid = (GID_T)-2;
+
+ uid = userid;
+ if (userid == 0) { /* running as root; take anything */
+ if (*owner == ':') {
+ uid = (UID_T) atoi(owner + 1);
+ } else if (pw == NULL || strcmp(owner, last_owner) != 0) {
+ if ((pw = getpwnam(owner)) == NULL) {
+ if (mode != -1 && IS_ON(mode, S_ISUID)) {
+ message(MT_NOTICE,
+ "%s: unknown login name \"%s\", clearing setuid",
+ target, owner);
+ mode &= ~S_ISUID;
+ uid = 0;
+ } else
+ message(MT_NOTICE,
+ "%s: unknown login name \"%s\"",
+ target, owner);
+ } else {
+ uid = pw->pw_uid;
+ strcpy(last_owner, owner);
+ }
+ } else {
+ uid = pw->pw_uid;
+ primegid = pw->pw_gid;
+ }
+ if (*group == ':') {
+ gid = (GID_T) atoi(group + 1);
+ goto ok;
+ }
+ } else { /* not root, setuid only if user==owner */
+ struct passwd *lupw;
+
+ if (mode != -1) {
+ if (IS_ON(mode, S_ISUID) &&
+ strcmp(locuser, owner) != 0)
+ mode &= ~S_ISUID;
+ if (mode)
+ mode &= ~S_ISVTX; /* and strip sticky too */
+ }
+
+ if ((lupw = getpwnam(locuser)) != NULL)
+ primegid = lupw->pw_gid;
+ }
+
+ gid = (GID_T) -1;
+ if (last_gid < (GID_T)0 || strcmp(group, last_group) != 0) {
+ /*
+ * Invalid cached values so we need to do a new lookup.
+ */
+ if (gr = mygetgroup(group)) {
+ last_gid = gid = gr->gr_gid;
+ strcpy(last_group, gr->gr_name);
+ } else {
+ if (mode != -1 && IS_ON(mode, S_ISGID)) {
+ message(MT_NOTICE,
+ "%s: unknown group \"%s\", clearing setgid",
+ target, group);
+ mode &= ~S_ISGID;
+ } else
+ message(MT_NOTICE,
+ "%s: unknown group \"%s\"",
+ target, group);
+ }
+ } else {
+ /*
+ * Use the cached values.
+ */
+ gid = last_gid;
+ }
+
+ /*
+ * We need to check non-root users to make sure they're a member
+ * of the group. If they are not, we don't set that gid ownership.
+ */
+ if (userid && gid >= 0 && gid != primegid) {
+ if (!gr)
+ gr = mygetgroup(group);
+ if (gr)
+ for (i = 0; gr->gr_mem[i] != NULL; i++)
+ if (strcmp(locuser, gr->gr_mem[i]) == 0)
+ goto ok;
+ if (mode != -1 && IS_ON(mode, S_ISGID)) {
+ message(MT_NOTICE,
+ "%s: user %s not in group %s, clearing setgid",
+ target, locuser, group);
+ mode &= ~S_ISGID;
+ }
+ gid = (GID_T) -1;
+ }
+ok:
+ /*
+ * Set uid and gid ownership. If that fails, strip setuid and
+ * setgid bits from mode. Once ownership is set, successful
+ * or otherwise, set the new file mode.
+ */
+ if (setownership(file, fd, uid, gid) < 0) {
+ if (mode != -1 && IS_ON(mode, S_ISUID)) {
+ message(MT_NOTICE,
+ "%s: chown failed, clearing setuid", target);
+ mode &= ~S_ISUID;
+ }
+ if (mode != -1 && IS_ON(mode, S_ISGID)) {
+ message(MT_NOTICE,
+ "%s: chown failed, clearing setgid", target);
+ mode &= ~S_ISGID;
+ }
+ }
+ (void) setfilemode(file, fd, mode);
+
+
+ return(0);
+}
+
+/*
+ * Remove a file or directory (recursively) and send back an acknowledge
+ * or an error message.
+ */
+static int removefile(statb)
+ struct stat *statb;
+{
+ DIR *d;
+ static DIRENTRY *dp;
+ register char *cp;
+ struct stat stb;
+ char *optarget;
+ int len, failures = 0;
+
+ switch (statb->st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFLNK:
+ if (unlink(target) < 0) {
+ if (errno == ETXTBSY) {
+ message(MT_REMOTE|MT_NOTICE,
+ "%s: unlink failed: %s",
+ target, SYSERR);
+ return(0);
+ } else {
+ error("%s: unlink failed: %s", target, SYSERR);
+ return(-1);
+ }
+ }
+ goto removed;
+
+ case S_IFDIR:
+ break;
+
+ default:
+ error("%s: not a plain file", target);
+ return(-1);
+ }
+
+ errno = 0;
+ if ((d = opendir(target)) == NULL) {
+ error("%s: opendir failed: %s", target, SYSERR);
+ return(-1);
+ }
+
+ optarget = ptarget;
+ len = ptarget - target;
+ while (dp = readdir(d)) {
+ if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
+ (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
+ dp->d_name[1] == '.'))
+ continue;
+
+ if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
+ message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long",
+ target, dp->d_name);
+ continue;
+ }
+ ptarget = optarget;
+ *ptarget++ = '/';
+ cp = dp->d_name;;
+ while (*ptarget++ = *cp++)
+ ;
+ ptarget--;
+ if (lstat(target, &stb) < 0) {
+ message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s",
+ target, SYSERR);
+ continue;
+ }
+ if (removefile(&stb) < 0)
+ ++failures;
+ }
+ (void) closedir(d);
+ ptarget = optarget;
+ *ptarget = CNULL;
+
+ if (failures)
+ return(-1);
+
+ if (rmdir(target) < 0) {
+ error("%s: rmdir failed: %s", target, SYSERR);
+ return(-1);
+ }
+removed:
+ message(MT_CHANGE|MT_REMOTE, "%s: removed", target);
+ return(0);
+}
+
+/*
+ * Check the current directory (initialized by the 'T' command to server())
+ * for extraneous files and remove them.
+ */
+static void doclean(cp)
+ register char *cp;
+{
+ DIR *d;
+ register DIRENTRY *dp;
+ struct stat stb;
+ char *optarget, *ep;
+ int len;
+ opt_t opts;
+
+ opts = strtol(cp, &ep, 8);
+ if (*ep != CNULL) {
+ error("clean: options not delimited");
+ return;
+ }
+ if ((d = opendir(target)) == NULL) {
+ error("%s: opendir failed: %s", target, SYSERR);
+ return;
+ }
+ ack();
+
+ optarget = ptarget;
+ len = ptarget - target;
+ while (dp = readdir(d)) {
+ if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
+ (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
+ dp->d_name[1] == '.'))
+ continue;
+
+ if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
+ message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long",
+ target, dp->d_name);
+ continue;
+ }
+ ptarget = optarget;
+ *ptarget++ = '/';
+ cp = dp->d_name;;
+ while (*ptarget++ = *cp++)
+ ;
+ ptarget--;
+ if (lstat(target, &stb) < 0) {
+ message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s",
+ target, SYSERR);
+ continue;
+ }
+
+ (void) sendcmd(CC_QUERY, "%s", dp->d_name);
+ (void) remline(cp = buf, sizeof(buf), TRUE);
+
+ if (*cp != CC_YES)
+ continue;
+
+ if (IS_ON(opts, DO_VERIFY))
+ message(MT_REMOTE|MT_INFO, "%s: need to remove",
+ target);
+ else
+ (void) removefile(&stb);
+ }
+ (void) closedir(d);
+
+ ptarget = optarget;
+ *ptarget = CNULL;
+}
+
+/*
+ * Frontend to doclean().
+ */
+static void clean(cp)
+ register char *cp;
+{
+ doclean(cp);
+ (void) sendcmd(CC_END, NULL);
+ (void) response();
+}
+
+/*
+ * Execute a shell command to handle special cases.
+ * We can't really set an alarm timeout here since we
+ * have no idea how long the command should take.
+ */
+static void dospecial(cmd)
+ char *cmd;
+{
+ runcommand(cmd);
+}
+
+/*
+ * Do a special cmd command. This differs from normal special
+ * commands in that it's done after an entire command has been updated.
+ * The list of updated target files is sent one at a time with RC_FILE
+ * commands. Each one is added to an environment variable defined by
+ * E_FILES. When an RC_COMMAND is finally received, the E_FILES variable
+ * is stuffed into our environment and a normal dospecial() command is run.
+ */
+static void docmdspecial()
+{
+ register char *cp;
+ char *cmd, *env = NULL;
+ int n;
+ int len;
+
+ /* We're ready */
+ ack();
+
+ for ( ; ; ) {
+ n = remline(cp = buf, sizeof(buf), FALSE);
+ if (n <= 0) {
+ error("cmdspecial: premature end of input.");
+ return;
+ }
+
+ switch (*cp++) {
+ case RC_FILE:
+ if (env == NULL) {
+ len = (2 * sizeof(E_FILES)) + strlen(cp) + 10;
+ env = (char *) xmalloc(len);
+ (void) sprintf(env, "export %s;%s=%s",
+ E_FILES, E_FILES, cp);
+ } else {
+ len = strlen(env);
+ env = (char *) xrealloc(env,
+ len + strlen(cp) + 2);
+ env[len] = CNULL;
+ (void) strcat(env, ":");
+ (void) strcat(env, cp);
+ }
+ ack();
+ break;
+
+ case RC_COMMAND:
+ if (env) {
+ len = strlen(env);
+ env = (char *) xrealloc(env,
+ len + strlen(cp) + 2);
+ env[len] = CNULL;
+ (void) strcat(env, ";");
+ (void) strcat(env, cp);
+ cmd = env;
+ } else
+ cmd = cp;
+
+ dospecial(cmd);
+ if (env)
+ (void) free(env);
+ return;
+
+ default:
+ error("Unknown cmdspecial command '%s'.", cp);
+ return;
+ }
+ }
+}
+
+/*
+ * Query. Check to see if file exists. Return one of the following:
+ *
+#ifdef NFS_CHECK
+ * QC_ONNFS - resides on a NFS
+#endif NFS_CHECK
+#ifdef RO_CHECK
+ * QC_ONRO - resides on a Read-Only filesystem
+#endif RO_CHECK
+ * QC_NO - doesn't exist
+ * QC_YESsize mtime - exists and its a regular file (size & mtime of file)
+ * QC_YES - exists and its a directory or symbolic link
+ * QC_ERRMSGmessage - error message
+ */
+static void query(name)
+ char *name;
+{
+ static struct stat stb;
+ int s = -1, stbvalid = 0;
+
+ if (catname && cattarget(name) < 0)
+ return;
+
+#if defined(NFS_CHECK)
+ if (IS_ON(options, DO_CHKNFS)) {
+ s = is_nfs_mounted(target, &stb, &stbvalid);
+ if (s > 0)
+ (void) sendcmd(QC_ONNFS, NULL);
+
+ /* Either the above check was true or an error occured */
+ /* and is_nfs_mounted sent the error message */
+ if (s != 0) {
+ *ptarget = CNULL;
+ return;
+ }
+ }
+#endif /* NFS_CHECK */
+
+#if defined(RO_CHECK)
+ if (IS_ON(options, DO_CHKREADONLY)) {
+ s = is_ro_mounted(target, &stb, &stbvalid);
+ if (s > 0)
+ (void) sendcmd(QC_ONRO, NULL);
+
+ /* Either the above check was true or an error occured */
+ /* and is_ro_mounted sent the error message */
+ if (s != 0) {
+ *ptarget = CNULL;
+ return;
+ }
+ }
+#endif /* RO_CHECK */
+
+ if (IS_ON(options, DO_CHKSYM)) {
+ if (is_symlinked(target, &stb, &stbvalid) > 0) {
+ (void) sendcmd(QC_SYM, NULL);
+ return;
+ }
+ }
+
+ /*
+ * If stbvalid is false, "stb" is not valid because:
+ * a) RO_CHECK and NFS_CHECK were not defined
+ * b) The stat by is_*_mounted() either failed or
+ * does not match "target".
+ */
+ if (!stbvalid && lstat(target, &stb) < 0) {
+ if (errno == ENOENT)
+ (void) sendcmd(QC_NO, NULL);
+ else
+ error("%s: lstat failed: %s", target, SYSERR);
+ *ptarget = CNULL;
+ return;
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFLNK:
+ case S_IFDIR:
+ case S_IFREG:
+ (void) sendcmd(QC_YES, "%ld %ld %o %s %s",
+ (long) stb.st_size,
+ stb.st_mtime,
+ stb.st_mode & 07777,
+ getusername(stb.st_uid, target, options),
+ getgroupname(stb.st_gid, target, options));
+ break;
+
+ default:
+ error("%s: not a file or directory", target);
+ break;
+ }
+ *ptarget = CNULL;
+}
+
+/*
+ * Check to see if parent directory exists and create one if not.
+ */
+static int chkparent(name, opts)
+ char *name;
+ opt_t opts;
+{
+ register char *cp;
+ struct stat stb;
+ int r = -1;
+
+ debugmsg(DM_CALL, "chkparent(%s, %o) start\n", name, opts);
+
+ cp = strrchr(name, '/');
+ if (cp == NULL || cp == name)
+ return(0);
+
+ *cp = CNULL;
+
+ if (lstat(name, &stb) < 0) {
+ if (errno == ENOENT && chkparent(name, opts) >= 0) {
+ if (mkdir(name, 0777 & ~oumask) == 0) {
+ message(MT_NOTICE, "%s: mkdir", name);
+ r = 0;
+ } else
+ debugmsg(DM_MISC,
+ "chkparent(%s, %o) mkdir fail: %s\n",
+ name, opts, SYSERR);
+ }
+ } else /* It exists */
+ r = 0;
+
+ /* Put back what we took away */
+ *cp = '/';
+
+ return(r);
+}
+
+/*
+ * Save a copy of 'file' by renaming it.
+ */
+static char *savetarget(file)
+ char *file;
+{
+ static char savefile[MAXPATHLEN];
+
+ if (strlen(file) + sizeof(SAVE_SUFFIX) + 1 > MAXPATHLEN) {
+ error("%s: Cannot save: Save name too long", file);
+ return((char *) NULL);
+ }
+
+ (void) sprintf(savefile, "%s%s", file, SAVE_SUFFIX);
+
+ if (unlink(savefile) != 0 && errno != ENOENT) {
+ message(MT_NOTICE, "%s: remove failed: %s", savefile, SYSERR);
+ return((char *) NULL);
+ }
+
+ if (rename(file, savefile) != 0 && errno != ENOENT) {
+ error("%s -> %s: rename failed: %s",
+ file, savefile, SYSERR);
+ return((char *) NULL);
+ }
+
+ return(savefile);
+}
+
+/*
+ * Receive a file
+ */
+static void recvfile(new, opts, mode, owner, group, mtime, atime, size)
+ /*ARGSUSED*/
+ char *new;
+ opt_t opts;
+ int mode;
+ char *owner, *group;
+ time_t mtime;
+ time_t atime;
+ off_t size;
+{
+ int f, wrerr, olderrno;
+ off_t i;
+ register char *cp;
+ char *savefile = NULL;
+
+ /*
+ * Create temporary file
+ */
+ if ((f = creat(new, mode)) < 0) {
+ if (errno != ENOENT || chkparent(new, opts) < 0 ||
+ (f = creat(new, mode)) < 0) {
+ error("%s: create failed: %s", new, SYSERR);
+ (void) unlink(new);
+ return;
+ }
+ }
+
+ /*
+ * Receive the file itself
+ */
+ ack();
+ wrerr = 0;
+ olderrno = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ int amt = BUFSIZ;
+
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ int j;
+
+ j = readrem(cp, amt);
+ if (j <= 0) {
+ (void) close(f);
+ (void) unlink(new);
+ fatalerr(
+ "Read error occured while receiving file.");
+ finish();
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (wrerr == 0 && xwrite(f, buf, amt) != amt) {
+ olderrno = errno;
+ wrerr++;
+ }
+ }
+
+ if (response() < 0) {
+ (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+ if (wrerr) {
+ error("%s: Write error: %s", new, strerror(olderrno));
+ (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+
+ /*
+ * Do file comparison if enabled
+ */
+ if (IS_ON(opts, DO_COMPARE)) {
+ FILE *f1, *f2;
+ int c;
+
+ errno = 0; /* fopen is not a syscall */
+ if ((f1 = fopen(target, "r")) == NULL) {
+ error("%s: open for read failed: %s", target, SYSERR);
+ (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+ errno = 0;
+ if ((f2 = fopen(new, "r")) == NULL) {
+ error("%s: open for read failed: %s", new, SYSERR);
+ (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+ while ((c = getc(f1)) == getc(f2))
+ if (c == EOF) {
+ debugmsg(DM_MISC,
+ "Files are the same '%s' '%s'.",
+ target, new);
+ (void) fclose(f1);
+ (void) fclose(f2);
+ (void) close(f);
+ (void) unlink(new);
+ /*
+ * This isn't an error per-se, but we
+ * need to indicate to the master that
+ * the file was not updated.
+ */
+ error("");
+ return;
+ }
+ debugmsg(DM_MISC, "Files are different '%s' '%s'.",
+ target, new);
+ (void) fclose(f1);
+ (void) fclose(f2);
+ if (IS_ON(opts, DO_VERIFY)) {
+ message(MT_REMOTE|MT_INFO, "%s: need to update",
+ target);
+ (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+ }
+
+ /*
+ * Set owner, group, and file mode
+ */
+ if (fchog(f, new, owner, group, mode) < 0) {
+ (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+ (void) close(f);
+
+ /*
+ * Perform utimes() after file is closed to make
+ * certain OS's, such as NeXT 2.1, happy.
+ */
+ if (setfiletime(new, time((time_t *) 0), mtime) < 0)
+ message(MT_NOTICE, "%s: utimes failed: %s", new, SYSERR);
+
+ /*
+ * Try to save target file from being over-written
+ */
+ if (IS_ON(opts, DO_SAVETARGETS))
+ if ((savefile = savetarget(target)) == NULL) {
+ (void) unlink(new);
+ return;
+ }
+
+ /*
+ * Install new (temporary) file as the actual target
+ */
+ if (rename(new, target) < 0) {
+ /*
+ * If the rename failed due to "Text file busy", then
+ * try to rename the target file and retry the rename.
+ */
+ if (errno == ETXTBSY) {
+ /* Save the target */
+ if ((savefile = savetarget(target)) != NULL) {
+ /* Retry installing new file as target */
+ if (rename(new, target) < 0) {
+ error("%s -> %s: rename failed: %s",
+ new, target, SYSERR);
+ /* Try to put back save file */
+ if (rename(savefile, target) < 0)
+ error(
+ "%s -> %s: rename failed: %s",
+ savefile, target,
+ SYSERR);
+ } else
+ message(MT_NOTICE, "%s: renamed to %s",
+ target, savefile);
+ }
+ } else {
+ error("%s -> %s: rename failed: %s",
+ new, target, SYSERR);
+ (void) unlink(new);
+ }
+ }
+
+ if (IS_ON(opts, DO_COMPARE))
+ message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
+ else
+ ack();
+}
+
+/*
+ * Receive a directory
+ */
+static void recvdir(opts, mode, owner, group)
+ opt_t opts;
+ int mode;
+ char *owner, *group;
+{
+ static char lowner[100], lgroup[100];
+ register char *cp;
+ struct stat stb;
+ int s;
+
+ s = lstat(target, &stb);
+ if (s == 0) {
+ /*
+ * If target is not a directory, remove it
+ */
+ if (!S_ISDIR(stb.st_mode)) {
+ if (IS_ON(opts, DO_VERIFY))
+ message(MT_NOTICE, "%s: need to remove",
+ target);
+ else {
+ if (unlink(target) < 0) {
+ error("%s: remove failed: %s",
+ target, SYSERR);
+ return;
+ }
+ }
+ s = -1;
+ errno = ENOENT;
+ } else {
+ if (!IS_ON(opts, DO_NOCHKMODE) &&
+ (stb.st_mode & 07777) != mode) {
+ if (IS_ON(opts, DO_VERIFY))
+ message(MT_NOTICE,
+ "%s: need to chmod to %o",
+ target, mode);
+ else {
+ if (chmod(target, mode) != 0)
+ message(MT_NOTICE,
+ "%s: chmod from %o to %o failed: %s",
+ target,
+ stb.st_mode & 07777,
+ mode,
+ SYSERR);
+ else
+ message(MT_NOTICE,
+ "%s: chmod from %o to %o",
+ target,
+ stb.st_mode & 07777,
+ mode);
+ }
+ }
+
+ /*
+ * Check ownership and set if necessary
+ */
+ lowner[0] = CNULL;
+ lgroup[0] = CNULL;
+
+ if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
+ int o;
+
+ o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER :
+ opts;
+ if (cp = getusername(stb.st_uid, target, o))
+ if (strcmp(owner, cp))
+ (void) strcpy(lowner, cp);
+ }
+ if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
+ int o;
+
+ o = (group[0] == ':') ? opts & DO_NUMCHKGROUP :
+ opts;
+ if (cp = getgroupname(stb.st_gid, target, o))
+ if (strcmp(group, cp))
+ (void) strcpy(lgroup, cp);
+ }
+
+ /*
+ * Need to set owner and/or group
+ */
+#define PRN(n) ((n[0] == ':') ? n+1 : n)
+ if (lowner[0] != CNULL || lgroup[0] != CNULL) {
+ if (lowner[0] == CNULL &&
+ (cp = getusername(stb.st_uid,
+ target, opts)))
+ (void) strcpy(lowner, cp);
+ if (lgroup[0] == CNULL &&
+ (cp = getgroupname(stb.st_gid,
+ target, opts)))
+ (void) strcpy(lgroup, cp);
+
+ if (IS_ON(opts, DO_VERIFY))
+ message(MT_NOTICE,
+ "%s: need to chown from %s.%s to %s.%s",
+ target,
+ PRN(lowner), PRN(lgroup),
+ PRN(owner), PRN(group));
+ else {
+ if (fchog(-1, target, owner,
+ group, -1) == 0)
+ message(MT_NOTICE,
+ "%s: chown from %s.%s to %s.%s",
+ target,
+ PRN(lowner),
+ PRN(lgroup),
+ PRN(owner),
+ PRN(group));
+ }
+ }
+#undef PRN
+ ack();
+ return;
+ }
+ }
+
+ if (IS_ON(opts, DO_VERIFY)) {
+ ack();
+ return;
+ }
+
+ /*
+ * Create the directory
+ */
+ if (s < 0) {
+ if (errno == ENOENT) {
+ if (mkdir(target, mode) == 0 ||
+ chkparent(target, opts) == 0 &&
+ mkdir(target, mode) == 0) {
+ message(MT_NOTICE, "%s: mkdir", target);
+ (void) fchog(-1, target, owner, group, mode);
+ ack();
+ } else {
+ error("%s: mkdir failed: %s", target, SYSERR);
+ ptarget = sptarget[--catname];
+ *ptarget = CNULL;
+ }
+ return;
+ }
+ }
+ error("%s: lstat failed: %s", target, SYSERR);
+ ptarget = sptarget[--catname];
+ *ptarget = CNULL;
+}
+
+/*
+ * Receive a link
+ */
+static void recvlink(new, opts, mode, size)
+ char *new;
+ opt_t opts;
+ int mode;
+ off_t size;
+{
+ struct stat stb;
+ char *optarget;
+ off_t i;
+
+ /*
+ * Read basic link info
+ */
+ ack();
+ (void) remline(buf, sizeof(buf), TRUE);
+
+ if (response() < 0) {
+ err();
+ return;
+ }
+
+ /*
+ * Make new symlink using a temporary name
+ */
+ if (symlink(buf, new) < 0) {
+ if (errno != ENOENT || chkparent(new, opts) < 0 ||
+ symlink(buf, new) < 0) {
+ error("%s -> %s: symlink failed: %s", new, buf,SYSERR);
+ (void) unlink(new);
+ return;
+ }
+ }
+
+ /*
+ * Do comparison of what link is pointing to if enabled
+ */
+ mode &= 0777;
+ if (IS_ON(opts, DO_COMPARE)) {
+ char tbuf[MAXPATHLEN];
+
+ if ((i = readlink(target, tbuf, sizeof(tbuf))) >= 0 &&
+ i == size && strncmp(buf, tbuf, (int) size) == 0) {
+ (void) unlink(new);
+ ack();
+ return;
+ }
+ if (IS_ON(opts, DO_VERIFY)) {
+ (void) unlink(new);
+ message(MT_REMOTE|MT_INFO, "%s: need to update",
+ target);
+ (void) sendcmd(C_END, NULL);
+ (void) response();
+ return;
+ }
+ }
+
+ /*
+ * See if target is a directory and remove it if it is
+ */
+ if (lstat(target, &stb) == 0) {
+ if (S_ISDIR(stb.st_mode)) {
+ optarget = ptarget;
+ for (ptarget = target; *ptarget; ptarget++);
+ if (removefile(&stb) < 0) {
+ ptarget = optarget;
+ (void) unlink(new);
+ (void) sendcmd(C_END, NULL);
+ (void) response();
+ return;
+ }
+ ptarget = optarget;
+ }
+ }
+
+ /*
+ * Install link as the target
+ */
+ if (rename(new, target) < 0) {
+ error("%s -> %s: symlink rename failed: %s",
+ new, target, SYSERR);
+ (void) unlink(new);
+ (void) sendcmd(C_END, NULL);
+ (void) response();
+ return;
+ }
+
+ if (IS_ON(opts, DO_COMPARE))
+ message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
+ else
+ ack();
+
+ /*
+ * Indicate end of receive operation
+ */
+ (void) sendcmd(C_END, NULL);
+ (void) response();
+}
+
+/*
+ * Creat a hard link to existing file.
+ */
+static void hardlink(cmd)
+ char *cmd;
+{
+ struct stat stb;
+ int exists = 0;
+ char *oldname, *newname;
+ char *cp = cmd;
+ static char expbuf[BUFSIZ];
+
+ /* Skip over opts */
+ (void) strtol(cp, &cp, 8);
+ if (*cp++ != ' ') {
+ error("hardlink: options not delimited");
+ return;
+ }
+
+ oldname = strtok(cp, " ");
+ if (oldname == NULL) {
+ error("hardlink: oldname name not delimited");
+ return;
+ }
+
+ newname = strtok((char *)NULL, " ");
+ if (newname == NULL) {
+ error("hardlink: new name not specified");
+ return;
+ }
+
+ if (exptilde(expbuf, oldname) == NULL) {
+ error("hardlink: tilde expansion failed");
+ return;
+ }
+ oldname = expbuf;
+
+ if (catname && cattarget(newname) < 0) {
+ error("Cannot set newname target.");
+ return;
+ }
+
+ if (lstat(target, &stb) == 0) {
+ int mode = stb.st_mode & S_IFMT;
+
+ if (mode != S_IFREG && mode != S_IFLNK) {
+ error("%s: not a regular file", target);
+ return;
+ }
+ exists = 1;
+ }
+
+ if (chkparent(target, options) < 0 ) {
+ error("%s: no parent: %s ", target, SYSERR);
+ return;
+ }
+ if (exists && (unlink(target) < 0)) {
+ error("%s: unlink failed: %s", target, SYSERR);
+ return;
+ }
+ if (link(oldname, target) < 0) {
+ error("%s: cannot link to %s: %s", target, oldname, SYSERR);
+ return;
+ }
+ ack();
+}
+
+/*
+ * Set configuration information.
+ *
+ * A key letter is followed immediately by the value
+ * to set. The keys are:
+ * SC_FREESPACE - Set minimium free space of filesystem
+ * SC_FREEFILES - Set minimium free number of files of filesystem
+ */
+static void setconfig(cmd)
+ char *cmd;
+{
+ register char *cp = cmd;
+ char *estr;
+
+ switch (*cp++) {
+ case SC_HOSTNAME: /* Set hostname */
+ /*
+ * Only use info if we don't know who this is.
+ */
+ if (!fromhost) {
+ fromhost = strdup(cp);
+ message(MT_SYSLOG, "startup for %s", fromhost);
+#if defined(SETARGS)
+ setproctitle("serving %s", cp);
+#endif /* SETARGS */
+ }
+ break;
+
+ case SC_FREESPACE: /* Minimium free space */
+ if (!isdigit(*cp)) {
+ fatalerr("Expected digit, got '%s'.", cp);
+ return;
+ }
+ min_freespace = (unsigned long) atoi(cp);
+ break;
+
+ case SC_FREEFILES: /* Minimium free files */
+ if (!isdigit(*cp)) {
+ fatalerr("Expected digit, got '%s'.", cp);
+ return;
+ }
+ min_freefiles = (unsigned long) atoi(cp);
+ break;
+
+ case SC_LOGGING: /* Logging options */
+ if (estr = msgparseopts(cp, TRUE)) {
+ fatalerr("Bad message option string (%s): %s",
+ cp, estr);
+ return;
+ }
+ break;
+
+ default:
+ message(MT_NOTICE, "Unknown config command \"%s\".", cp-1);
+ return;
+ }
+}
+
+/*
+ * Receive something
+ */
+static void recvit(cmd, type)
+ char *cmd;
+ int type;
+{
+ int mode;
+ opt_t opts;
+ off_t size;
+ time_t mtime, atime;
+ char *owner, *group, *file;
+ char new[MAXPATHLEN];
+ int freespace = -1, freefiles = -1;
+ char *cp = cmd;
+
+ /*
+ * Get rdist option flags
+ */
+ opts = strtol(cp, &cp, 8);
+ if (*cp++ != ' ') {
+ error("recvit: options not delimited");
+ return;
+ }
+
+ /*
+ * Get file mode
+ */
+ mode = strtol(cp, &cp, 8);
+ if (*cp++ != ' ') {
+ error("recvit: mode not delimited");
+ return;
+ }
+
+ /*
+ * Get file size
+ */
+ size = strtol(cp, &cp, 10);
+ if (*cp++ != ' ') {
+ error("recvit: size not delimited");
+ return;
+ }
+
+ /*
+ * Get modification time
+ */
+ mtime = strtol(cp, &cp, 10);
+ if (*cp++ != ' ') {
+ error("recvit: mtime not delimited");
+ return;
+ }
+
+ /*
+ * Get access time
+ */
+ atime = strtol(cp, &cp, 10);
+ if (*cp++ != ' ') {
+ error("recvit: atime not delimited");
+ return;
+ }
+
+ /*
+ * Get file owner name
+ */
+ owner = strtok(cp, " ");
+ if (owner == NULL) {
+ error("recvit: owner name not delimited");
+ return;
+ }
+
+ /*
+ * Get file group name
+ */
+ group = strtok((char *)NULL, " ");
+ if (group == NULL) {
+ error("recvit: group name not delimited");
+ return;
+ }
+
+ /*
+ * Get file name. Can't use strtok() since there could
+ * be white space in the file name.
+ */
+ file = group + strlen(group) + 1;
+ if (file == NULL) {
+ error("recvit: no file name");
+ return;
+ }
+
+ debugmsg(DM_MISC,
+ "recvit: opts = %04o mode = %04o size = %d mtime = %d",
+ opts, mode, size, mtime);
+ debugmsg(DM_MISC,
+ "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
+ owner, group, file, catname, (type == S_IFDIR) ? 1 : 0);
+
+ if (type == S_IFDIR) {
+ if (catname >= sizeof(sptarget)) {
+ error("%s: too many directory levels", target);
+ return;
+ }
+ sptarget[catname] = ptarget;
+ if (catname++) {
+ *ptarget++ = '/';
+ while (*ptarget++ = *file++)
+ ;
+ ptarget--;
+ }
+ } else {
+ /*
+ * Create name of temporary file
+ */
+ if (catname && cattarget(file) < 0) {
+ error("Cannot set file name.");
+ return;
+ }
+ file = strrchr(target, '/');
+ if (file == NULL)
+ (void) strcpy(new, tempname);
+ else if (file == target)
+ (void) sprintf(new, "/%s", tempname);
+ else {
+ *file = CNULL;
+ (void) sprintf(new, "%s/%s", target, tempname);
+ *file = '/';
+ }
+ (void) mktemp(new);
+ }
+
+ /*
+ * Check to see if there is enough free space and inodes
+ * to install this file.
+ */
+ if (min_freespace || min_freefiles) {
+ /* Convert file size to kilobytes */
+ int fsize = size / 1024;
+
+ if (getfilesysinfo(target, &freespace, &freefiles) != 0)
+ return;
+
+ /*
+ * filesystem values < 0 indicate unsupported or unavailable
+ * information.
+ */
+ if (min_freespace && (freespace >= 0) &&
+ (freespace - fsize < min_freespace)) {
+ error(
+ "%s: Not enough free space on filesystem: min %d free %d",
+ target, min_freespace, freespace);
+ return;
+ }
+ if (min_freefiles && (freefiles >= 0) &&
+ (freefiles - 1 < min_freefiles)) {
+ error(
+ "%s: Not enough free files on filesystem: min %d free %d",
+ target, min_freefiles, freefiles);
+ return;
+ }
+ }
+
+ /*
+ * Call appropriate receive function to receive file
+ */
+ switch (type) {
+ case S_IFDIR:
+ recvdir(opts, mode, owner, group);
+ break;
+
+ case S_IFLNK:
+ recvlink(new, opts, mode, size);
+ break;
+
+ case S_IFREG:
+ recvfile(new, opts, mode, owner, group, mtime, atime, size);
+ break;
+
+ default:
+ error("%d: unknown file type", type);
+ break;
+ }
+}
+
+/*
+ * Set target information
+ */
+static void settarget(cmd, isdir)
+ char *cmd;
+ int isdir;
+{
+ char *cp = cmd;
+ opt_t opts;
+
+ catname = isdir;
+
+ /*
+ * Parse options for this target
+ */
+ opts = strtol(cp, &cp, 8);
+ if (*cp++ != ' ') {
+ error("settarget: options not delimited");
+ return;
+ }
+ options = opts;
+
+ /*
+ * Handle target
+ */
+ if (exptilde(target, cp) == NULL)
+ return;
+ ptarget = target;
+ while (*ptarget)
+ ptarget++;
+
+ ack();
+}
+
+/*
+ * Cleanup in preparation for exiting.
+ */
+extern void cleanup()
+{
+ /* We don't need to do anything */
+}
+
+/*
+ * Server routine to read requests and process them.
+ */
+extern void server()
+{
+ static char cmdbuf[BUFSIZ];
+ register char *cp;
+ register int n;
+ extern jmp_buf finish_jmpbuf;
+
+ if (setjmp(finish_jmpbuf))
+ return;
+ (void) signal(SIGHUP, sighandler);
+ (void) signal(SIGINT, sighandler);
+ (void) signal(SIGQUIT, sighandler);
+ (void) signal(SIGTERM, sighandler);
+ (void) signal(SIGPIPE, sighandler);
+ (void) umask(oumask = umask(0));
+ (void) strcpy(tempname, _RDIST_TMP);
+ if (fromhost) {
+ message(MT_SYSLOG, "Startup for %s", fromhost);
+#if defined(SETARGS)
+ setproctitle("Serving %s", fromhost);
+#endif /* SETARGS */
+ }
+
+ /*
+ * Let client know we want it to send it's version number
+ */
+ (void) sendcmd(S_VERSION, NULL);
+
+ if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) {
+ error("server: expected control record");
+ return;
+ }
+
+ if (cmdbuf[0] != S_VERSION || !isdigit(cmdbuf[1])) {
+ error("Expected version command, received: \"%s\".", cmdbuf);
+ return;
+ }
+
+ proto_version = atoi(&cmdbuf[1]);
+ if (proto_version != VERSION) {
+ error("Protocol version %d is not supported.", proto_version);
+ return;
+ }
+
+ /* Version number is okay */
+ ack();
+
+ /*
+ * Main command loop
+ */
+ for ( ; ; ) {
+ n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE);
+ if (n == -1) /* EOF */
+ return;
+ if (n == 0) {
+ error("server: expected control record");
+ continue;
+ }
+
+ switch (*cp++) {
+ case C_SETCONFIG: /* Configuration info */
+ setconfig(cp);
+ ack();
+ continue;
+
+ case C_DIRTARGET: /* init target file/directory name */
+ settarget(cp, TRUE);
+ continue;
+
+ case C_TARGET: /* init target file/directory name */
+ settarget(cp, FALSE);
+ continue;
+
+ case C_RECVREG: /* Transfer a regular file. */
+ recvit(cp, S_IFREG);
+ continue;
+
+ case C_RECVDIR: /* Transfer a directory. */
+ recvit(cp, S_IFDIR);
+ continue;
+
+ case C_RECVSYMLINK: /* Transfer symbolic link. */
+ recvit(cp, S_IFLNK);
+ continue;
+
+ case C_RECVHARDLINK: /* Transfer hard link. */
+ hardlink(cp);
+ continue;
+
+ case C_END: /* End of transfer */
+ *ptarget = CNULL;
+ if (catname <= 0) {
+ error("server: too many '%c's", C_END);
+ continue;
+ }
+ ptarget = sptarget[--catname];
+ *ptarget = CNULL;
+ ack();
+ continue;
+
+ case C_CLEAN: /* Clean. Cleanup a directory */
+ clean(cp);
+ continue;
+
+ case C_QUERY: /* Query file/directory */
+ query(cp);
+ continue;
+
+ case C_SPECIAL: /* Special. Execute commands */
+ dospecial(cp);
+ continue;
+
+ case C_CMDSPECIAL: /* Cmd Special. Execute commands */
+ docmdspecial();
+ continue;
+
+#ifdef DOCHMOD
+ case C_CHMOD: /* Set mode */
+ dochmod(cp);
+ continue;
+#endif /* DOCHMOD */
+
+ case C_ERRMSG: /* Normal error message */
+ if (cp && *cp)
+ message(MT_NERROR|MT_NOREMOTE, "%s", cp);
+ continue;
+
+ case C_FERRMSG: /* Fatal error message */
+ if (cp && *cp)
+ message(MT_FERROR|MT_NOREMOTE, "%s", cp);
+ return;
+
+ default:
+ error("server: unknown command '%s'", cp - 1);
+ case CNULL:
+ continue;
+ }
+ }
+}