rdist 6.1.1
authordm <dm@openbsd.org>
Sat, 3 Feb 1996 12:11:34 +0000 (12:11 +0000)
committerdm <dm@openbsd.org>
Sat, 3 Feb 1996 12:11:34 +0000 (12:11 +0000)
45 files changed:
usr.bin/Makefile
usr.bin/oldrdist/Makefile [new file with mode: 0644]
usr.bin/oldrdist/defs.h [new file with mode: 0644]
usr.bin/oldrdist/docmd.c [new file with mode: 0644]
usr.bin/oldrdist/expand.c [new file with mode: 0644]
usr.bin/oldrdist/gram.y [new file with mode: 0644]
usr.bin/oldrdist/lookup.c [new file with mode: 0644]
usr.bin/oldrdist/main.c [new file with mode: 0644]
usr.bin/oldrdist/oldrdist.1 [new file with mode: 0644]
usr.bin/oldrdist/pathnames.h [new file with mode: 0644]
usr.bin/oldrdist/server.c [new file with mode: 0644]
usr.bin/rdist/Makefile
usr.bin/rdist/child.c [new file with mode: 0644]
usr.bin/rdist/client.c [new file with mode: 0644]
usr.bin/rdist/common.c [new file with mode: 0644]
usr.bin/rdist/config-data.h [new file with mode: 0644]
usr.bin/rdist/config-def.h [new file with mode: 0644]
usr.bin/rdist/config.h [new file with mode: 0644]
usr.bin/rdist/defs.h
usr.bin/rdist/distopt.c [new file with mode: 0644]
usr.bin/rdist/docmd.c
usr.bin/rdist/expand.c
usr.bin/rdist/gram.y
usr.bin/rdist/isexec.c [new file with mode: 0644]
usr.bin/rdist/lookup.c
usr.bin/rdist/main.c [deleted file]
usr.bin/rdist/message.c [new file with mode: 0644]
usr.bin/rdist/os-openbsd.h [new file with mode: 0644]
usr.bin/rdist/pathnames.h
usr.bin/rdist/rdist.1
usr.bin/rdist/rdist.c [new file with mode: 0644]
usr.bin/rdist/rshrcmd.c [new file with mode: 0644]
usr.bin/rdist/server.c [deleted file]
usr.bin/rdist/setargs.c [new file with mode: 0644]
usr.bin/rdist/signal.c [new file with mode: 0644]
usr.bin/rdist/types.h [new file with mode: 0644]
usr.bin/rdist/version.h [new file with mode: 0644]
usr.bin/rdistd/Makefile [new file with mode: 0644]
usr.bin/rdistd/filesys-os.c [new file with mode: 0644]
usr.bin/rdistd/filesys.c [new file with mode: 0644]
usr.bin/rdistd/filesys.h [new file with mode: 0644]
usr.bin/rdistd/message.c [new file with mode: 0644]
usr.bin/rdistd/rdistd.1 [new file with mode: 0644]
usr.bin/rdistd/rdistd.c [new file with mode: 0644]
usr.bin/rdistd/server.c [new file with mode: 0644]

index 035fdcd..18169cf 100644 (file)
@@ -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 (file)
index 0000000..2d11b21
--- /dev/null
@@ -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 (file)
index 0000000..ebfab36
--- /dev/null
@@ -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 (file)
index 0000000..5fc5344
--- /dev/null
@@ -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 (file)
index 0000000..06e021b
--- /dev/null
@@ -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 (file)
index 0000000..8639687
--- /dev/null
@@ -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 (file)
index 0000000..11e6c6a
--- /dev/null
@@ -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/oldrdist/main.c b/usr.bin/oldrdist/main.c
new file mode 100644 (file)
index 0000000..fd487b0
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/* from: static char sccsid[] = "@(#)main.c    8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: main.c,v 1.1 1996/02/03 12:12:00 dm Exp $";
+#endif /* not lint */
+
+#include "defs.h"
+
+#define NHOSTS 100
+
+/*
+ * Remote distribution program.
+ */
+
+char   *distfile = NULL;
+#define _RDIST_TMP     "/rdistXXXXXX"
+char   tempfile[sizeof _PATH_TMP + sizeof _RDIST_TMP + 1];
+char   *tempname;
+
+int    debug;          /* debugging flag */
+int    nflag;          /* NOP flag, just print commands without executing */
+int    qflag;          /* Quiet. Don't print messages */
+int    options;        /* global options */
+int    iamremote;      /* act as remote server for transfering files */
+
+FILE   *fin = NULL;    /* input file pointer */
+int    rem = -1;       /* file descriptor to remote source/sink process */
+char   host[32];       /* host name */
+int    nerrs;          /* number of errors while sending/receiving */
+char   user[10];       /* user's name */
+char   homedir[128];   /* user's home directory */
+int    userid;         /* user's user ID */
+int    groupid;        /* user's group ID */
+
+struct passwd *pw;     /* pointer to static area used by getpwent */
+struct group *gr;      /* pointer to static area used by getgrent */
+
+static void usage __P((void));
+static void docmdargs __P((int, char *[]));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register char *arg;
+       int cmdargs = 0;
+       char *dhosts[NHOSTS], **hp = dhosts;
+
+       pw = getpwuid(userid = getuid());
+       if (pw == NULL) {
+               fprintf(stderr, "%s: Who are you?\n", argv[0]);
+               exit(1);
+       }
+       strcpy(user, pw->pw_name);
+       strcpy(homedir, pw->pw_dir);
+       groupid = pw->pw_gid;
+       gethostname(host, sizeof(host));
+       strcpy(tempfile, _PATH_TMP);
+       strcat(tempfile, _RDIST_TMP);
+       if ((tempname = rindex(tempfile, '/')) != 0)
+               tempname++;
+       else
+               tempname = tempfile;
+
+       while (--argc > 0) {
+               if ((arg = *++argv)[0] != '-')
+                       break;
+               if (!strcmp(arg, "-Server"))
+                       iamremote++;
+               else while (*++arg)
+                       switch (*arg) {
+                       case 'f':
+                               if (--argc <= 0)
+                                       usage();
+                               distfile = *++argv;
+                               if (distfile[0] == '-' && distfile[1] == '\0')
+                                       fin = stdin;
+                               break;
+
+                       case 'm':
+                               if (--argc <= 0)
+                                       usage();
+                               if (hp >= &dhosts[NHOSTS-2]) {
+                                       fprintf(stderr, "rdist: too many destination hosts\n");
+                                       exit(1);
+                               }
+                               *hp++ = *++argv;
+                               break;
+
+                       case 'd':
+                               if (--argc <= 0)
+                                       usage();
+                               define(*++argv);
+                               break;
+
+                       case 'D':
+                               debug++;
+                               break;
+
+                       case 'c':
+                               cmdargs++;
+                               break;
+
+                       case 'n':
+                               if (options & VERIFY) {
+                                       printf("rdist: -n overrides -v\n");
+                                       options &= ~VERIFY;
+                               }
+                               nflag++;
+                               break;
+
+                       case 'q':
+                               qflag++;
+                               break;
+
+                       case 'b':
+                               options |= COMPARE;
+                               break;
+
+                       case 'R':
+                               options |= REMOVE;
+                               break;
+
+                       case 'v':
+                               if (nflag) {
+                                       printf("rdist: -n overrides -v\n");
+                                       break;
+                               }
+                               options |= VERIFY;
+                               break;
+
+                       case 'w':
+                               options |= WHOLE;
+                               break;
+
+                       case 'y':
+                               options |= YOUNGER;
+                               break;
+
+                       case 'h':
+                               options |= FOLLOW;
+                               break;
+
+                       case 'i':
+                               options |= IGNLNKS;
+                               break;
+
+                       default:
+                               usage();
+                       }
+       }
+       *hp = NULL;
+
+       seteuid(userid);
+       mktemp(tempfile);
+
+       if (iamremote) {
+               server();
+               exit(nerrs != 0);
+       }
+
+       if (cmdargs)
+               docmdargs(argc, argv);
+       else {
+               if (fin == NULL) {
+                       if(distfile == NULL) {
+                               if((fin = fopen("distfile","r")) == NULL)
+                                       fin = fopen("Distfile", "r");
+                       } else
+                               fin = fopen(distfile, "r");
+                       if(fin == NULL) {
+                               perror(distfile ? distfile : "distfile");
+                               exit(1);
+                       }
+               }
+               yyparse();
+               if (nerrs == 0)
+                       docmds(dhosts, argc, argv);
+       }
+
+       exit(nerrs != 0);
+}
+
+static void
+usage()
+{
+       printf("Usage: rdist [-nqbhirvwyD] [-f distfile] [-d var=value] [-m host] [file ...]\n");
+       printf("or: rdist [-nqbhirvwyD] -c source [...] machine[:dest]\n");
+       exit(1);
+}
+
+/*
+ * rcp like interface for distributing files.
+ */
+static void
+docmdargs(nargs, args)
+       int nargs;
+       char *args[];
+{
+       register struct namelist *nl, *prev;
+       register char *cp;
+       struct namelist *files, *hosts;
+       struct subcmd *cmds;
+       char *dest;
+       static struct namelist tnl = { NULL, NULL };
+       int i;
+
+       if (nargs < 2)
+               usage();
+
+       prev = NULL;
+       for (i = 0; i < nargs - 1; i++) {
+               nl = makenl(args[i]);
+               if (prev == NULL)
+                       files = prev = nl;
+               else {
+                       prev->n_next = nl;
+                       prev = nl;
+               }
+       }
+
+       cp = args[i];
+       if ((dest = index(cp, ':')) != NULL)
+               *dest++ = '\0';
+       tnl.n_name = cp;
+       hosts = expand(&tnl, E_ALL);
+       if (nerrs)
+               exit(1);
+
+       if (dest == NULL || *dest == '\0')
+               cmds = NULL;
+       else {
+               cmds = makesubcmd(INSTALL);
+               cmds->sc_options = options;
+               cmds->sc_name = dest;
+       }
+
+       if (debug) {
+               printf("docmdargs()\nfiles = ");
+               prnames(files);
+               printf("hosts = ");
+               prnames(hosts);
+       }
+       insert(NULL, files, hosts, cmds);
+       docmds(NULL, 0, NULL);
+}
+
+/*
+ * Print a list of NAME blocks (mostly for debugging).
+ */
+void
+prnames(nl)
+       register struct namelist *nl;
+{
+       printf("( ");
+       while (nl != NULL) {
+               printf("%s ", nl->n_name);
+               nl = nl->n_next;
+       }
+       printf(")\n");
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+       char *fmt;
+        va_dcl
+#endif
+{
+       extern int yylineno;
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)fprintf(stderr, "rdist: line %d: Warning: ", yylineno);
+       (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
+       va_end(ap);
+}
diff --git a/usr.bin/oldrdist/oldrdist.1 b/usr.bin/oldrdist/oldrdist.1
new file mode 100644 (file)
index 0000000..35ae3f8
--- /dev/null
@@ -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 (file)
index 0000000..85872f2
--- /dev/null
@@ -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/oldrdist/server.c b/usr.bin/oldrdist/server.c
new file mode 100644 (file)
index 0000000..c3ef0e1
--- /dev/null
@@ -0,0 +1,1589 @@
+/*
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+/* from: static char sccsid[] = "@(#)server.c  8.1 (Berkeley) 6/9/93"; */
+static char *rcsid = "$Id: server.c,v 1.1 1996/02/03 12:12:03 dm Exp $";
+#endif /* not lint */
+
+#include <sys/wait.h>
+#include "defs.h"
+
+#define        ack()   (void) write(rem, "\0\n", 2)
+#define        err()   (void) write(rem, "\1\n", 2)
+
+struct linkbuf *ihead;         /* list of files with more than one link */
+char   buf[BUFSIZ];            /* general purpose buffer */
+char   target[BUFSIZ];         /* target/source directory name */
+char   *tp;                    /* pointer to end of target name */
+char   *Tdest;                 /* pointer to last T dest*/
+int    catname;                /* cat name to target name */
+char   *stp[32];               /* stack of saved tp's for directories */
+int    oumask;                 /* old umask for creating files */
+
+extern FILE *lfp;              /* log file for mailing changes */
+
+static int     chkparent __P((char *));
+static void    clean __P((char *));
+static void    comment __P((char *));
+static void    dospecial __P((char *));
+static int     fchog __P((int, char *, char *, char *, int));
+static void    hardlink __P((char *));
+static void    note __P((const char *, ...));
+static void    query __P((char *));
+static void    recvf __P((char *, int));
+static void    removeit __P((struct stat *));
+static int     response __P((void));
+static void    rmchk __P((int));
+static struct linkbuf *
+                   savelink __P((struct stat *));
+static void    sendf __P((char *, int));
+static int     update __P((char *, int, struct stat *));
+
+/*
+ * Server routine to read requests and process them.
+ * Commands are:
+ *     Tname   - Transmit file if out of date
+ *     Vname   - Verify if file out of date or not
+ *     Qname   - Query if file exists. Return mtime & size if it does.
+ */
+void
+server()
+{
+       char cmdbuf[BUFSIZ];
+       register char *cp;
+
+       signal(SIGHUP, cleanup);
+       signal(SIGINT, cleanup);
+       signal(SIGQUIT, cleanup);
+       signal(SIGTERM, cleanup);
+       signal(SIGPIPE, cleanup);
+
+       rem = 0;
+       oumask = umask(0);
+       (void) sprintf(buf, "V%d\n", VERSION);
+       (void) write(rem, buf, strlen(buf));
+
+       for (;;) {
+               cp = cmdbuf;
+               if (read(rem, cp, 1) <= 0)
+                       return;
+               if (*cp++ == '\n') {
+                       error("server: expected control record\n");
+                       continue;
+               }
+               do {
+                       if (read(rem, cp, 1) != 1)
+                               cleanup(0);
+               } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
+               *--cp = '\0';
+               cp = cmdbuf;
+               switch (*cp++) {
+               case 'T':  /* init target file/directory name */
+                       catname = 1;    /* target should be directory */
+                       goto dotarget;
+
+               case 't':  /* init target file/directory name */
+                       catname = 0;
+               dotarget:
+                       if (exptilde(target, cp) == NULL)
+                               continue;
+                       tp = target;
+                       while (*tp)
+                               tp++;
+                       ack();
+                       continue;
+
+               case 'R':  /* Transfer a regular file. */
+                       recvf(cp, S_IFREG);
+                       continue;
+
+               case 'D':  /* Transfer a directory. */
+                       recvf(cp, S_IFDIR);
+                       continue;
+
+               case 'K':  /* Transfer symbolic link. */
+                       recvf(cp, S_IFLNK);
+                       continue;
+
+               case 'k':  /* Transfer hard link. */
+                       hardlink(cp);
+                       continue;
+
+               case 'E':  /* End. (of directory) */
+                       *tp = '\0';
+                       if (catname <= 0) {
+                               error("server: too many 'E's\n");
+                               continue;
+                       }
+                       tp = stp[--catname];
+                       *tp = '\0';
+                       ack();
+                       continue;
+
+               case 'C':  /* Clean. Cleanup a directory */
+                       clean(cp);
+                       continue;
+
+               case 'Q':  /* Query. Does the file/directory exist? */
+                       query(cp);
+                       continue;
+
+               case 'S':  /* Special. Execute commands */
+                       dospecial(cp);
+                       continue;
+
+#ifdef notdef
+               /*
+                * These entries are reserved but not currently used.
+                * The intent is to allow remote hosts to have master copies.
+                * Currently, only the host rdist runs on can have masters.
+                */
+               case 'X':  /* start a new list of files to exclude */
+                       except = bp = NULL;
+               case 'x':  /* add name to list of files to exclude */
+                       if (*cp == '\0') {
+                               ack();
+                               continue;
+                       }
+                       if (*cp == '~') {
+                               if (exptilde(buf, cp) == NULL)
+                                       continue;
+                               cp = buf;
+                       }
+                       if (bp == NULL)
+                               except = bp = expand(makeblock(NAME, cp), E_VARS);
+                       else
+                               bp->b_next = expand(makeblock(NAME, cp), E_VARS);
+                       while (bp->b_next != NULL)
+                               bp = bp->b_next;
+                       ack();
+                       continue;
+
+               case 'I':  /* Install. Transfer file if out of date. */
+                       opts = 0;
+                       while (*cp >= '0' && *cp <= '7')
+                               opts = (opts << 3) | (*cp++ - '0');
+                       if (*cp++ != ' ') {
+                               error("server: options not delimited\n");
+                               return;
+                       }
+                       install(cp, opts);
+                       continue;
+
+               case 'L':  /* Log. save message in log file */
+                       log(lfp, cp);
+                       continue;
+#endif
+
+               case '\1':
+                       nerrs++;
+                       continue;
+
+               case '\2':
+                       return;
+
+               default:
+                       error("server: unknown command '%s'\n", cp);
+               case '\0':
+                       continue;
+               }
+       }
+}
+
+/*
+ * Update the file(s) if they are different.
+ * destdir = 1 if destination should be a directory
+ * (i.e., more than one source is being copied to the same destination).
+ */
+void
+install(src, dest, destdir, opts)
+       char *src, *dest;
+       int destdir, opts;
+{
+       char *rname;
+       char destcopy[BUFSIZ];
+
+       if (dest == NULL) {
+               opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
+               dest = src;
+       }
+
+       if (nflag || debug) {
+               printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
+                       opts & WHOLE ? " -w" : "",
+                       opts & YOUNGER ? " -y" : "",
+                       opts & COMPARE ? " -b" : "",
+                       opts & REMOVE ? " -R" : "", src, dest);
+               if (nflag)
+                       return;
+       }
+
+       rname = exptilde(target, src);
+       if (rname == NULL)
+               return;
+       tp = target;
+       while (*tp)
+               tp++;
+       /*
+        * If we are renaming a directory and we want to preserve
+        * the directory heirarchy (-w), we must strip off the leading
+        * directory name and preserve the rest.
+        */
+       if (opts & WHOLE) {
+               while (*rname == '/')
+                       rname++;
+               destdir = 1;
+       } else {
+               rname = rindex(target, '/');
+               if (rname == NULL)
+                       rname = target;
+               else
+                       rname++;
+       }
+       if (debug)
+               printf("target = %s, rname = %s\n", target, rname);
+       /*
+        * Pass the destination file/directory name to remote.
+        */
+       (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
+       if (debug)
+               printf("buf = %s", buf);
+       (void) write(rem, buf, strlen(buf));
+       if (response() < 0)
+               return;
+
+       if (destdir) {
+               strcpy(destcopy, dest);
+               Tdest = destcopy;
+       }
+       sendf(rname, opts);
+       Tdest = 0;
+}
+
+#define protoname() (pw ? pw->pw_name : user)
+#define protogroup() (gr ? gr->gr_name : group)
+/*
+ * Transfer the file or directory in target[].
+ * rname is the name of the file on the remote host.
+ */
+static void
+sendf(rname, opts)
+       char *rname;
+       int opts;
+{
+       register struct subcmd *sc;
+       struct stat stb;
+       int sizerr, f, u, len;
+       off_t i;
+       DIR *d;
+       struct direct *dp;
+       char *otp, *cp;
+       extern struct subcmd *subcmds;
+       static char user[15], group[15];
+
+       if (debug)
+               printf("sendf(%s, %x)\n", rname, opts);
+
+       if (except(target))
+               return;
+       if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
+               error("%s: %s\n", target, strerror(errno));
+               return;
+       }
+       if ((u = update(rname, opts, &stb)) == 0) {
+               if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
+                       (void) savelink(&stb);
+               return;
+       }
+
+       if (pw == NULL || pw->pw_uid != stb.st_uid)
+               if ((pw = getpwuid(stb.st_uid)) == NULL) {
+                       log(lfp, "%s: no password entry for uid %d \n",
+                               target, stb.st_uid);
+                       pw = NULL;
+                       (void)sprintf(user, ":%lu", stb.st_uid);
+               }
+       if (gr == NULL || gr->gr_gid != stb.st_gid)
+               if ((gr = getgrgid(stb.st_gid)) == NULL) {
+                       log(lfp, "%s: no name for group %d\n",
+                               target, stb.st_gid);
+                       gr = NULL;
+                       (void)sprintf(group, ":%lu", stb.st_gid);
+               }
+       if (u == 1) {
+               if (opts & VERIFY) {
+                       log(lfp, "need to install: %s\n", target);
+                       goto dospecial;
+               }
+               log(lfp, "installing: %s\n", target);
+               opts &= ~(COMPARE|REMOVE);
+       }
+
+       switch (stb.st_mode & S_IFMT) {
+       case S_IFDIR:
+               if ((d = opendir(target)) == NULL) {
+                       error("%s: %s\n", target, strerror(errno));
+                       return;
+               }
+               (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
+                       stb.st_mode & 07777, protoname(), protogroup(), rname);
+               if (debug)
+                       printf("buf = %s", buf);
+               (void) write(rem, buf, strlen(buf));
+               if (response() < 0) {
+                       closedir(d);
+                       return;
+               }
+
+               if (opts & REMOVE)
+                       rmchk(opts);
+
+               otp = tp;
+               len = tp - target;
+               while (dp = readdir(d)) {
+                       if (!strcmp(dp->d_name, ".") ||
+                           !strcmp(dp->d_name, ".."))
+                               continue;
+                       if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+                               error("%s/%s: Name too long\n", target,
+                                       dp->d_name);
+                               continue;
+                       }
+                       tp = otp;
+                       *tp++ = '/';
+                       cp = dp->d_name;
+                       while (*tp++ = *cp++)
+                               ;
+                       tp--;
+                       sendf(dp->d_name, opts);
+               }
+               closedir(d);
+               (void) write(rem, "E\n", 2);
+               (void) response();
+               tp = otp;
+               *tp = '\0';
+               return;
+
+       case S_IFLNK:
+               if (u != 1)
+                       opts |= COMPARE;
+               if (stb.st_nlink > 1) {
+                       struct linkbuf *lp;
+
+                       if ((lp = savelink(&stb)) != NULL) {
+                               /* install link */
+                               if (*lp->target == 0)
+                               (void) sprintf(buf, "k%o %s %s\n", opts,
+                                       lp->pathname, rname);
+                               else
+                               (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+                                       lp->target, lp->pathname, rname);
+                               if (debug)
+                                       printf("buf = %s", buf);
+                               (void) write(rem, buf, strlen(buf));
+                               (void) response();
+                               return;
+                       }
+               }
+               (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts,
+                       stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+                       protoname(), protogroup(), rname);
+               if (debug)
+                       printf("buf = %s", buf);
+               (void) write(rem, buf, strlen(buf));
+               if (response() < 0)
+                       return;
+               sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
+               (void) write(rem, buf, stb.st_size);
+               if (debug)
+                       printf("readlink = %.*s\n", (int)stb.st_size, buf);
+               goto done;
+
+       case S_IFREG:
+               break;
+
+       default:
+               error("%s: not a file or directory\n", target);
+               return;
+       }
+
+       if (u == 2) {
+               if (opts & VERIFY) {
+                       log(lfp, "need to update: %s\n", target);
+                       goto dospecial;
+               }
+               log(lfp, "updating: %s\n", target);
+       }
+
+       if (stb.st_nlink > 1) {
+               struct linkbuf *lp;
+
+               if ((lp = savelink(&stb)) != NULL) {
+                       /* install link */
+                       if (*lp->target == 0)
+                       (void) sprintf(buf, "k%o %s %s\n", opts,
+                               lp->pathname, rname);
+                       else
+                       (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+                               lp->target, lp->pathname, rname);
+                       if (debug)
+                               printf("buf = %s", buf);
+                       (void) write(rem, buf, strlen(buf));
+                       (void) response();
+                       return;
+               }
+       }
+
+       if ((f = open(target, O_RDONLY, 0)) < 0) {
+               error("%s: %s\n", target, strerror(errno));
+               return;
+       }
+       (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts,
+               stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+               protoname(), protogroup(), rname);
+       if (debug)
+               printf("buf = %s", buf);
+       (void) write(rem, buf, strlen(buf));
+       if (response() < 0) {
+               (void) close(f);
+               return;
+       }
+       sizerr = 0;
+       for (i = 0; i < stb.st_size; i += BUFSIZ) {
+               int amt = BUFSIZ;
+               if (i + amt > stb.st_size)
+                       amt = stb.st_size - i;
+               if (sizerr == 0 && read(f, buf, amt) != amt)
+                       sizerr = 1;
+               (void) write(rem, buf, amt);
+       }
+       (void) close(f);
+done:
+       if (sizerr) {
+               error("%s: file changed size\n", target);
+               err();
+       } else
+               ack();
+       f = response();
+       if (f < 0 || f == 0 && (opts & COMPARE))
+               return;
+dospecial:
+       for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+               if (sc->sc_type != SPECIAL)
+                       continue;
+               if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
+                       continue;
+               log(lfp, "special \"%s\"\n", sc->sc_name);
+               if (opts & VERIFY)
+                       continue;
+               (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
+               if (debug)
+                       printf("buf = %s", buf);
+               (void) write(rem, buf, strlen(buf));
+               while (response() > 0)
+                       ;
+       }
+}
+
+static struct linkbuf *
+savelink(stp)
+       struct stat *stp;
+{
+       struct linkbuf *lp;
+
+       for (lp = ihead; lp != NULL; lp = lp->nextp)
+               if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
+                       lp->count--;
+                       return(lp);
+               }
+       lp = (struct linkbuf *) malloc(sizeof(*lp));
+       if (lp == NULL)
+               log(lfp, "out of memory, link information lost\n");
+       else {
+               lp->nextp = ihead;
+               ihead = lp;
+               lp->inum = stp->st_ino;
+               lp->devnum = stp->st_dev;
+               lp->count = stp->st_nlink - 1;
+               strcpy(lp->pathname, target);
+               if (Tdest)
+                       strcpy(lp->target, Tdest);
+               else
+                       *lp->target = 0;
+       }
+       return(NULL);
+}
+
+/*
+ * Check to see if file needs to be updated on the remote machine.
+ * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
+ * and 3 if comparing binaries to determine if out of date.
+ */
+static int
+update(rname, opts, stp)
+       char *rname;
+       int opts;
+       struct stat *stp;
+{
+       register char *cp, *s;
+       register off_t size;
+       register time_t mtime;
+
+       if (debug) 
+               printf("update(%s, %x, %x)\n", rname, opts, stp);
+
+       /*
+        * Check to see if the file exists on the remote machine.
+        */
+       (void) sprintf(buf, "Q%s\n", rname);
+       if (debug)
+               printf("buf = %s", buf);
+       (void) write(rem, buf, strlen(buf));
+again:
+       cp = s = buf;
+       do {
+               if (read(rem, cp, 1) != 1)
+                       lostconn(0);
+       } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+
+       switch (*s++) {
+       case 'Y':
+               break;
+
+       case 'N':  /* file doesn't exist so install it */
+               return(1);
+
+       case '\1':
+               nerrs++;
+               if (*s != '\n') {
+                       if (!iamremote) {
+                               fflush(stdout);
+                               (void) write(2, s, cp - s);
+                       }
+                       if (lfp != NULL)
+                               (void) fwrite(s, 1, cp - s, lfp);
+               }
+               return(0);
+
+       case '\3':
+               *--cp = '\0';
+               if (lfp != NULL) 
+                       log(lfp, "update: note: %s\n", s);
+               goto again;
+
+       default:
+               *--cp = '\0';
+               error("update: unexpected response '%s'\n", s);
+               return(0);
+       }
+
+       if (*s == '\n')
+               return(2);
+
+       if (opts & COMPARE)
+               return(3);
+
+       size = 0;
+       while (isdigit(*s))
+               size = size * 10 + (*s++ - '0');
+       if (*s++ != ' ') {
+               error("update: size not delimited\n");
+               return(0);
+       }
+       mtime = 0;
+       while (isdigit(*s))
+               mtime = mtime * 10 + (*s++ - '0');
+       if (*s != '\n') {
+               error("update: mtime not delimited\n");
+               return(0);
+       }
+       /*
+        * File needs to be updated?
+        */
+       if (opts & YOUNGER) {
+               if (stp->st_mtime == mtime)
+                       return(0);
+               if (stp->st_mtime < mtime) {
+                       log(lfp, "Warning: %s: remote copy is newer\n", target);
+                       return(0);
+               }
+       } else if (stp->st_mtime == mtime && stp->st_size == size)
+               return(0);
+       return(2);
+}
+
+/*
+ * Query. Check to see if file exists. Return one of the following:
+ *     N\n             - doesn't exist
+ *     Ysize mtime\n   - exists and its a regular file (size & mtime of file)
+ *     Y\n             - exists and its a directory or symbolic link
+ *     ^Aerror message\n
+ */
+static void
+query(name)
+       char *name;
+{
+       struct stat stb;
+
+       if (catname)
+               (void) sprintf(tp, "/%s", name);
+
+       if (lstat(target, &stb) < 0) {
+               if (errno == ENOENT)
+                       (void) write(rem, "N\n", 2);
+               else
+                       error("%s:%s: %s\n", host, target, strerror(errno));
+               *tp = '\0';
+               return;
+       }
+
+       switch (stb.st_mode & S_IFMT) {
+       case S_IFREG:
+               (void) sprintf(buf, "Y%qd %ld\n", stb.st_size,
+                   stb.st_mtime);
+               (void) write(rem, buf, strlen(buf));
+               break;
+
+       case S_IFLNK:
+       case S_IFDIR:
+               (void) write(rem, "Y\n", 2);
+               break;
+
+       default:
+               error("%s: not a file or directory\n", name);
+               break;
+       }
+       *tp = '\0';
+}
+
+static void
+recvf(cmd, type)
+       char *cmd;
+       int type;
+{
+       register char *cp;
+       int f, mode, opts, wrerr, olderrno;
+       off_t i, size;
+       time_t mtime;
+       struct stat stb;
+       struct timeval tvp[2];
+       char *owner, *group;
+       char new[BUFSIZ];
+       extern char *tempname;
+
+       cp = cmd;
+       opts = 0;
+       f = 0;          /*  Initialize, so for links it remains 0.  */
+       while (*cp >= '0' && *cp <= '7')
+               opts = (opts << 3) | (*cp++ - '0');
+       if (*cp++ != ' ') {
+               error("recvf: options not delimited\n");
+               return;
+       }
+       mode = 0;
+       while (*cp >= '0' && *cp <= '7')
+               mode = (mode << 3) | (*cp++ - '0');
+       if (*cp++ != ' ') {
+               error("recvf: mode not delimited\n");
+               return;
+       }
+       size = 0;
+       while (isdigit(*cp))
+               size = size * 10 + (*cp++ - '0');
+       if (*cp++ != ' ') {
+               error("recvf: size not delimited\n");
+               return;
+       }
+       mtime = 0;
+       while (isdigit(*cp))
+               mtime = mtime * 10 + (*cp++ - '0');
+       if (*cp++ != ' ') {
+               error("recvf: mtime not delimited\n");
+               return;
+       }
+       owner = cp;
+       while (*cp && *cp != ' ')
+               cp++;
+       if (*cp != ' ') {
+               error("recvf: owner name not delimited\n");
+               return;
+       }
+       *cp++ = '\0';
+       group = cp;
+       while (*cp && *cp != ' ')
+               cp++;
+       if (*cp != ' ') {
+               error("recvf: group name not delimited\n");
+               return;
+       }
+       *cp++ = '\0';
+
+       if (type == S_IFDIR) {
+               if (catname >= sizeof(stp)) {
+                       error("%s:%s: too many directory levels\n",
+                               host, target);
+                       return;
+               }
+               stp[catname] = tp;
+               if (catname++) {
+                       *tp++ = '/';
+                       while (*tp++ = *cp++)
+                               ;
+                       tp--;
+               }
+               if (opts & VERIFY) {
+                       ack();
+                       return;
+               }
+               if (lstat(target, &stb) == 0) {
+                       if (ISDIR(stb.st_mode)) {
+                               if ((stb.st_mode & 07777) == mode) {
+                                       ack();
+                                       return;
+                               }
+                               buf[0] = '\0';
+                               (void) sprintf(buf + 1,
+                                       "%s: Warning: remote mode %o != local mode %o\n",
+                                       target, stb.st_mode & 07777, mode);
+                               (void) write(rem, buf, strlen(buf + 1) + 1);
+                               return;
+                       }
+                       errno = ENOTDIR;
+               } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
+                   chkparent(target) == 0 && mkdir(target, mode) == 0)) {
+                       if (fchog(-1, target, owner, group, mode) == 0)
+                               ack();
+                       return;
+               }
+               error("%s:%s: %s\n", host, target, strerror(errno));
+               tp = stp[--catname];
+               *tp = '\0';
+               return;
+       }
+
+       if (catname)
+               (void) sprintf(tp, "/%s", cp);
+       cp = rindex(target, '/');
+       if (cp == NULL)
+               strcpy(new, tempname);
+       else if (cp == target)
+               (void) sprintf(new, "/%s", tempname);
+       else {
+               *cp = '\0';
+               (void) sprintf(new, "%s/%s", target, tempname);
+               *cp = '/';
+       }
+
+       if (type == S_IFLNK) {
+               int j;
+
+               ack();
+               cp = buf;
+               for (i = 0; i < size; i += j) {
+                       if ((j = read(rem, cp, size - i)) <= 0)
+                               cleanup(0);
+                       cp += j;
+               }
+               *cp = '\0';
+               if (response() < 0) {
+                       err();
+                       return;
+               }
+               if (symlink(buf, new) < 0) {
+                       if (errno != ENOENT || chkparent(new) < 0 ||
+                           symlink(buf, new) < 0)
+                               goto badnew1;
+               }
+               mode &= 0777;
+               if (opts & COMPARE) {
+                       char tbuf[BUFSIZ];
+
+                       if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
+                           i == size && strncmp(buf, tbuf, size) == 0) {
+                               (void) unlink(new);
+                               ack();
+                               return;
+                       }
+                       if (opts & VERIFY)
+                               goto differ;
+               }
+               goto fixup;
+       }
+
+       if ((f = creat(new, mode)) < 0) {
+               if (errno != ENOENT || chkparent(new) < 0 ||
+                   (f = creat(new, mode)) < 0)
+                       goto badnew1;
+       }
+
+       ack();
+       wrerr = 0;
+       for (i = 0; i < size; i += BUFSIZ) {
+               int amt = BUFSIZ;
+
+               cp = buf;
+               if (i + amt > size)
+                       amt = size - i;
+               do {
+                       int j = read(rem, cp, amt);
+
+                       if (j <= 0) {
+                               (void) close(f);
+                               (void) unlink(new);
+                               cleanup(0);
+                       }
+                       amt -= j;
+                       cp += j;
+               } while (amt > 0);
+               amt = BUFSIZ;
+               if (i + amt > size)
+                       amt = size - i;
+               if (wrerr == 0 && write(f, buf, amt) != amt) {
+                       olderrno = errno;
+                       wrerr++;
+               }
+       }
+       if (response() < 0) {
+               err();
+               goto badnew2;
+       }
+       if (wrerr)
+               goto badnew1;
+       if (opts & COMPARE) {
+               FILE *f1, *f2;
+               int c;
+
+               if ((f1 = fopen(target, "r")) == NULL)
+                       goto badtarget;
+               if ((f2 = fopen(new, "r")) == NULL) {
+badnew1:               error("%s:%s: %s\n", host, new, strerror(errno));
+                       goto badnew2;
+               }
+               while ((c = getc(f1)) == getc(f2))
+                       if (c == EOF) {
+                               (void) fclose(f1);
+                               (void) fclose(f2);
+                               ack();
+                               goto badnew2;
+                       }
+               (void) fclose(f1);
+               (void) fclose(f2);
+               if (opts & VERIFY) {
+differ:                        buf[0] = '\0';
+                       (void) sprintf(buf + 1, "need to update: %s\n",target);
+                       (void) write(rem, buf, strlen(buf + 1) + 1);
+                       goto badnew2;
+               }
+       }
+
+       /*
+        * Set last modified time
+        */
+       tvp[0].tv_sec = time(0);
+       tvp[0].tv_usec = 0;
+       tvp[1].tv_sec = mtime;
+       tvp[1].tv_usec = 0;
+       if (utimes(new, tvp) < 0)
+               note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
+
+       if (fchog(f, new, owner, group, mode) < 0) {
+badnew2:       
+               if (f)          /*  Don't close if f hasn't been opened.  */
+                       (void) close(f);
+               (void) unlink(new);
+               return;
+       }
+       (void) close(f);
+
+fixup: if (rename(new, target) < 0) {
+badtarget:     error("%s:%s: %s\n", host, target, strerror(errno));
+               (void) unlink(new);
+               return;
+       }
+
+       if (opts & COMPARE) {
+               buf[0] = '\0';
+               (void) sprintf(buf + 1, "updated %s\n", target);
+               (void) write(rem, buf, strlen(buf + 1) + 1);
+       } else
+               ack();
+}
+
+/*
+ * Creat a hard link to existing file.
+ */
+static void
+hardlink(cmd)
+       char *cmd;
+{
+       register char *cp;
+       struct stat stb;
+       char *oldname;
+       int opts, exists = 0;
+
+       cp = cmd;
+       opts = 0;
+       while (*cp >= '0' && *cp <= '7')
+               opts = (opts << 3) | (*cp++ - '0');
+       if (*cp++ != ' ') {
+               error("hardlink: options not delimited\n");
+               return;
+       }
+       oldname = cp;
+       while (*cp && *cp != ' ')
+               cp++;
+       if (*cp != ' ') {
+               error("hardlink: oldname name not delimited\n");
+               return;
+       }
+       *cp++ = '\0';
+
+       if (catname) {
+               (void) sprintf(tp, "/%s", cp);
+       }
+       if (lstat(target, &stb) == 0) {
+               int mode = stb.st_mode & S_IFMT;
+               if (mode != S_IFREG && mode != S_IFLNK) {
+                       error("%s:%s: not a regular file\n", host, target);
+                       return;
+               }
+               exists = 1;
+       }
+       if (chkparent(target) < 0 ) {
+               error("%s:%s: %s (no parent)\n",
+                       host, target, strerror(errno));
+               return;
+       }
+       if (exists && (unlink(target) < 0)) {
+               error("%s:%s: %s (unlink)\n",
+                       host, target, strerror(errno));
+               return;
+       }
+       if (link(oldname, target) < 0) {
+               error("%s:can't link %s to %s\n",
+                       host, target, oldname);
+               return;
+       }
+       ack();
+}
+
+/*
+ * Check to see if parent directory exists and create one if not.
+ */
+static int
+chkparent(name)
+       char *name;
+{
+       register char *cp;
+       struct stat stb;
+
+       cp = rindex(name, '/');
+       if (cp == NULL || cp == name)
+               return(0);
+       *cp = '\0';
+       if (lstat(name, &stb) < 0) {
+               if (errno == ENOENT && chkparent(name) >= 0 &&
+                   mkdir(name, 0777 & ~oumask) >= 0) {
+                       *cp = '/';
+                       return(0);
+               }
+       } else if (ISDIR(stb.st_mode)) {
+               *cp = '/';
+               return(0);
+       }
+       *cp = '/';
+       return(-1);
+}
+
+/*
+ * Change owner, group and mode of file.
+ */
+static int
+fchog(fd, file, owner, group, mode)
+       int fd;
+       char *file, *owner, *group;
+       int mode;
+{
+       register int i;
+       int uid, gid;
+       extern char user[];
+       extern int userid;
+
+       uid = userid;
+       if (userid == 0) {
+               if (*owner == ':') {
+                       uid = atoi(owner + 1);
+               } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
+                       if ((pw = getpwnam(owner)) == NULL) {
+                               if (mode & 04000) {
+                                       note("%s:%s: unknown login name, clearing setuid",
+                                               host, owner);
+                                       mode &= ~04000;
+                                       uid = 0;
+                               }
+                       } else
+                               uid = pw->pw_uid;
+               } else
+                       uid = pw->pw_uid;
+               if (*group == ':') {
+                       gid = atoi(group + 1);
+                       goto ok;
+               }
+       } else if ((mode & 04000) && strcmp(user, owner) != 0)
+               mode &= ~04000;
+       gid = -1;
+       if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
+               if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
+                  || ((gr = getgrnam(group)) == NULL)) {
+                       if (mode & 02000) {
+                               note("%s:%s: unknown group", host, group);
+                               mode &= ~02000;
+                       }
+               } else
+                       gid = gr->gr_gid;
+       } else
+               gid = gr->gr_gid;
+       if (userid && gid >= 0) {
+               if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
+                       if (!(strcmp(user, gr->gr_mem[i])))
+                               goto ok;
+               mode &= ~02000;
+               gid = -1;
+       }
+ok:    if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
+               note("%s: %s chown: %s", host, file, strerror(errno));
+       else if (mode & 07000 &&
+          (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
+               note("%s: %s chmod: %s", host, file, strerror(errno));
+       return(0);
+}
+
+/*
+ * Check for files on the machine being updated that are not on the master
+ * machine and remove them.
+ */
+static void
+rmchk(opts)
+       int opts;
+{
+       register char *cp, *s;
+       struct stat stb;
+
+       if (debug)
+               printf("rmchk()\n");
+
+       /*
+        * Tell the remote to clean the files from the last directory sent.
+        */
+       (void) sprintf(buf, "C%o\n", opts & VERIFY);
+       if (debug)
+               printf("buf = %s", buf);
+       (void) write(rem, buf, strlen(buf));
+       if (response() < 0)
+               return;
+       for (;;) {
+               cp = s = buf;
+               do {
+                       if (read(rem, cp, 1) != 1)
+                               lostconn(0);
+               } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+
+               switch (*s++) {
+               case 'Q': /* Query if file should be removed */
+                       /*
+                        * Return the following codes to remove query.
+                        * N\n -- file exists - DON'T remove.
+                        * Y\n -- file doesn't exist - REMOVE.
+                        */
+                       *--cp = '\0';
+                       (void) sprintf(tp, "/%s", s);
+                       if (debug)
+                               printf("check %s\n", target);
+                       if (except(target))
+                               (void) write(rem, "N\n", 2);
+                       else if (lstat(target, &stb) < 0)
+                               (void) write(rem, "Y\n", 2);
+                       else
+                               (void) write(rem, "N\n", 2);
+                       break;
+
+               case '\0':
+                       *--cp = '\0';
+                       if (*s != '\0')
+                               log(lfp, "%s\n", s);
+                       break;
+
+               case 'E':
+                       *tp = '\0';
+                       ack();
+                       return;
+
+               case '\1':
+               case '\2':
+                       nerrs++;
+                       if (*s != '\n') {
+                               if (!iamremote) {
+                                       fflush(stdout);
+                                       (void) write(2, s, cp - s);
+                               }
+                               if (lfp != NULL)
+                                       (void) fwrite(s, 1, cp - s, lfp);
+                       }
+                       if (buf[0] == '\2')
+                               lostconn(0);
+                       break;
+
+               default:
+                       error("rmchk: unexpected response '%s'\n", buf);
+                       err();
+               }
+       }
+}
+
+/*
+ * Check the current directory (initialized by the 'T' command to server())
+ * for extraneous files and remove them.
+ */
+static void
+clean(cp)
+       register char *cp;
+{
+       DIR *d;
+       register struct direct *dp;
+       struct stat stb;
+       char *otp;
+       int len, opts;
+
+       opts = 0;
+       while (*cp >= '0' && *cp <= '7')
+               opts = (opts << 3) | (*cp++ - '0');
+       if (*cp != '\0') {
+               error("clean: options not delimited\n");
+               return;
+       }
+       if ((d = opendir(target)) == NULL) {
+               error("%s:%s: %s\n", host, target, strerror(errno));
+               return;
+       }
+       ack();
+
+       otp = tp;
+       len = tp - target;
+       while (dp = readdir(d)) {
+               if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+                       continue;
+               if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+                       error("%s:%s/%s: Name too long\n",
+                               host, target, dp->d_name);
+                       continue;
+               }
+               tp = otp;
+               *tp++ = '/';
+               cp = dp->d_name;;
+               while (*tp++ = *cp++)
+                       ;
+               tp--;
+               if (lstat(target, &stb) < 0) {
+                       error("%s:%s: %s\n", host, target, strerror(errno));
+                       continue;
+               }
+               (void) sprintf(buf, "Q%s\n", dp->d_name);
+               (void) write(rem, buf, strlen(buf));
+               cp = buf;
+               do {
+                       if (read(rem, cp, 1) != 1)
+                               cleanup(0);
+               } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+               *--cp = '\0';
+               cp = buf;
+               if (*cp != 'Y')
+                       continue;
+               if (opts & VERIFY) {
+                       cp = buf;
+                       *cp++ = '\0';
+                       (void) sprintf(cp, "need to remove: %s\n", target);
+                       (void) write(rem, buf, strlen(cp) + 1);
+               } else
+                       removeit(&stb);
+       }
+       closedir(d);
+       (void) write(rem, "E\n", 2);
+       (void) response();
+       tp = otp;
+       *tp = '\0';
+}
+
+/*
+ * Remove a file or directory (recursively) and send back an acknowledge
+ * or an error message.
+ */
+static void
+removeit(stp)
+       struct stat *stp;
+{
+       DIR *d;
+       struct direct *dp;
+       register char *cp;
+       struct stat stb;
+       char *otp;
+       int len;
+
+       switch (stp->st_mode & S_IFMT) {
+       case S_IFREG:
+       case S_IFLNK:
+               if (unlink(target) < 0)
+                       goto bad;
+               goto removed;
+
+       case S_IFDIR:
+               break;
+
+       default:
+               error("%s:%s: not a plain file\n", host, target);
+               return;
+       }
+
+       if ((d = opendir(target)) == NULL)
+               goto bad;
+
+       otp = tp;
+       len = tp - target;
+       while (dp = readdir(d)) {
+               if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+                       continue;
+               if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+                       error("%s:%s/%s: Name too long\n",
+                               host, target, dp->d_name);
+                       continue;
+               }
+               tp = otp;
+               *tp++ = '/';
+               cp = dp->d_name;;
+               while (*tp++ = *cp++)
+                       ;
+               tp--;
+               if (lstat(target, &stb) < 0) {
+                       error("%s:%s: %s\n", host, target, strerror(errno));
+                       continue;
+               }
+               removeit(&stb);
+       }
+       closedir(d);
+       tp = otp;
+       *tp = '\0';
+       if (rmdir(target) < 0) {
+bad:
+               error("%s:%s: %s\n", host, target, strerror(errno));
+               return;
+       }
+removed:
+       cp = buf;
+       *cp++ = '\0';
+       (void) sprintf(cp, "removed %s\n", target);
+       (void) write(rem, buf, strlen(cp) + 1);
+}
+
+/*
+ * Execute a shell command to handle special cases.
+ */
+static void
+dospecial(cmd)
+       char *cmd;
+{
+       int fd[2], status, pid, i;
+       register char *cp, *s;
+       char sbuf[BUFSIZ];
+       extern int userid, groupid;
+
+       if (pipe(fd) < 0) {
+               error("%s\n", strerror(errno));
+               return;
+       }
+       if ((pid = fork()) == 0) {
+               /*
+                * Return everything the shell commands print.
+                */
+               (void) close(0);
+               (void) close(1);
+               (void) close(2);
+               (void) open(_PATH_DEVNULL, O_RDONLY);
+               (void) dup(fd[1]);
+               (void) dup(fd[1]);
+               (void) close(fd[0]);
+               (void) close(fd[1]);
+               setgid(groupid);
+               setuid(userid);
+               execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
+               _exit(127);
+       }
+       (void) close(fd[1]);
+       s = sbuf;
+       *s++ = '\0';
+       while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
+               cp = buf;
+               do {
+                       *s++ = *cp++;
+                       if (cp[-1] != '\n') {
+                               if (s < &sbuf[sizeof(sbuf)-1])
+                                       continue;
+                               *s++ = '\n';
+                       }
+                       /*
+                        * Throw away blank lines.
+                        */
+                       if (s == &sbuf[2]) {
+                               s--;
+                               continue;
+                       }
+                       (void) write(rem, sbuf, s - sbuf);
+                       s = &sbuf[1];
+               } while (--i);
+       }
+       if (s > &sbuf[1]) {
+               *s++ = '\n';
+               (void) write(rem, sbuf, s - sbuf);
+       }
+       while ((i = wait(&status)) != pid && i != -1)
+               ;
+       if (i == -1)
+               status = -1;
+       (void) close(fd[0]);
+       if (status)
+               error("shell returned %d\n", status);
+       else
+               ack();
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+log(FILE *fp, const char *fmt, ...)
+#else
+log(fp, fmt, va_alist)
+       FILE *fp;
+       char *fmt;
+        va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       /* Print changes locally if not quiet mode */
+       if (!qflag)
+               (void)vprintf(fmt, ap);
+
+       /* Save changes (for mailing) if really updating files */
+       if (!(options & VERIFY) && fp != NULL)
+               (void)vfprintf(fp, fmt, ap);
+       va_end(ap);
+}
+
+void
+#if __STDC__
+error(const char *fmt, ...)
+#else
+error(fmt, va_alist)
+       char *fmt;
+        va_dcl
+#endif
+{
+       static FILE *fp;
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+
+       ++nerrs;
+       if (!fp && !(fp = fdopen(rem, "w")))
+               return;
+       if (iamremote) {
+               (void)fprintf(fp, "%crdist: ", 0x01);
+               (void)vfprintf(fp, fmt, ap);
+               fflush(fp);
+       }
+       else {
+               fflush(stdout);
+               (void)fprintf(stderr, "rdist: ");
+               (void)vfprintf(stderr, fmt, ap);
+               fflush(stderr);
+       }
+       if (lfp != NULL) {
+               (void)fprintf(lfp, "rdist: ");
+               (void)vfprintf(lfp, fmt, ap);
+               fflush(lfp);
+       }
+       va_end(ap);
+}
+
+void
+#if __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+       char *fmt;
+        va_dcl
+#endif
+{
+       static FILE *fp;
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+
+       ++nerrs;
+       if (!fp && !(fp = fdopen(rem, "w")))
+               return;
+       if (iamremote) {
+               (void)fprintf(fp, "%crdist: ", 0x02);
+               (void)vfprintf(fp, fmt, ap);
+               fflush(fp);
+       }
+       else {
+               fflush(stdout);
+               (void)fprintf(stderr, "rdist: ");
+               (void)vfprintf(stderr, fmt, ap);
+               fflush(stderr);
+       }
+       if (lfp != NULL) {
+               (void)fprintf(lfp, "rdist: ");
+               (void)vfprintf(lfp, fmt, ap);
+               fflush(lfp);
+       }
+       cleanup(0);
+}
+
+static int
+response()
+{
+       char *cp, *s;
+       char resp[BUFSIZ];
+
+       if (debug)
+               printf("response()\n");
+
+       cp = s = resp;
+       do {
+               if (read(rem, cp, 1) != 1)
+                       lostconn(0);
+       } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
+
+       switch (*s++) {
+       case '\0':
+               *--cp = '\0';
+               if (*s != '\0') {
+                       log(lfp, "%s\n", s);
+                       return(1);
+               }
+               return(0);
+       case '\3':
+               *--cp = '\0';
+               log(lfp, "Note: %s\n",s);
+               return(response());
+
+       default:
+               s--;
+               /* fall into... */
+       case '\1':
+       case '\2':
+               nerrs++;
+               if (*s != '\n') {
+                       if (!iamremote) {
+                               fflush(stdout);
+                               (void) write(2, s, cp - s);
+                       }
+                       if (lfp != NULL)
+                               (void) fwrite(s, 1, cp - s, lfp);
+               }
+               if (resp[0] == '\2')
+                       lostconn(0);
+               return(-1);
+       }
+}
+
+/*
+ * Remove temporary files and do any cleanup operations before exiting.
+ */
+void
+cleanup(signo)
+       int signo;
+{
+       (void) unlink(tempfile);
+       exit(1);
+}
+
+static void
+#if __STDC__
+note(const char *fmt, ...)
+#else
+note(fmt, va_alist)
+       char *fmt;
+        va_dcl
+#endif
+{
+       static char buf[BUFSIZ];
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+       comment(buf);
+}
+
+static void
+comment(s)
+       char *s;
+{
+       char c;
+
+       c = '\3';
+       write(rem, &c, 1);
+       write(rem, s, strlen(s));
+       c = '\n';
+       write(rem, &c, 1);
+}
index 617ba3d..f6d186c 100644 (file)
@@ -1,12 +1,11 @@
 #      from: @(#)Makefile      5.11 (Berkeley) 3/12/91
-#      $Id: Makefile,v 1.1.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 (file)
index 0000000..5beac4e
--- /dev/null
@@ -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 (file)
index 0000000..14bd028
--- /dev/null
@@ -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 (file)
index 0000000..80c9b11
--- /dev/null
@@ -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 (file)
index 0000000..9641dc5
--- /dev/null
@@ -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 (file)
index 0000000..b6d24f9
--- /dev/null
@@ -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 (file)
index 0000000..f593de6
--- /dev/null
@@ -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__ */
index 9108d11..c369e49 100644 (file)
@@ -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
  * 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.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 (file)
index 0000000..9123ee5
--- /dev/null
@@ -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);
+}
+
index 931a9d3..f9ac062 100644 (file)
@@ -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
  */
 
 #ifndef lint
-/* from: static char sccsid[] = "@(#)docmd.c   8.1 (Berkeley) 6/9/93"; */
-static char *rcsid = "$Id: docmd.c,v 1.1.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);
 }
index 8a55c52..82aec43 100644 (file)
@@ -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
  */
 
 #ifndef lint
-/* from: static char sccsid[] = "@(#)expand.c  8.1 (Berkeley) 6/9/93"; */
-static char *rcsid = "$Id: expand.c,v 1.1.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);
-}
index a4b9f54..9e4d451 100644 (file)
@@ -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
  */
 
 #ifndef lint
-/* from: static char sccsid[] = "@(#)gram.y    8.1 (Berkeley) 6/9/93"; */
-static char *rcsid = "$Id: gram.y,v 1.1.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 (file)
index 0000000..27a6ec4
--- /dev/null
@@ -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);
+}
+
index fc3ecca..1477ac1 100644 (file)
@@ -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
  */
 
 #ifndef lint
-/* from: static char sccsid[] = "@(#)lookup.c  8.1 (Berkeley) 6/9/93"; */
-static char *rcsid = "$Id: lookup.c,v 1.1.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/main.c b/usr.bin/rdist/main.c
deleted file mode 100644 (file)
index a0c0dc7..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (c) 1983, 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-static char copyright[] =
-"@(#) Copyright (c) 1983, 1993\n\
-       The Regents of the University of California.  All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-/* from: static char sccsid[] = "@(#)main.c    8.1 (Berkeley) 6/9/93"; */
-static char *rcsid = "$Id: main.c,v 1.1.1.1 1995/10/18 08:45:59 deraadt Exp $";
-#endif /* not lint */
-
-#include "defs.h"
-
-#define NHOSTS 100
-
-/*
- * Remote distribution program.
- */
-
-char   *distfile = NULL;
-#define _RDIST_TMP     "/rdistXXXXXX"
-char   tempfile[sizeof _PATH_TMP + sizeof _RDIST_TMP + 1];
-char   *tempname;
-
-int    debug;          /* debugging flag */
-int    nflag;          /* NOP flag, just print commands without executing */
-int    qflag;          /* Quiet. Don't print messages */
-int    options;        /* global options */
-int    iamremote;      /* act as remote server for transfering files */
-
-FILE   *fin = NULL;    /* input file pointer */
-int    rem = -1;       /* file descriptor to remote source/sink process */
-char   host[32];       /* host name */
-int    nerrs;          /* number of errors while sending/receiving */
-char   user[10];       /* user's name */
-char   homedir[128];   /* user's home directory */
-int    userid;         /* user's user ID */
-int    groupid;        /* user's group ID */
-
-struct passwd *pw;     /* pointer to static area used by getpwent */
-struct group *gr;      /* pointer to static area used by getgrent */
-
-static void usage __P((void));
-static void docmdargs __P((int, char *[]));
-
-int
-main(argc, argv)
-       int argc;
-       char *argv[];
-{
-       register char *arg;
-       int cmdargs = 0;
-       char *dhosts[NHOSTS], **hp = dhosts;
-
-       pw = getpwuid(userid = getuid());
-       if (pw == NULL) {
-               fprintf(stderr, "%s: Who are you?\n", argv[0]);
-               exit(1);
-       }
-       strcpy(user, pw->pw_name);
-       strcpy(homedir, pw->pw_dir);
-       groupid = pw->pw_gid;
-       gethostname(host, sizeof(host));
-       strcpy(tempfile, _PATH_TMP);
-       strcat(tempfile, _RDIST_TMP);
-       if ((tempname = rindex(tempfile, '/')) != 0)
-               tempname++;
-       else
-               tempname = tempfile;
-
-       while (--argc > 0) {
-               if ((arg = *++argv)[0] != '-')
-                       break;
-               if (!strcmp(arg, "-Server"))
-                       iamremote++;
-               else while (*++arg)
-                       switch (*arg) {
-                       case 'f':
-                               if (--argc <= 0)
-                                       usage();
-                               distfile = *++argv;
-                               if (distfile[0] == '-' && distfile[1] == '\0')
-                                       fin = stdin;
-                               break;
-
-                       case 'm':
-                               if (--argc <= 0)
-                                       usage();
-                               if (hp >= &dhosts[NHOSTS-2]) {
-                                       fprintf(stderr, "rdist: too many destination hosts\n");
-                                       exit(1);
-                               }
-                               *hp++ = *++argv;
-                               break;
-
-                       case 'd':
-                               if (--argc <= 0)
-                                       usage();
-                               define(*++argv);
-                               break;
-
-                       case 'D':
-                               debug++;
-                               break;
-
-                       case 'c':
-                               cmdargs++;
-                               break;
-
-                       case 'n':
-                               if (options & VERIFY) {
-                                       printf("rdist: -n overrides -v\n");
-                                       options &= ~VERIFY;
-                               }
-                               nflag++;
-                               break;
-
-                       case 'q':
-                               qflag++;
-                               break;
-
-                       case 'b':
-                               options |= COMPARE;
-                               break;
-
-                       case 'R':
-                               options |= REMOVE;
-                               break;
-
-                       case 'v':
-                               if (nflag) {
-                                       printf("rdist: -n overrides -v\n");
-                                       break;
-                               }
-                               options |= VERIFY;
-                               break;
-
-                       case 'w':
-                               options |= WHOLE;
-                               break;
-
-                       case 'y':
-                               options |= YOUNGER;
-                               break;
-
-                       case 'h':
-                               options |= FOLLOW;
-                               break;
-
-                       case 'i':
-                               options |= IGNLNKS;
-                               break;
-
-                       default:
-                               usage();
-                       }
-       }
-       *hp = NULL;
-
-       seteuid(userid);
-       mktemp(tempfile);
-
-       if (iamremote) {
-               server();
-               exit(nerrs != 0);
-       }
-
-       if (cmdargs)
-               docmdargs(argc, argv);
-       else {
-               if (fin == NULL) {
-                       if(distfile == NULL) {
-                               if((fin = fopen("distfile","r")) == NULL)
-                                       fin = fopen("Distfile", "r");
-                       } else
-                               fin = fopen(distfile, "r");
-                       if(fin == NULL) {
-                               perror(distfile ? distfile : "distfile");
-                               exit(1);
-                       }
-               }
-               yyparse();
-               if (nerrs == 0)
-                       docmds(dhosts, argc, argv);
-       }
-
-       exit(nerrs != 0);
-}
-
-static void
-usage()
-{
-       printf("Usage: rdist [-nqbhirvwyD] [-f distfile] [-d var=value] [-m host] [file ...]\n");
-       printf("or: rdist [-nqbhirvwyD] -c source [...] machine[:dest]\n");
-       exit(1);
-}
-
-/*
- * rcp like interface for distributing files.
- */
-static void
-docmdargs(nargs, args)
-       int nargs;
-       char *args[];
-{
-       register struct namelist *nl, *prev;
-       register char *cp;
-       struct namelist *files, *hosts;
-       struct subcmd *cmds;
-       char *dest;
-       static struct namelist tnl = { NULL, NULL };
-       int i;
-
-       if (nargs < 2)
-               usage();
-
-       prev = NULL;
-       for (i = 0; i < nargs - 1; i++) {
-               nl = makenl(args[i]);
-               if (prev == NULL)
-                       files = prev = nl;
-               else {
-                       prev->n_next = nl;
-                       prev = nl;
-               }
-       }
-
-       cp = args[i];
-       if ((dest = index(cp, ':')) != NULL)
-               *dest++ = '\0';
-       tnl.n_name = cp;
-       hosts = expand(&tnl, E_ALL);
-       if (nerrs)
-               exit(1);
-
-       if (dest == NULL || *dest == '\0')
-               cmds = NULL;
-       else {
-               cmds = makesubcmd(INSTALL);
-               cmds->sc_options = options;
-               cmds->sc_name = dest;
-       }
-
-       if (debug) {
-               printf("docmdargs()\nfiles = ");
-               prnames(files);
-               printf("hosts = ");
-               prnames(hosts);
-       }
-       insert(NULL, files, hosts, cmds);
-       docmds(NULL, 0, NULL);
-}
-
-/*
- * Print a list of NAME blocks (mostly for debugging).
- */
-void
-prnames(nl)
-       register struct namelist *nl;
-{
-       printf("( ");
-       while (nl != NULL) {
-               printf("%s ", nl->n_name);
-               nl = nl->n_next;
-       }
-       printf(")\n");
-}
-
-#if __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
-
-void
-#if __STDC__
-warn(const char *fmt, ...)
-#else
-warn(fmt, va_alist)
-       char *fmt;
-        va_dcl
-#endif
-{
-       extern int yylineno;
-       va_list ap;
-#if __STDC__
-       va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
-       (void)fprintf(stderr, "rdist: line %d: Warning: ", yylineno);
-       (void)vfprintf(stderr, fmt, ap);
-       (void)fprintf(stderr, "\n");
-       va_end(ap);
-}
diff --git a/usr.bin/rdist/message.c b/usr.bin/rdist/message.c
new file mode 100644 (file)
index 0000000..8724bc5
--- /dev/null
@@ -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 (file)
index 0000000..8abf35f
--- /dev/null
@@ -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"                  /**/
index 77a0378..5aeefd1 100644 (file)
@@ -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
  * 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.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 */
index 1de33db..e5d3e42 100644 (file)
@@ -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
 .\" 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.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 (file)
index 0000000..c07386e
--- /dev/null
@@ -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 (file)
index 0000000..3383edc
--- /dev/null
@@ -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/server.c b/usr.bin/rdist/server.c
deleted file mode 100644 (file)
index f112164..0000000
+++ /dev/null
@@ -1,1589 +0,0 @@
-/*
- * Copyright (c) 1983, 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-/* from: static char sccsid[] = "@(#)server.c  8.1 (Berkeley) 6/9/93"; */
-static char *rcsid = "$Id: server.c,v 1.2 1995/12/15 08:58:45 deraadt Exp $";
-#endif /* not lint */
-
-#include <sys/wait.h>
-#include "defs.h"
-
-#define        ack()   (void) write(rem, "\0\n", 2)
-#define        err()   (void) write(rem, "\1\n", 2)
-
-struct linkbuf *ihead;         /* list of files with more than one link */
-char   buf[BUFSIZ];            /* general purpose buffer */
-char   target[BUFSIZ];         /* target/source directory name */
-char   *tp;                    /* pointer to end of target name */
-char   *Tdest;                 /* pointer to last T dest*/
-int    catname;                /* cat name to target name */
-char   *stp[32];               /* stack of saved tp's for directories */
-int    oumask;                 /* old umask for creating files */
-
-extern FILE *lfp;              /* log file for mailing changes */
-
-static int     chkparent __P((char *));
-static void    clean __P((char *));
-static void    comment __P((char *));
-static void    dospecial __P((char *));
-static int     fchog __P((int, char *, char *, char *, int));
-static void    hardlink __P((char *));
-static void    note __P((const char *, ...));
-static void    query __P((char *));
-static void    recvf __P((char *, int));
-static void    removeit __P((struct stat *));
-static int     response __P((void));
-static void    rmchk __P((int));
-static struct linkbuf *
-                   savelink __P((struct stat *));
-static void    sendf __P((char *, int));
-static int     update __P((char *, int, struct stat *));
-
-/*
- * Server routine to read requests and process them.
- * Commands are:
- *     Tname   - Transmit file if out of date
- *     Vname   - Verify if file out of date or not
- *     Qname   - Query if file exists. Return mtime & size if it does.
- */
-void
-server()
-{
-       char cmdbuf[BUFSIZ];
-       register char *cp;
-
-       signal(SIGHUP, cleanup);
-       signal(SIGINT, cleanup);
-       signal(SIGQUIT, cleanup);
-       signal(SIGTERM, cleanup);
-       signal(SIGPIPE, cleanup);
-
-       rem = 0;
-       oumask = umask(0);
-       (void) sprintf(buf, "V%d\n", VERSION);
-       (void) write(rem, buf, strlen(buf));
-
-       for (;;) {
-               cp = cmdbuf;
-               if (read(rem, cp, 1) <= 0)
-                       return;
-               if (*cp++ == '\n') {
-                       error("server: expected control record\n");
-                       continue;
-               }
-               do {
-                       if (read(rem, cp, 1) != 1)
-                               cleanup(0);
-               } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
-               *--cp = '\0';
-               cp = cmdbuf;
-               switch (*cp++) {
-               case 'T':  /* init target file/directory name */
-                       catname = 1;    /* target should be directory */
-                       goto dotarget;
-
-               case 't':  /* init target file/directory name */
-                       catname = 0;
-               dotarget:
-                       if (exptilde(target, cp) == NULL)
-                               continue;
-                       tp = target;
-                       while (*tp)
-                               tp++;
-                       ack();
-                       continue;
-
-               case 'R':  /* Transfer a regular file. */
-                       recvf(cp, S_IFREG);
-                       continue;
-
-               case 'D':  /* Transfer a directory. */
-                       recvf(cp, S_IFDIR);
-                       continue;
-
-               case 'K':  /* Transfer symbolic link. */
-                       recvf(cp, S_IFLNK);
-                       continue;
-
-               case 'k':  /* Transfer hard link. */
-                       hardlink(cp);
-                       continue;
-
-               case 'E':  /* End. (of directory) */
-                       *tp = '\0';
-                       if (catname <= 0) {
-                               error("server: too many 'E's\n");
-                               continue;
-                       }
-                       tp = stp[--catname];
-                       *tp = '\0';
-                       ack();
-                       continue;
-
-               case 'C':  /* Clean. Cleanup a directory */
-                       clean(cp);
-                       continue;
-
-               case 'Q':  /* Query. Does the file/directory exist? */
-                       query(cp);
-                       continue;
-
-               case 'S':  /* Special. Execute commands */
-                       dospecial(cp);
-                       continue;
-
-#ifdef notdef
-               /*
-                * These entries are reserved but not currently used.
-                * The intent is to allow remote hosts to have master copies.
-                * Currently, only the host rdist runs on can have masters.
-                */
-               case 'X':  /* start a new list of files to exclude */
-                       except = bp = NULL;
-               case 'x':  /* add name to list of files to exclude */
-                       if (*cp == '\0') {
-                               ack();
-                               continue;
-                       }
-                       if (*cp == '~') {
-                               if (exptilde(buf, cp) == NULL)
-                                       continue;
-                               cp = buf;
-                       }
-                       if (bp == NULL)
-                               except = bp = expand(makeblock(NAME, cp), E_VARS);
-                       else
-                               bp->b_next = expand(makeblock(NAME, cp), E_VARS);
-                       while (bp->b_next != NULL)
-                               bp = bp->b_next;
-                       ack();
-                       continue;
-
-               case 'I':  /* Install. Transfer file if out of date. */
-                       opts = 0;
-                       while (*cp >= '0' && *cp <= '7')
-                               opts = (opts << 3) | (*cp++ - '0');
-                       if (*cp++ != ' ') {
-                               error("server: options not delimited\n");
-                               return;
-                       }
-                       install(cp, opts);
-                       continue;
-
-               case 'L':  /* Log. save message in log file */
-                       log(lfp, cp);
-                       continue;
-#endif
-
-               case '\1':
-                       nerrs++;
-                       continue;
-
-               case '\2':
-                       return;
-
-               default:
-                       error("server: unknown command '%s'\n", cp);
-               case '\0':
-                       continue;
-               }
-       }
-}
-
-/*
- * Update the file(s) if they are different.
- * destdir = 1 if destination should be a directory
- * (i.e., more than one source is being copied to the same destination).
- */
-void
-install(src, dest, destdir, opts)
-       char *src, *dest;
-       int destdir, opts;
-{
-       char *rname;
-       char destcopy[BUFSIZ];
-
-       if (dest == NULL) {
-               opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
-               dest = src;
-       }
-
-       if (nflag || debug) {
-               printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
-                       opts & WHOLE ? " -w" : "",
-                       opts & YOUNGER ? " -y" : "",
-                       opts & COMPARE ? " -b" : "",
-                       opts & REMOVE ? " -R" : "", src, dest);
-               if (nflag)
-                       return;
-       }
-
-       rname = exptilde(target, src);
-       if (rname == NULL)
-               return;
-       tp = target;
-       while (*tp)
-               tp++;
-       /*
-        * If we are renaming a directory and we want to preserve
-        * the directory heirarchy (-w), we must strip off the leading
-        * directory name and preserve the rest.
-        */
-       if (opts & WHOLE) {
-               while (*rname == '/')
-                       rname++;
-               destdir = 1;
-       } else {
-               rname = rindex(target, '/');
-               if (rname == NULL)
-                       rname = target;
-               else
-                       rname++;
-       }
-       if (debug)
-               printf("target = %s, rname = %s\n", target, rname);
-       /*
-        * Pass the destination file/directory name to remote.
-        */
-       (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
-       if (debug)
-               printf("buf = %s", buf);
-       (void) write(rem, buf, strlen(buf));
-       if (response() < 0)
-               return;
-
-       if (destdir) {
-               strcpy(destcopy, dest);
-               Tdest = destcopy;
-       }
-       sendf(rname, opts);
-       Tdest = 0;
-}
-
-#define protoname() (pw ? pw->pw_name : user)
-#define protogroup() (gr ? gr->gr_name : group)
-/*
- * Transfer the file or directory in target[].
- * rname is the name of the file on the remote host.
- */
-static void
-sendf(rname, opts)
-       char *rname;
-       int opts;
-{
-       register struct subcmd *sc;
-       struct stat stb;
-       int sizerr, f, u, len;
-       off_t i;
-       DIR *d;
-       struct direct *dp;
-       char *otp, *cp;
-       extern struct subcmd *subcmds;
-       static char user[15], group[15];
-
-       if (debug)
-               printf("sendf(%s, %x)\n", rname, opts);
-
-       if (except(target))
-               return;
-       if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
-               error("%s: %s\n", target, strerror(errno));
-               return;
-       }
-       if ((u = update(rname, opts, &stb)) == 0) {
-               if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
-                       (void) savelink(&stb);
-               return;
-       }
-
-       if (pw == NULL || pw->pw_uid != stb.st_uid)
-               if ((pw = getpwuid(stb.st_uid)) == NULL) {
-                       log(lfp, "%s: no password entry for uid %d \n",
-                               target, stb.st_uid);
-                       pw = NULL;
-                       (void)sprintf(user, ":%lu", stb.st_uid);
-               }
-       if (gr == NULL || gr->gr_gid != stb.st_gid)
-               if ((gr = getgrgid(stb.st_gid)) == NULL) {
-                       log(lfp, "%s: no name for group %d\n",
-                               target, stb.st_gid);
-                       gr = NULL;
-                       (void)sprintf(group, ":%lu", stb.st_gid);
-               }
-       if (u == 1) {
-               if (opts & VERIFY) {
-                       log(lfp, "need to install: %s\n", target);
-                       goto dospecial;
-               }
-               log(lfp, "installing: %s\n", target);
-               opts &= ~(COMPARE|REMOVE);
-       }
-
-       switch (stb.st_mode & S_IFMT) {
-       case S_IFDIR:
-               if ((d = opendir(target)) == NULL) {
-                       error("%s: %s\n", target, strerror(errno));
-                       return;
-               }
-               (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
-                       stb.st_mode & 07777, protoname(), protogroup(), rname);
-               if (debug)
-                       printf("buf = %s", buf);
-               (void) write(rem, buf, strlen(buf));
-               if (response() < 0) {
-                       closedir(d);
-                       return;
-               }
-
-               if (opts & REMOVE)
-                       rmchk(opts);
-
-               otp = tp;
-               len = tp - target;
-               while (dp = readdir(d)) {
-                       if (!strcmp(dp->d_name, ".") ||
-                           !strcmp(dp->d_name, ".."))
-                               continue;
-                       if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
-                               error("%s/%s: Name too long\n", target,
-                                       dp->d_name);
-                               continue;
-                       }
-                       tp = otp;
-                       *tp++ = '/';
-                       cp = dp->d_name;
-                       while (*tp++ = *cp++)
-                               ;
-                       tp--;
-                       sendf(dp->d_name, opts);
-               }
-               closedir(d);
-               (void) write(rem, "E\n", 2);
-               (void) response();
-               tp = otp;
-               *tp = '\0';
-               return;
-
-       case S_IFLNK:
-               if (u != 1)
-                       opts |= COMPARE;
-               if (stb.st_nlink > 1) {
-                       struct linkbuf *lp;
-
-                       if ((lp = savelink(&stb)) != NULL) {
-                               /* install link */
-                               if (*lp->target == 0)
-                               (void) sprintf(buf, "k%o %s %s\n", opts,
-                                       lp->pathname, rname);
-                               else
-                               (void) sprintf(buf, "k%o %s/%s %s\n", opts,
-                                       lp->target, lp->pathname, rname);
-                               if (debug)
-                                       printf("buf = %s", buf);
-                               (void) write(rem, buf, strlen(buf));
-                               (void) response();
-                               return;
-                       }
-               }
-               (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts,
-                       stb.st_mode & 07777, stb.st_size, stb.st_mtime,
-                       protoname(), protogroup(), rname);
-               if (debug)
-                       printf("buf = %s", buf);
-               (void) write(rem, buf, strlen(buf));
-               if (response() < 0)
-                       return;
-               sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
-               (void) write(rem, buf, stb.st_size);
-               if (debug)
-                       printf("readlink = %.*s\n", (int)stb.st_size, buf);
-               goto done;
-
-       case S_IFREG:
-               break;
-
-       default:
-               error("%s: not a file or directory\n", target);
-               return;
-       }
-
-       if (u == 2) {
-               if (opts & VERIFY) {
-                       log(lfp, "need to update: %s\n", target);
-                       goto dospecial;
-               }
-               log(lfp, "updating: %s\n", target);
-       }
-
-       if (stb.st_nlink > 1) {
-               struct linkbuf *lp;
-
-               if ((lp = savelink(&stb)) != NULL) {
-                       /* install link */
-                       if (*lp->target == 0)
-                       (void) sprintf(buf, "k%o %s %s\n", opts,
-                               lp->pathname, rname);
-                       else
-                       (void) sprintf(buf, "k%o %s/%s %s\n", opts,
-                               lp->target, lp->pathname, rname);
-                       if (debug)
-                               printf("buf = %s", buf);
-                       (void) write(rem, buf, strlen(buf));
-                       (void) response();
-                       return;
-               }
-       }
-
-       if ((f = open(target, O_RDONLY, 0)) < 0) {
-               error("%s: %s\n", target, strerror(errno));
-               return;
-       }
-       (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts,
-               stb.st_mode & 07777, stb.st_size, stb.st_mtime,
-               protoname(), protogroup(), rname);
-       if (debug)
-               printf("buf = %s", buf);
-       (void) write(rem, buf, strlen(buf));
-       if (response() < 0) {
-               (void) close(f);
-               return;
-       }
-       sizerr = 0;
-       for (i = 0; i < stb.st_size; i += BUFSIZ) {
-               int amt = BUFSIZ;
-               if (i + amt > stb.st_size)
-                       amt = stb.st_size - i;
-               if (sizerr == 0 && read(f, buf, amt) != amt)
-                       sizerr = 1;
-               (void) write(rem, buf, amt);
-       }
-       (void) close(f);
-done:
-       if (sizerr) {
-               error("%s: file changed size\n", target);
-               err();
-       } else
-               ack();
-       f = response();
-       if (f < 0 || f == 0 && (opts & COMPARE))
-               return;
-dospecial:
-       for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
-               if (sc->sc_type != SPECIAL)
-                       continue;
-               if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
-                       continue;
-               log(lfp, "special \"%s\"\n", sc->sc_name);
-               if (opts & VERIFY)
-                       continue;
-               (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
-               if (debug)
-                       printf("buf = %s", buf);
-               (void) write(rem, buf, strlen(buf));
-               while (response() > 0)
-                       ;
-       }
-}
-
-static struct linkbuf *
-savelink(stp)
-       struct stat *stp;
-{
-       struct linkbuf *lp;
-
-       for (lp = ihead; lp != NULL; lp = lp->nextp)
-               if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
-                       lp->count--;
-                       return(lp);
-               }
-       lp = (struct linkbuf *) malloc(sizeof(*lp));
-       if (lp == NULL)
-               log(lfp, "out of memory, link information lost\n");
-       else {
-               lp->nextp = ihead;
-               ihead = lp;
-               lp->inum = stp->st_ino;
-               lp->devnum = stp->st_dev;
-               lp->count = stp->st_nlink - 1;
-               strcpy(lp->pathname, target);
-               if (Tdest)
-                       strcpy(lp->target, Tdest);
-               else
-                       *lp->target = 0;
-       }
-       return(NULL);
-}
-
-/*
- * Check to see if file needs to be updated on the remote machine.
- * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
- * and 3 if comparing binaries to determine if out of date.
- */
-static int
-update(rname, opts, stp)
-       char *rname;
-       int opts;
-       struct stat *stp;
-{
-       register char *cp, *s;
-       register off_t size;
-       register time_t mtime;
-
-       if (debug) 
-               printf("update(%s, %x, %x)\n", rname, opts, stp);
-
-       /*
-        * Check to see if the file exists on the remote machine.
-        */
-       (void) sprintf(buf, "Q%s\n", rname);
-       if (debug)
-               printf("buf = %s", buf);
-       (void) write(rem, buf, strlen(buf));
-again:
-       cp = s = buf;
-       do {
-               if (read(rem, cp, 1) != 1)
-                       lostconn(0);
-       } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
-
-       switch (*s++) {
-       case 'Y':
-               break;
-
-       case 'N':  /* file doesn't exist so install it */
-               return(1);
-
-       case '\1':
-               nerrs++;
-               if (*s != '\n') {
-                       if (!iamremote) {
-                               fflush(stdout);
-                               (void) write(2, s, cp - s);
-                       }
-                       if (lfp != NULL)
-                               (void) fwrite(s, 1, cp - s, lfp);
-               }
-               return(0);
-
-       case '\3':
-               *--cp = '\0';
-               if (lfp != NULL) 
-                       log(lfp, "update: note: %s\n", s);
-               goto again;
-
-       default:
-               *--cp = '\0';
-               error("update: unexpected response '%s'\n", s);
-               return(0);
-       }
-
-       if (*s == '\n')
-               return(2);
-
-       if (opts & COMPARE)
-               return(3);
-
-       size = 0;
-       while (isdigit(*s))
-               size = size * 10 + (*s++ - '0');
-       if (*s++ != ' ') {
-               error("update: size not delimited\n");
-               return(0);
-       }
-       mtime = 0;
-       while (isdigit(*s))
-               mtime = mtime * 10 + (*s++ - '0');
-       if (*s != '\n') {
-               error("update: mtime not delimited\n");
-               return(0);
-       }
-       /*
-        * File needs to be updated?
-        */
-       if (opts & YOUNGER) {
-               if (stp->st_mtime == mtime)
-                       return(0);
-               if (stp->st_mtime < mtime) {
-                       log(lfp, "Warning: %s: remote copy is newer\n", target);
-                       return(0);
-               }
-       } else if (stp->st_mtime == mtime && stp->st_size == size)
-               return(0);
-       return(2);
-}
-
-/*
- * Query. Check to see if file exists. Return one of the following:
- *     N\n             - doesn't exist
- *     Ysize mtime\n   - exists and its a regular file (size & mtime of file)
- *     Y\n             - exists and its a directory or symbolic link
- *     ^Aerror message\n
- */
-static void
-query(name)
-       char *name;
-{
-       struct stat stb;
-
-       if (catname)
-               (void) sprintf(tp, "/%s", name);
-
-       if (lstat(target, &stb) < 0) {
-               if (errno == ENOENT)
-                       (void) write(rem, "N\n", 2);
-               else
-                       error("%s:%s: %s\n", host, target, strerror(errno));
-               *tp = '\0';
-               return;
-       }
-
-       switch (stb.st_mode & S_IFMT) {
-       case S_IFREG:
-               (void) sprintf(buf, "Y%qd %ld\n", stb.st_size,
-                   stb.st_mtime);
-               (void) write(rem, buf, strlen(buf));
-               break;
-
-       case S_IFLNK:
-       case S_IFDIR:
-               (void) write(rem, "Y\n", 2);
-               break;
-
-       default:
-               error("%s: not a file or directory\n", name);
-               break;
-       }
-       *tp = '\0';
-}
-
-static void
-recvf(cmd, type)
-       char *cmd;
-       int type;
-{
-       register char *cp;
-       int f, mode, opts, wrerr, olderrno;
-       off_t i, size;
-       time_t mtime;
-       struct stat stb;
-       struct timeval tvp[2];
-       char *owner, *group;
-       char new[BUFSIZ];
-       extern char *tempname;
-
-       cp = cmd;
-       opts = 0;
-       f = 0;          /*  Initialize, so for links it remains 0.  */
-       while (*cp >= '0' && *cp <= '7')
-               opts = (opts << 3) | (*cp++ - '0');
-       if (*cp++ != ' ') {
-               error("recvf: options not delimited\n");
-               return;
-       }
-       mode = 0;
-       while (*cp >= '0' && *cp <= '7')
-               mode = (mode << 3) | (*cp++ - '0');
-       if (*cp++ != ' ') {
-               error("recvf: mode not delimited\n");
-               return;
-       }
-       size = 0;
-       while (isdigit(*cp))
-               size = size * 10 + (*cp++ - '0');
-       if (*cp++ != ' ') {
-               error("recvf: size not delimited\n");
-               return;
-       }
-       mtime = 0;
-       while (isdigit(*cp))
-               mtime = mtime * 10 + (*cp++ - '0');
-       if (*cp++ != ' ') {
-               error("recvf: mtime not delimited\n");
-               return;
-       }
-       owner = cp;
-       while (*cp && *cp != ' ')
-               cp++;
-       if (*cp != ' ') {
-               error("recvf: owner name not delimited\n");
-               return;
-       }
-       *cp++ = '\0';
-       group = cp;
-       while (*cp && *cp != ' ')
-               cp++;
-       if (*cp != ' ') {
-               error("recvf: group name not delimited\n");
-               return;
-       }
-       *cp++ = '\0';
-
-       if (type == S_IFDIR) {
-               if (catname >= sizeof(stp)) {
-                       error("%s:%s: too many directory levels\n",
-                               host, target);
-                       return;
-               }
-               stp[catname] = tp;
-               if (catname++) {
-                       *tp++ = '/';
-                       while (*tp++ = *cp++)
-                               ;
-                       tp--;
-               }
-               if (opts & VERIFY) {
-                       ack();
-                       return;
-               }
-               if (lstat(target, &stb) == 0) {
-                       if (ISDIR(stb.st_mode)) {
-                               if ((stb.st_mode & 07777) == mode) {
-                                       ack();
-                                       return;
-                               }
-                               buf[0] = '\0';
-                               (void) sprintf(buf + 1,
-                                       "%s: Warning: remote mode %o != local mode %o\n",
-                                       target, stb.st_mode & 07777, mode);
-                               (void) write(rem, buf, strlen(buf + 1) + 1);
-                               return;
-                       }
-                       errno = ENOTDIR;
-               } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
-                   chkparent(target) == 0 && mkdir(target, mode) == 0)) {
-                       if (fchog(-1, target, owner, group, mode) == 0)
-                               ack();
-                       return;
-               }
-               error("%s:%s: %s\n", host, target, strerror(errno));
-               tp = stp[--catname];
-               *tp = '\0';
-               return;
-       }
-
-       if (catname)
-               (void) sprintf(tp, "/%s", cp);
-       cp = rindex(target, '/');
-       if (cp == NULL)
-               strcpy(new, tempname);
-       else if (cp == target)
-               (void) sprintf(new, "/%s", tempname);
-       else {
-               *cp = '\0';
-               (void) sprintf(new, "%s/%s", target, tempname);
-               *cp = '/';
-       }
-
-       if (type == S_IFLNK) {
-               int j;
-
-               ack();
-               cp = buf;
-               for (i = 0; i < size; i += j) {
-                       if ((j = read(rem, cp, size - i)) <= 0)
-                               cleanup(0);
-                       cp += j;
-               }
-               *cp = '\0';
-               if (response() < 0) {
-                       err();
-                       return;
-               }
-               if (symlink(buf, new) < 0) {
-                       if (errno != ENOENT || chkparent(new) < 0 ||
-                           symlink(buf, new) < 0)
-                               goto badnew1;
-               }
-               mode &= 0777;
-               if (opts & COMPARE) {
-                       char tbuf[BUFSIZ];
-
-                       if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
-                           i == size && strncmp(buf, tbuf, size) == 0) {
-                               (void) unlink(new);
-                               ack();
-                               return;
-                       }
-                       if (opts & VERIFY)
-                               goto differ;
-               }
-               goto fixup;
-       }
-
-       if ((f = creat(new, mode)) < 0) {
-               if (errno != ENOENT || chkparent(new) < 0 ||
-                   (f = creat(new, mode)) < 0)
-                       goto badnew1;
-       }
-
-       ack();
-       wrerr = 0;
-       for (i = 0; i < size; i += BUFSIZ) {
-               int amt = BUFSIZ;
-
-               cp = buf;
-               if (i + amt > size)
-                       amt = size - i;
-               do {
-                       int j = read(rem, cp, amt);
-
-                       if (j <= 0) {
-                               (void) close(f);
-                               (void) unlink(new);
-                               cleanup(0);
-                       }
-                       amt -= j;
-                       cp += j;
-               } while (amt > 0);
-               amt = BUFSIZ;
-               if (i + amt > size)
-                       amt = size - i;
-               if (wrerr == 0 && write(f, buf, amt) != amt) {
-                       olderrno = errno;
-                       wrerr++;
-               }
-       }
-       if (response() < 0) {
-               err();
-               goto badnew2;
-       }
-       if (wrerr)
-               goto badnew1;
-       if (opts & COMPARE) {
-               FILE *f1, *f2;
-               int c;
-
-               if ((f1 = fopen(target, "r")) == NULL)
-                       goto badtarget;
-               if ((f2 = fopen(new, "r")) == NULL) {
-badnew1:               error("%s:%s: %s\n", host, new, strerror(errno));
-                       goto badnew2;
-               }
-               while ((c = getc(f1)) == getc(f2))
-                       if (c == EOF) {
-                               (void) fclose(f1);
-                               (void) fclose(f2);
-                               ack();
-                               goto badnew2;
-                       }
-               (void) fclose(f1);
-               (void) fclose(f2);
-               if (opts & VERIFY) {
-differ:                        buf[0] = '\0';
-                       (void) sprintf(buf + 1, "need to update: %s\n",target);
-                       (void) write(rem, buf, strlen(buf + 1) + 1);
-                       goto badnew2;
-               }
-       }
-
-       /*
-        * Set last modified time
-        */
-       tvp[0].tv_sec = time(0);
-       tvp[0].tv_usec = 0;
-       tvp[1].tv_sec = mtime;
-       tvp[1].tv_usec = 0;
-       if (utimes(new, tvp) < 0)
-               note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
-
-       if (fchog(f, new, owner, group, mode) < 0) {
-badnew2:       
-               if (f)          /*  Don't close if f hasn't been opened.  */
-                       (void) close(f);
-               (void) unlink(new);
-               return;
-       }
-       (void) close(f);
-
-fixup: if (rename(new, target) < 0) {
-badtarget:     error("%s:%s: %s\n", host, target, strerror(errno));
-               (void) unlink(new);
-               return;
-       }
-
-       if (opts & COMPARE) {
-               buf[0] = '\0';
-               (void) sprintf(buf + 1, "updated %s\n", target);
-               (void) write(rem, buf, strlen(buf + 1) + 1);
-       } else
-               ack();
-}
-
-/*
- * Creat a hard link to existing file.
- */
-static void
-hardlink(cmd)
-       char *cmd;
-{
-       register char *cp;
-       struct stat stb;
-       char *oldname;
-       int opts, exists = 0;
-
-       cp = cmd;
-       opts = 0;
-       while (*cp >= '0' && *cp <= '7')
-               opts = (opts << 3) | (*cp++ - '0');
-       if (*cp++ != ' ') {
-               error("hardlink: options not delimited\n");
-               return;
-       }
-       oldname = cp;
-       while (*cp && *cp != ' ')
-               cp++;
-       if (*cp != ' ') {
-               error("hardlink: oldname name not delimited\n");
-               return;
-       }
-       *cp++ = '\0';
-
-       if (catname) {
-               (void) sprintf(tp, "/%s", cp);
-       }
-       if (lstat(target, &stb) == 0) {
-               int mode = stb.st_mode & S_IFMT;
-               if (mode != S_IFREG && mode != S_IFLNK) {
-                       error("%s:%s: not a regular file\n", host, target);
-                       return;
-               }
-               exists = 1;
-       }
-       if (chkparent(target) < 0 ) {
-               error("%s:%s: %s (no parent)\n",
-                       host, target, strerror(errno));
-               return;
-       }
-       if (exists && (unlink(target) < 0)) {
-               error("%s:%s: %s (unlink)\n",
-                       host, target, strerror(errno));
-               return;
-       }
-       if (link(oldname, target) < 0) {
-               error("%s:can't link %s to %s\n",
-                       host, target, oldname);
-               return;
-       }
-       ack();
-}
-
-/*
- * Check to see if parent directory exists and create one if not.
- */
-static int
-chkparent(name)
-       char *name;
-{
-       register char *cp;
-       struct stat stb;
-
-       cp = rindex(name, '/');
-       if (cp == NULL || cp == name)
-               return(0);
-       *cp = '\0';
-       if (lstat(name, &stb) < 0) {
-               if (errno == ENOENT && chkparent(name) >= 0 &&
-                   mkdir(name, 0777 & ~oumask) >= 0) {
-                       *cp = '/';
-                       return(0);
-               }
-       } else if (ISDIR(stb.st_mode)) {
-               *cp = '/';
-               return(0);
-       }
-       *cp = '/';
-       return(-1);
-}
-
-/*
- * Change owner, group and mode of file.
- */
-static int
-fchog(fd, file, owner, group, mode)
-       int fd;
-       char *file, *owner, *group;
-       int mode;
-{
-       register int i;
-       int uid, gid;
-       extern char user[];
-       extern int userid;
-
-       uid = userid;
-       if (userid == 0) {
-               if (*owner == ':') {
-                       uid = atoi(owner + 1);
-               } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
-                       if ((pw = getpwnam(owner)) == NULL) {
-                               if (mode & 04000) {
-                                       note("%s:%s: unknown login name, clearing setuid",
-                                               host, owner);
-                                       mode &= ~04000;
-                                       uid = 0;
-                               }
-                       } else
-                               uid = pw->pw_uid;
-               } else
-                       uid = pw->pw_uid;
-               if (*group == ':') {
-                       gid = atoi(group + 1);
-                       goto ok;
-               }
-       } else if ((mode & 04000) && strcmp(user, owner) != 0)
-               mode &= ~04000;
-       gid = -1;
-       if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
-               if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
-                  || ((gr = getgrnam(group)) == NULL)) {
-                       if (mode & 02000) {
-                               note("%s:%s: unknown group", host, group);
-                               mode &= ~02000;
-                       }
-               } else
-                       gid = gr->gr_gid;
-       } else
-               gid = gr->gr_gid;
-       if (userid && gid >= 0) {
-               if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
-                       if (!(strcmp(user, gr->gr_mem[i])))
-                               goto ok;
-               mode &= ~02000;
-               gid = -1;
-       }
-ok:    if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
-               note("%s: %s chown: %s", host, file, strerror(errno));
-       else if (mode & 07000 &&
-          (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
-               note("%s: %s chmod: %s", host, file, strerror(errno));
-       return(0);
-}
-
-/*
- * Check for files on the machine being updated that are not on the master
- * machine and remove them.
- */
-static void
-rmchk(opts)
-       int opts;
-{
-       register char *cp, *s;
-       struct stat stb;
-
-       if (debug)
-               printf("rmchk()\n");
-
-       /*
-        * Tell the remote to clean the files from the last directory sent.
-        */
-       (void) sprintf(buf, "C%o\n", opts & VERIFY);
-       if (debug)
-               printf("buf = %s", buf);
-       (void) write(rem, buf, strlen(buf));
-       if (response() < 0)
-               return;
-       for (;;) {
-               cp = s = buf;
-               do {
-                       if (read(rem, cp, 1) != 1)
-                               lostconn(0);
-               } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
-
-               switch (*s++) {
-               case 'Q': /* Query if file should be removed */
-                       /*
-                        * Return the following codes to remove query.
-                        * N\n -- file exists - DON'T remove.
-                        * Y\n -- file doesn't exist - REMOVE.
-                        */
-                       *--cp = '\0';
-                       (void) sprintf(tp, "/%s", s);
-                       if (debug)
-                               printf("check %s\n", target);
-                       if (except(target))
-                               (void) write(rem, "N\n", 2);
-                       else if (lstat(target, &stb) < 0)
-                               (void) write(rem, "Y\n", 2);
-                       else
-                               (void) write(rem, "N\n", 2);
-                       break;
-
-               case '\0':
-                       *--cp = '\0';
-                       if (*s != '\0')
-                               log(lfp, "%s\n", s);
-                       break;
-
-               case 'E':
-                       *tp = '\0';
-                       ack();
-                       return;
-
-               case '\1':
-               case '\2':
-                       nerrs++;
-                       if (*s != '\n') {
-                               if (!iamremote) {
-                                       fflush(stdout);
-                                       (void) write(2, s, cp - s);
-                               }
-                               if (lfp != NULL)
-                                       (void) fwrite(s, 1, cp - s, lfp);
-                       }
-                       if (buf[0] == '\2')
-                               lostconn(0);
-                       break;
-
-               default:
-                       error("rmchk: unexpected response '%s'\n", buf);
-                       err();
-               }
-       }
-}
-
-/*
- * Check the current directory (initialized by the 'T' command to server())
- * for extraneous files and remove them.
- */
-static void
-clean(cp)
-       register char *cp;
-{
-       DIR *d;
-       register struct direct *dp;
-       struct stat stb;
-       char *otp;
-       int len, opts;
-
-       opts = 0;
-       while (*cp >= '0' && *cp <= '7')
-               opts = (opts << 3) | (*cp++ - '0');
-       if (*cp != '\0') {
-               error("clean: options not delimited\n");
-               return;
-       }
-       if ((d = opendir(target)) == NULL) {
-               error("%s:%s: %s\n", host, target, strerror(errno));
-               return;
-       }
-       ack();
-
-       otp = tp;
-       len = tp - target;
-       while (dp = readdir(d)) {
-               if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
-                       continue;
-               if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
-                       error("%s:%s/%s: Name too long\n",
-                               host, target, dp->d_name);
-                       continue;
-               }
-               tp = otp;
-               *tp++ = '/';
-               cp = dp->d_name;;
-               while (*tp++ = *cp++)
-                       ;
-               tp--;
-               if (lstat(target, &stb) < 0) {
-                       error("%s:%s: %s\n", host, target, strerror(errno));
-                       continue;
-               }
-               (void) sprintf(buf, "Q%s\n", dp->d_name);
-               (void) write(rem, buf, strlen(buf));
-               cp = buf;
-               do {
-                       if (read(rem, cp, 1) != 1)
-                               cleanup(0);
-               } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
-               *--cp = '\0';
-               cp = buf;
-               if (*cp != 'Y')
-                       continue;
-               if (opts & VERIFY) {
-                       cp = buf;
-                       *cp++ = '\0';
-                       (void) sprintf(cp, "need to remove: %s\n", target);
-                       (void) write(rem, buf, strlen(cp) + 1);
-               } else
-                       removeit(&stb);
-       }
-       closedir(d);
-       (void) write(rem, "E\n", 2);
-       (void) response();
-       tp = otp;
-       *tp = '\0';
-}
-
-/*
- * Remove a file or directory (recursively) and send back an acknowledge
- * or an error message.
- */
-static void
-removeit(stp)
-       struct stat *stp;
-{
-       DIR *d;
-       struct direct *dp;
-       register char *cp;
-       struct stat stb;
-       char *otp;
-       int len;
-
-       switch (stp->st_mode & S_IFMT) {
-       case S_IFREG:
-       case S_IFLNK:
-               if (unlink(target) < 0)
-                       goto bad;
-               goto removed;
-
-       case S_IFDIR:
-               break;
-
-       default:
-               error("%s:%s: not a plain file\n", host, target);
-               return;
-       }
-
-       if ((d = opendir(target)) == NULL)
-               goto bad;
-
-       otp = tp;
-       len = tp - target;
-       while (dp = readdir(d)) {
-               if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
-                       continue;
-               if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
-                       error("%s:%s/%s: Name too long\n",
-                               host, target, dp->d_name);
-                       continue;
-               }
-               tp = otp;
-               *tp++ = '/';
-               cp = dp->d_name;;
-               while (*tp++ = *cp++)
-                       ;
-               tp--;
-               if (lstat(target, &stb) < 0) {
-                       error("%s:%s: %s\n", host, target, strerror(errno));
-                       continue;
-               }
-               removeit(&stb);
-       }
-       closedir(d);
-       tp = otp;
-       *tp = '\0';
-       if (rmdir(target) < 0) {
-bad:
-               error("%s:%s: %s\n", host, target, strerror(errno));
-               return;
-       }
-removed:
-       cp = buf;
-       *cp++ = '\0';
-       (void) sprintf(cp, "removed %s\n", target);
-       (void) write(rem, buf, strlen(cp) + 1);
-}
-
-/*
- * Execute a shell command to handle special cases.
- */
-static void
-dospecial(cmd)
-       char *cmd;
-{
-       int fd[2], status, pid, i;
-       register char *cp, *s;
-       char sbuf[BUFSIZ];
-       extern int userid, groupid;
-
-       if (pipe(fd) < 0) {
-               error("%s\n", strerror(errno));
-               return;
-       }
-       if ((pid = fork()) == 0) {
-               /*
-                * Return everything the shell commands print.
-                */
-               (void) close(0);
-               (void) close(1);
-               (void) close(2);
-               (void) open(_PATH_DEVNULL, O_RDONLY);
-               (void) dup(fd[1]);
-               (void) dup(fd[1]);
-               (void) close(fd[0]);
-               (void) close(fd[1]);
-               setgid(groupid);
-               setuid(userid);
-               execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
-               _exit(127);
-       }
-       (void) close(fd[1]);
-       s = sbuf;
-       *s++ = '\0';
-       while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
-               cp = buf;
-               do {
-                       *s++ = *cp++;
-                       if (cp[-1] != '\n') {
-                               if (s < &sbuf[sizeof(sbuf)-1])
-                                       continue;
-                               *s++ = '\n';
-                       }
-                       /*
-                        * Throw away blank lines.
-                        */
-                       if (s == &sbuf[2]) {
-                               s--;
-                               continue;
-                       }
-                       (void) write(rem, sbuf, s - sbuf);
-                       s = &sbuf[1];
-               } while (--i);
-       }
-       if (s > &sbuf[1]) {
-               *s++ = '\n';
-               (void) write(rem, sbuf, s - sbuf);
-       }
-       while ((i = wait(&status)) != pid && i != -1)
-               ;
-       if (i == -1)
-               status = -1;
-       (void) close(fd[0]);
-       if (status)
-               error("shell returned %d\n", status);
-       else
-               ack();
-}
-
-#if __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
-
-void
-#if __STDC__
-log(FILE *fp, const char *fmt, ...)
-#else
-log(fp, fmt, va_alist)
-       FILE *fp;
-       char *fmt;
-        va_dcl
-#endif
-{
-       va_list ap;
-#if __STDC__
-       va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
-       /* Print changes locally if not quiet mode */
-       if (!qflag)
-               (void)vprintf(fmt, ap);
-
-       /* Save changes (for mailing) if really updating files */
-       if (!(options & VERIFY) && fp != NULL)
-               (void)vfprintf(fp, fmt, ap);
-       va_end(ap);
-}
-
-void
-#if __STDC__
-error(const char *fmt, ...)
-#else
-error(fmt, va_alist)
-       char *fmt;
-        va_dcl
-#endif
-{
-       static FILE *fp;
-       va_list ap;
-#if __STDC__
-       va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
-
-       ++nerrs;
-       if (!fp && !(fp = fdopen(rem, "w")))
-               return;
-       if (iamremote) {
-               (void)fprintf(fp, "%crdist: ", 0x01);
-               (void)vfprintf(fp, fmt, ap);
-               fflush(fp);
-       }
-       else {
-               fflush(stdout);
-               (void)fprintf(stderr, "rdist: ");
-               (void)vfprintf(stderr, fmt, ap);
-               fflush(stderr);
-       }
-       if (lfp != NULL) {
-               (void)fprintf(lfp, "rdist: ");
-               (void)vfprintf(lfp, fmt, ap);
-               fflush(lfp);
-       }
-       va_end(ap);
-}
-
-void
-#if __STDC__
-fatal(const char *fmt, ...)
-#else
-fatal(fmt, va_alist)
-       char *fmt;
-        va_dcl
-#endif
-{
-       static FILE *fp;
-       va_list ap;
-#if __STDC__
-       va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
-
-       ++nerrs;
-       if (!fp && !(fp = fdopen(rem, "w")))
-               return;
-       if (iamremote) {
-               (void)fprintf(fp, "%crdist: ", 0x02);
-               (void)vfprintf(fp, fmt, ap);
-               fflush(fp);
-       }
-       else {
-               fflush(stdout);
-               (void)fprintf(stderr, "rdist: ");
-               (void)vfprintf(stderr, fmt, ap);
-               fflush(stderr);
-       }
-       if (lfp != NULL) {
-               (void)fprintf(lfp, "rdist: ");
-               (void)vfprintf(lfp, fmt, ap);
-               fflush(lfp);
-       }
-       cleanup(0);
-}
-
-static int
-response()
-{
-       char *cp, *s;
-       char resp[BUFSIZ];
-
-       if (debug)
-               printf("response()\n");
-
-       cp = s = resp;
-       do {
-               if (read(rem, cp, 1) != 1)
-                       lostconn(0);
-       } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
-
-       switch (*s++) {
-       case '\0':
-               *--cp = '\0';
-               if (*s != '\0') {
-                       log(lfp, "%s\n", s);
-                       return(1);
-               }
-               return(0);
-       case '\3':
-               *--cp = '\0';
-               log(lfp, "Note: %s\n",s);
-               return(response());
-
-       default:
-               s--;
-               /* fall into... */
-       case '\1':
-       case '\2':
-               nerrs++;
-               if (*s != '\n') {
-                       if (!iamremote) {
-                               fflush(stdout);
-                               (void) write(2, s, cp - s);
-                       }
-                       if (lfp != NULL)
-                               (void) fwrite(s, 1, cp - s, lfp);
-               }
-               if (resp[0] == '\2')
-                       lostconn(0);
-               return(-1);
-       }
-}
-
-/*
- * Remove temporary files and do any cleanup operations before exiting.
- */
-void
-cleanup(signo)
-       int signo;
-{
-       (void) unlink(tempfile);
-       exit(1);
-}
-
-static void
-#if __STDC__
-note(const char *fmt, ...)
-#else
-note(fmt, va_alist)
-       char *fmt;
-        va_dcl
-#endif
-{
-       static char buf[BUFSIZ];
-       va_list ap;
-#if __STDC__
-       va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
-       (void)vsnprintf(buf, sizeof(buf), fmt, ap);
-       va_end(ap);
-       comment(buf);
-}
-
-static void
-comment(s)
-       char *s;
-{
-       char c;
-
-       c = '\3';
-       write(rem, &c, 1);
-       write(rem, s, strlen(s));
-       c = '\n';
-       write(rem, &c, 1);
-}
diff --git a/usr.bin/rdist/setargs.c b/usr.bin/rdist/setargs.c
new file mode 100644 (file)
index 0000000..815ffe3
--- /dev/null
@@ -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 (file)
index 0000000..7138271
--- /dev/null
@@ -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 (file)
index 0000000..63483c0
--- /dev/null
@@ -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 (file)
index 0000000..7f30f9b
--- /dev/null
@@ -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 (file)
index 0000000..5f57de4
--- /dev/null
@@ -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 (file)
index 0000000..43226af
--- /dev/null
@@ -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 (file)
index 0000000..5f7586f
--- /dev/null
@@ -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 (file)
index 0000000..6416b36
--- /dev/null
@@ -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 (file)
index 0000000..4b753ad
--- /dev/null
@@ -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 (file)
index 0000000..10eca5e
--- /dev/null
@@ -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 (file)
index 0000000..db04524
--- /dev/null
@@ -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 (file)
index 0000000..7331068
--- /dev/null
@@ -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;
+               }
+       }
+}