[BACK]Return to docmd.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rdist

Diff for /src/usr.bin/rdist/docmd.c between version 1.1 and 1.2

version 1.1, 1995/10/18 08:45:58 version 1.2, 1996/02/03 12:12:22
Line 1 
Line 1 
 /*  /*
  * Copyright (c) 1983, 1993   * Copyright (c) 1983 Regents of the University of California.
  *      The Regents of the University of California.  All rights reserved.   * All rights reserved.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
Line 32 
Line 32 
  */   */
   
 #ifndef lint  #ifndef lint
 /* from: static char sccsid[] = "@(#)docmd.c    8.1 (Berkeley) 6/9/93"; */  static char RCSid[] =
 static char *rcsid = "$Id$";  "$Id$";
   
   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 */  #endif /* not lint */
   
   /*
    * Functions for rdist that do command (cmd) related activities.
    */
   
 #include "defs.h"  #include "defs.h"
 #include <setjmp.h>  #include "y.tab.h"
   #include <sys/socket.h>
 #include <netdb.h>  #include <netdb.h>
   
 FILE    *lfp;                   /* log file for recording files updated */  struct subcmd          *subcmds;                /* list of sub-commands for
 struct  subcmd *subcmds;        /* list of sub-commands for current cmd */                                                     current cmd */
 jmp_buf env;  struct namelist        *filelist;               /* list of source files */
   extern struct cmd      *cmds;                   /* Initialized by yyparse() */
   time_t                  lastmod;                /* Last modify time */
   
 static int       makeconn __P((char *));  extern char             target[];
 static int       okname __P((char *));  extern char            *ptarget;
 static void      closeconn __P((void));  extern int              activechildren;
 static void      cmptime __P((char *));  extern int              maxchildren;
 static void      doarrow __P((char **,  extern int              amchild;
                     struct namelist *, char *, struct subcmd *));  extern char            *path_rdistd;
 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 *));  
   
   static void cmptime();
   
 /*  /*
  * Do the commands in cmds (initialized by yyparse).   * Signal end of connection.
  */   */
 void  static void closeconn()
 docmds(dhosts, argc, argv)  
         char **dhosts;  
         int argc;  
         char **argv;  
 {  {
         register struct cmd *c;          debugmsg(DM_CALL, "closeconn() called\n");
         register struct namelist *f;  
         register char **cpp;  
         extern struct cmd *cmds;  
   
         signal(SIGHUP, cleanup);          if (rem_w >= 0) {
         signal(SIGINT, cleanup);                  /* We don't care if the connection is still good or not */
         signal(SIGQUIT, cleanup);                  signal(SIGPIPE, SIG_IGN);
         signal(SIGTERM, cleanup);  
   
         for (c = cmds; c != NULL; c = c->c_next) {                  (void) sendcmd(C_FERRMSG, NULL);
                 if (dhosts != NULL && *dhosts != NULL) {                  (void) close(rem_w);
                         for (cpp = dhosts; *cpp; cpp++)                  (void) close(rem_r); /* This can't hurt */
                                 if (strcmp(c->c_name, *cpp) == 0)                  rem_w = -1;
                                         goto fndhost;                  rem_r = -1;
                         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.   * 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  static void notify(rhost, to, lmod)
 doarrow(filev, files, rhost, cmds)  
         char **filev;  
         struct namelist *files;  
         char *rhost;          char *rhost;
         struct subcmd *cmds;          register struct namelist *to;
           time_t lmod;
 {  {
         register struct namelist *f;          register int fd, len;
         register struct subcmd *sc;          FILE *pf, *popen();
         register char **cpp;          struct stat stb;
         int n, ddir, opts = options;          static char buf[BUFSIZ];
           char *file;
   
         if (debug)          if (IS_ON(options, DO_VERIFY) || to == NULL)
                 printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);                  return;
   
         if (files == NULL) {          if ((file = getnotifyfile()) == NULL)
                 error("no files to be updated\n");  
                 return;                  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)          if (nflag)
                 printf("updating host %s\n", rhost);                  return;
         else {  
                 if (setjmp(env))          debugmsg(DM_MISC, "notify() temp file = '%s'", file);
                         goto done;  
                 signal(SIGPIPE, lostconn);          if ((fd = open(file, O_RDONLY)) < 0) {
                 if (!makeconn(rhost))                  error("%s: open for reading failed: %s", file, SYSERR);
                         return;                  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 (fstat(fd, &stb) < 0) {
                 if (filev) {                  error("%s: fstat failed: %s", file, SYSERR);
                         for (cpp = filev; *cpp; cpp++)                  (void) close(fd);
                                 if (strcmp(f->n_name, *cpp) == 0)                  return;
                                         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 (stb.st_size == 0) {
         if (!nflag) {                  (void) close(fd);
                 (void) signal(SIGPIPE, cleanup);                  return;
                 (void) fclose(lfp);  
                 lfp = NULL;  
         }          }
         for (sc = cmds; sc != NULL; sc = sc->sc_next)          /*
                 if (sc->sc_type == NOTIFY)           * Create a pipe to mailling program.
                         notify(tempfile, rhost, sc->sc_args, 0);           * Set IFS to avoid possible security problem with users
         if (!nflag) {           * setting "IFS=/".
                 (void) unlink(tempfile);           */
                 for (; ihead != NULL; ihead = ihead->nextp) {          (void) sprintf(buf, "IFS=\" \t\"; export IFS; %s -oi -t",
                         free(ihead);                         _PATH_SENDMAIL);
                         if ((opts & IGNLNKS) || ihead->count == 0)          pf = popen(buf, "w");
                                 continue;          if (pf == NULL) {
                         log(lfp, "%s: Warning: missing links\n",                  error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
                                 ihead->pathname);                  (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.   * Create a connection to the rdist server on the machine rhost.
    * Return 0 if the connection fails or 1 if it succeeds.
  */   */
 static int  static int makeconn(rhost)
 makeconn(rhost)  
         char *rhost;          char *rhost;
 {  {
         register char *ruser, *cp;          register char *ruser, *cp;
         static char *cur_host = NULL;          static char *cur_host = NULL;
         static int port = -1;          extern char *locuser;
         char tuser[20];          extern long min_freefiles, min_freespace;
           extern char *remotemsglist;
           char tuser[BUFSIZ], buf[BUFSIZ];
           u_char respbuff[BUFSIZ];
         int n;          int n;
         extern char user[];  
         extern int userid;  
   
         if (debug)          debugmsg(DM_CALL, "makeconn(%s)", rhost);
                 printf("makeconn(%s)\n", 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)                  if (strcmp(cur_host, rhost) == 0)
                         return(1);                          return(1);
                 closeconn();                  closeconn();
         }          }
   
           /*
            * Determine remote user and current host names
            */
         cur_host = rhost;          cur_host = rhost;
         cp = index(rhost, '@');          cp = strchr(rhost, '@');
   
         if (cp != NULL) {          if (cp != NULL) {
                 char c = *cp;                  char c = *cp;
   
                 *cp = '\0';                  *cp = CNULL;
                 strncpy(tuser, rhost, sizeof(tuser)-1);                  (void) strncpy((char *)tuser, rhost, sizeof(tuser)-1);
                 *cp = c;                  *cp = c;
                 rhost = cp + 1;                  rhost = cp + 1;
                 ruser = tuser;                  ruser = tuser;
                 if (*ruser == '\0')                  if (*ruser == CNULL)
                         ruser = user;                          ruser = locuser;
                 else if (!okname(ruser))                  else if (!okname(ruser))
                         return(0);                          return(0);
         } else          } else
                 ruser = user;                  ruser = locuser;
         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)          if (!IS_ON(options, DO_QUIET))
                         fatal("shell/tcp: unknown service");                  message(MT_VERBOSE, "updating host %s", rhost);
                 port = sp->s_port;  
         }  
   
         if (debug) {          (void) sprintf(buf, "%.*s -S", sizeof(buf)-5, path_rdistd);
                 printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);  
                 printf("buf = %s\n", buf);          if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0)
         }                  return(0);
   
         fflush(stdout);          /*
         seteuid(0);           * First thing received should be S_VERSION
         rem = rcmd(&rhost, port, user, ruser, buf, 0);           */
         seteuid(userid);          n = remline(respbuff, sizeof(respbuff), TRUE);
         if (rem < 0)          if (n <= 0 || respbuff[0] != S_VERSION) {
                   error("Unexpected input from server: \"%s\".", respbuff);
                   closeconn();
                 return(0);                  return(0);
         cp = buf;          }
         if (read(rem, cp, 1) != 1)  
                 lostconn(0);          /*
         if (*cp == 'V') {           * For future compatibility we check to see if the server
                 do {           * sent it's version number to us.  If it did, we use it,
                         if (read(rem, cp, 1) != 1)           * otherwise, we send our version number to the server and let
                                 lostconn(0);           * it decide if it can handle our protocol version.
                 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);           */
                 *--cp = '\0';          if (respbuff[1] == CNULL) {
                 cp = buf;                  /*
                 n = 0;                   * The server wants us to send it our version number
                 while (*cp >= '0' && *cp <= '9')                   */
                         n = (n * 10) + (*cp++ - '0');                  (void) sendcmd(S_VERSION, "%d", VERSION);
                 if (*cp == '\0' && n == VERSION)                  if (response() < 0)
                         return(1);                          return(0);
                 error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);  
         } else {          } else {
                 error("connection failed: version numbers don't match\n");                  /*
                 error("got unexpected input:");                   * The server sent it's version number to us
                 do {                   */
                         error("%c", *cp);                  proto_version = atoi(&respbuff[1]);
                 } while (*cp != '\n' && read(rem, cp, 1) == 1);                  if (proto_version != VERSION) {
                           fatalerr(
                     "Server version (%d) is not the same as local version (%d).",
                                 proto_version, VERSION);
                           return(0);
                   }
         }          }
         closeconn();  
         return(0);  
 }  
   
 /*          /*
  * Signal end of previous connection.           * Send config commands
  */           */
 static void          if (host[0]) {
 closeconn()                  (void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host);
 {                  if (response() < 0)
         if (debug)                          return(0);
                 printf("closeconn()\n");  
   
         if (rem >= 0) {  
                 (void) write(rem, "\2\n", 2);  
                 (void) close(rem);  
                 rem = -1;  
         }          }
 }          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);
           }
   
 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);          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  static void doarrow(cmd, filev)
 dodcolon(filev, files, stamp, cmds)          struct cmd *cmd;
         char **filev;          char **filev;
         struct namelist *files;  
         char *stamp;  
         struct subcmd *cmds;  
 {  {
         register struct subcmd *sc;  
         register struct namelist *f;          register struct namelist *f;
           register struct subcmd *sc;
         register char **cpp;          register char **cpp;
         struct timeval tv[2];          int n, ddir, destdir, opts = options;
         struct timezone tz;          struct namelist *files;
         struct stat stb;          struct subcmd *sbcmds;
           char *rhost;
           int didupdate = 0;
   
         if (debug)          if (!cmd) {
                 printf("dodcolon()\n");                  debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
                   return;
           }
   
           files = cmd->c_files;
           sbcmds = cmd->c_cmds;
           rhost = cmd->c_name;
   
         if (files == NULL) {          if (files == NULL) {
                 error("no files to be updated\n");                  error("No files to be updated on %s for target \"%s\"",
                         rhost, cmd->c_label);
                 return;                  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;          debugmsg(DM_CALL, "doarrow(%x, %s, %x) start",
         lastmod = stb.st_mtime;                   files, A(rhost), sbcmds);
         if (nflag || (options & VERIFY))  
                 tfp = NULL;          if (nflag)
                   (void) printf("updating host %s\n", rhost);
         else {          else {
                 if ((tfp = fopen(tempfile, "w")) == NULL) {                  if (cmd->c_flags & CMD_CONNFAILED) {
                         error("%s: %s\n", stamp, strerror(errno));                          debugmsg(DM_MISC,
                                    "makeconn %s failed before; skipping\n",
                                    rhost);
                         return;                          return;
                 }                  }
                 (void) gettimeofday(&tv[0], &tz);  
                 tv[1] = tv[0];                  if (setjmp(finish_jmpbuf)) {
                 (void) utimes(stamp, tv);                          debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
                           markfailed(cmd, cmds);
                           return;
                   }
   
                   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) {          for (f = files; f != NULL; f = f->n_next) {
                 if (filev) {                  if (filev) {
                         for (cpp = filev; *cpp; cpp++)                          for (cpp = filev; *cpp; cpp++)
Line 384 
Line 528 
                         continue;                          continue;
                 }                  }
         found:          found:
                 tp = NULL;                  if (install(f->n_name, sc->sc_name, ddir, destdir,
                 cmptime(f->n_name);                                  sc->sc_options) > 0)
                           ++didupdate;
                   opts = sc->sc_options;
         }          }
   
         if (tfp != NULL)          } /* end loop for each INSTALL command */
                 (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);  
 }  
   
 /*          /* if no INSTALL commands present, do default install */
  * Compare the mtime of file to the list of time stamps.          if (!n) {
  */                  for (f = files; f != NULL; f = f->n_next) {
 static void                          if (filev) {
 cmptime(name)                                  for (cpp = filev; *cpp; cpp++)
         char *name;                                          if (strcmp(f->n_name, *cpp) == 0)
 {                                                  goto found2;
         struct stat stb;                                  continue;
                           }
         if (debug)                  found2:
                 printf("cmptime(%s)\n", name);                          /* ddir & destdir set to zero for default install */
                           if (install(f->n_name, NULL, 0, 0, options) > 0)
         if (except(name))                                  ++didupdate;
                 return;                  }
   
         if (nflag) {  
                 printf("comparing dates: %s\n", name);  
                 return;  
         }          }
   
   done:
         /*          /*
          * first time cmptime() is called?           * Run any commands for the entire cmd
          */           */
         if (tp == NULL) {          if (didupdate > 0) {
                 if (exptilde(target, name) == NULL)                  runcmdspecial(cmd, filev, opts);
                         return;                  didupdate = 0;
                 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) {          if (!nflag)
         case S_IFREG:                  (void) signal(SIGPIPE, cleanup);
                 break;  
   
         case S_IFDIR:          for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
                 rcmptime(&stb);                  if (sc->sc_type == NOTIFY)
                 return;                          notify(rhost, sc->sc_args, (time_t) 0);
   
         default:          if (!nflag) {
                 error("%s: not a plain file\n", name);                  register struct linkbuf *nextl, *l;
                 return;  
                   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;
         }          }
   }
   
         if (stb.st_mtime > lastmod)  okname(name)
                 log(tfp, "new: %s\n", 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 (isbad) {
                   error("Invalid user name \"%s\"\n", name);
                   return(0);
           }
           return(1);
 }  }
   
 static void  static void rcmptime(st, sbcmds, env)
 rcmptime(st)  
         struct stat *st;          struct stat *st;
           struct subcmd *sbcmds;
           char **env;
 {  {
         register DIR *d;          register DIR *d;
         register struct direct *dp;          register DIRENTRY *dp;
         register char *cp;          register char *cp;
         char *otp;          char *optarget;
         int len;          int len;
   
         if (debug)          debugmsg(DM_CALL, "rcmptime(%x) start", st);
                 printf("rcmptime(%x)\n", st);  
   
         if ((d = opendir(target)) == NULL) {          if ((d = opendir((char *) target)) == NULL) {
                 error("%s: %s\n", target, strerror(errno));                  error("%s: open directory failed: %s", target, SYSERR);
                 return;                  return;
         }          }
         otp = tp;          optarget = ptarget;
         len = tp - target;          len = ptarget - target;
         while (dp = readdir(d)) {          while (dp = readdir(d)) {
                 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))                  if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
                         continue;                          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);                          error("%s/%s: Name too long\n", target, dp->d_name);
                         continue;                          continue;
                 }                  }
                 tp = otp;                  ptarget = optarget;
                 *tp++ = '/';                  *ptarget++ = '/';
                 cp = dp->d_name;                  cp = dp->d_name;
                 while (*tp++ = *cp++)                  while (*ptarget++ = *cp++)
                         ;                          ;
                 tp--;                  ptarget--;
                 cmptime(target);                  cmptime(target, sbcmds, env);
         }          }
         closedir(d);          (void) closedir((DIR *) d);
         tp = otp;          ptarget = optarget;
         *tp = '\0';          *ptarget = '\0';
 }  }
   
 /*  /*
  * Notify the list of people the changes that were made.   * Compare the mtime of file to the list of time stamps.
  * rhost == NULL if we are mailing a list of changes compared to at time  
  * stamp file.  
  */   */
 static void  static void cmptime(name, sbcmds, env)
 notify(file, rhost, to, lmod)          char *name;
         char *file, *rhost;          struct subcmd *sbcmds;
         register struct namelist *to;          char **env;
         time_t lmod;  
 {  {
         register int fd, len;          struct subcmd *sc;
         struct stat stb;          struct stat stb;
         FILE *pf;          int inlist();
   
         if ((options & VERIFY) || to == NULL)          debugmsg(DM_CALL, "cmptime(%s)", name);
   
           if (except(name))
                 return;                  return;
         if (!qflag) {  
                 printf("notify ");  
                 if (rhost)  
                         printf("@%s ", rhost);  
                 prnames(to);  
         }  
         if (nflag)  
                 return;  
   
         if ((fd = open(file, 0)) < 0) {          if (nflag) {
                 error("%s: %s\n", file, strerror(errno));                  (void) printf("comparing dates: %s\n", name);
                 return;                  return;
         }          }
         if (fstat(fd, &stb) < 0) {  
                 error("%s: %s\n", file, strerror(errno));          /*
                 (void) close(fd);           * first time cmptime() is called?
                 return;           */
           if (ptarget == NULL) {
                   if (exptilde(target, name) == NULL)
                           return;
                   ptarget = name = target;
                   while (*ptarget)
                           ptarget++;
         }          }
         if (stb.st_size == 0) {          if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
                 (void) close(fd);                  error("%s: cannot access file: %s", name, SYSERR);
                 return;                  return;
         }          }
         /*  
          * Create a pipe to mailling program.          if (S_ISDIR(stb.st_mode)) {
          */                  rcmptime(&stb, sbcmds, env);
         (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;                  return;
           } else if (!S_ISREG(stb.st_mode)) {
                   error("%s: not a plain file", name);
                   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)          if (stb.st_mtime > lastmod) {
                 (void) fwrite(buf, 1, len, pf);                  message(MT_INFO, "%s: file is newer", name);
         (void) close(fd);                  for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
         (void) pclose(pf);                          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  static void dodcolon(cmd, filev)
 inlist(list, file)          struct cmd *cmd;
         struct namelist *list;          char **filev;
         char *file;  
 {  {
         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)          debugmsg(DM_CALL, "dodcolon()");
                 if (!strcmp(file, nl->n_name))  
                         return(1);          if (files == NULL) {
         return(0);                  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.   * Return TRUE if file is in the exception list.
  */   */
 int  extern int except(file)
 except(file)  
         char *file;          char *file;
 {  {
         register struct subcmd *sc;          register struct subcmd *sc;
         register struct namelist *nl;          register struct namelist *nl;
   
         if (debug)          debugmsg(DM_CALL, "except(%s)", file);
                 printf("except(%s)\n", file);  
   
         for (sc = subcmds; sc != NULL; sc = sc->sc_next) {          for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
                 if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)                  if (sc->sc_type == EXCEPT) {
                         continue;                          for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
                 for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {                                  if (strcmp(file, nl->n_name) == 0)
                         if (sc->sc_type == EXCEPT) {  
                                 if (!strcmp(file, nl->n_name))  
                                         return(1);                                          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);          return(0);
 }  }
   
 char *  /*
 colon(cp)   * Do a specific command for a specific host
         register char *cp;   */
   static void docmdhost(cmd, filev)
           struct cmd *cmd;
           char **filev;
 {  {
           checkcmd(cmd);
   
         while (*cp) {          /*
                 if (*cp == ':')           * If we're multi-threaded and we're the parent, spawn a
                         return(cp);           * new child process.
                 if (*cp == '/')           */
                         return(0);          if (do_fork && !amchild) {
                 cp++;                  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;
         }          }
         return(0);  
           /*
            * 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();
           }
 }  }

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2