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

Annotation of src/usr.bin/rdist/docmd.c, Revision 1.2

1.1       deraadt     1: /*
1.2     ! dm          2:  * Copyright (c) 1983 Regents of the University of California.
        !             3:  * All rights reserved.
1.1       deraadt     4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  * 3. All advertising materials mentioning features or use of this software
                     14:  *    must display the following acknowledgement:
                     15:  *     This product includes software developed by the University of
                     16:  *     California, Berkeley and its contributors.
                     17:  * 4. Neither the name of the University nor the names of its contributors
                     18:  *    may be used to endorse or promote products derived from this software
                     19:  *    without specific prior written permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     22:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     23:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     24:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     25:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     26:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     27:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     28:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     29:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     31:  * SUCH DAMAGE.
                     32:  */
                     33:
                     34: #ifndef lint
1.2     ! dm         35: static char RCSid[] =
        !            36: "$Id: docmd.c,v 6.85 1995/12/12 00:20:24 mcooper Exp $";
        !            37:
        !            38: static char sccsid[] = "@(#)docmd.c    5.1 (Berkeley) 6/6/85";
        !            39:
        !            40: static char copyright[] =
        !            41: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
        !            42:  All rights reserved.\n";
1.1       deraadt    43: #endif /* not lint */
                     44:
1.2     ! dm         45: /*
        !            46:  * Functions for rdist that do command (cmd) related activities.
        !            47:  */
        !            48:
1.1       deraadt    49: #include "defs.h"
1.2     ! dm         50: #include "y.tab.h"
        !            51: #include <sys/socket.h>
1.1       deraadt    52: #include <netdb.h>
                     53:
1.2     ! dm         54: struct subcmd         *subcmds;                /* list of sub-commands for
        !            55:                                                   current cmd */
        !            56: struct namelist               *filelist;               /* list of source files */
        !            57: extern struct cmd      *cmds;                  /* Initialized by yyparse() */
        !            58: time_t                 lastmod;                /* Last modify time */
        !            59:
        !            60: extern char            target[];
        !            61: extern char           *ptarget;
        !            62: extern int             activechildren;
        !            63: extern int             maxchildren;
        !            64: extern int             amchild;
        !            65: extern char           *path_rdistd;
        !            66:
        !            67: static void cmptime();
1.1       deraadt    68:
                     69: /*
1.2     ! dm         70:  * Signal end of connection.
1.1       deraadt    71:  */
1.2     ! dm         72: static void closeconn()
1.1       deraadt    73: {
1.2     ! dm         74:        debugmsg(DM_CALL, "closeconn() called\n");
1.1       deraadt    75:
1.2     ! dm         76:        if (rem_w >= 0) {
        !            77:                /* We don't care if the connection is still good or not */
        !            78:                signal(SIGPIPE, SIG_IGN);
        !            79:
        !            80:                (void) sendcmd(C_FERRMSG, NULL);
        !            81:                (void) close(rem_w);
        !            82:                (void) close(rem_r); /* This can't hurt */
        !            83:                rem_w = -1;
        !            84:                rem_r = -1;
1.1       deraadt    85:        }
                     86: }
                     87:
                     88: /*
1.2     ! dm         89:  * Notify the list of people the changes that were made.
        !            90:  * rhost == NULL if we are mailing a list of changes compared to at time
        !            91:  * stamp file.
1.1       deraadt    92:  */
1.2     ! dm         93: static void notify(rhost, to, lmod)
1.1       deraadt    94:        char *rhost;
1.2     ! dm         95:        register struct namelist *to;
        !            96:        time_t lmod;
1.1       deraadt    97: {
1.2     ! dm         98:        register int fd, len;
        !            99:        FILE *pf, *popen();
        !           100:        struct stat stb;
        !           101:        static char buf[BUFSIZ];
        !           102:        char *file;
1.1       deraadt   103:
1.2     ! dm        104:        if (IS_ON(options, DO_VERIFY) || to == NULL)
        !           105:                return;
1.1       deraadt   106:
1.2     ! dm        107:        if ((file = getnotifyfile()) == NULL)
1.1       deraadt   108:                return;
1.2     ! dm        109:
        !           110:        if (!IS_ON(options, DO_QUIET)) {
        !           111:                message(MT_INFO, "notify %s%s %s",
        !           112:                        (rhost) ? "@" : "",
        !           113:                        (rhost) ? rhost : "", getnlstr(to));
1.1       deraadt   114:        }
                    115:
                    116:        if (nflag)
1.2     ! dm        117:                return;
        !           118:
        !           119:        debugmsg(DM_MISC, "notify() temp file = '%s'", file);
        !           120:
        !           121:        if ((fd = open(file, O_RDONLY)) < 0) {
        !           122:                error("%s: open for reading failed: %s", file, SYSERR);
        !           123:                return;
        !           124:        }
        !           125:        if (fstat(fd, &stb) < 0) {
        !           126:                error("%s: fstat failed: %s", file, SYSERR);
        !           127:                (void) close(fd);
        !           128:                return;
        !           129:        }
        !           130:        if (stb.st_size == 0) {
        !           131:                (void) close(fd);
        !           132:                return;
        !           133:        }
        !           134:        /*
        !           135:         * Create a pipe to mailling program.
        !           136:         * Set IFS to avoid possible security problem with users
        !           137:         * setting "IFS=/".
        !           138:         */
        !           139:        (void) sprintf(buf, "IFS=\" \t\"; export IFS; %s -oi -t",
        !           140:                       _PATH_SENDMAIL);
        !           141:        pf = popen(buf, "w");
        !           142:        if (pf == NULL) {
        !           143:                error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
        !           144:                (void) unlink(file);
        !           145:                (void) close(fd);
        !           146:                return;
        !           147:        }
        !           148:        /*
        !           149:         * Output the proper header information.
        !           150:         */
        !           151:        (void) fprintf(pf, "From: rdist (Remote distribution program)\n");
        !           152:        (void) fprintf(pf, "To:");
        !           153:        if (!any('@', to->n_name) && rhost != NULL)
        !           154:                (void) fprintf(pf, " %s@%s", to->n_name, rhost);
        !           155:        else
        !           156:                (void) fprintf(pf, " %s", to->n_name);
        !           157:        to = to->n_next;
        !           158:        while (to != NULL) {
        !           159:                if (!any('@', to->n_name) && rhost != NULL)
        !           160:                        (void) fprintf(pf, ", %s@%s", to->n_name, rhost);
        !           161:                else
        !           162:                        (void) fprintf(pf, ", %s", to->n_name);
        !           163:                to = to->n_next;
        !           164:        }
        !           165:        (void) putc('\n', pf);
        !           166:        if (rhost != NULL)
        !           167:                (void) fprintf(pf,
        !           168:                             "Subject: files updated by rdist from %s to %s\n",
        !           169:                               host, rhost);
        !           170:        else
        !           171:                (void) fprintf(pf, "Subject: files updated after %s\n",
        !           172:                               ctime(&lmod));
        !           173:        (void) putc('\n', pf);
        !           174:        (void) putc('\n', pf);
        !           175:
        !           176:        while ((len = read(fd, buf, sizeof(buf))) > 0)
        !           177:                (void) fwrite(buf, 1, len, pf);
        !           178:
        !           179:        (void) pclose(pf);
        !           180:        (void) close(fd);
        !           181:        (void) unlink(file);
        !           182: }
        !           183:
        !           184: /*
        !           185:  * XXX Hack for NFS.  If a hostname from the distfile
        !           186:  * ends with a '+', then the normal restriction of
        !           187:  * skipping files that are on an NFS filesystem is
        !           188:  * bypassed.  We always strip '+' to be consistent.
        !           189:  */
        !           190: static void checkcmd(cmd)
        !           191:        struct cmd *cmd;
        !           192: {
        !           193:        int l;
        !           194:
        !           195:        if (!cmd || !(cmd->c_name)) {
        !           196:                debugmsg(DM_MISC, "checkcmd() NULL cmd parameter");
        !           197:                return;
        !           198:        }
        !           199:
        !           200:        l = strlen(cmd->c_name);
        !           201:        if (l <= 0)
        !           202:                return;
        !           203:        if (cmd->c_name[l-1] == '+') {
        !           204:                cmd->c_flags |= CMD_NOCHKNFS;
        !           205:                cmd->c_name[l-1] = CNULL;
        !           206:        }
        !           207: }
        !           208:
        !           209: /*
        !           210:  * Mark all other entries for this command (cmd)
        !           211:  * as assigned.
        !           212:  */
        !           213: extern void markassigned(cmd, cmdlist)
        !           214:        struct cmd *cmd;
        !           215:        struct cmd *cmdlist;
        !           216: {
        !           217:        register struct cmd *pcmd;
        !           218:
        !           219:        for (pcmd = cmdlist; pcmd; pcmd = pcmd->c_next) {
        !           220:                checkcmd(pcmd);
        !           221:                if (pcmd->c_type == cmd->c_type &&
        !           222:                    strcmp(pcmd->c_name, cmd->c_name)==0)
        !           223:                        pcmd->c_flags |= CMD_ASSIGNED;
1.1       deraadt   224:        }
1.2     ! dm        225: }
        !           226:
        !           227: /*
        !           228:  * Mark the command "cmd" as failed for all commands in list cmdlist.
        !           229:  */
        !           230: static void markfailed(cmd, cmdlist)
        !           231:        struct cmd *cmd;
        !           232:        struct cmd *cmdlist;
        !           233: {
        !           234:        register struct cmd *pc;
        !           235:
        !           236:        if (!cmd) {
        !           237:                debugmsg(DM_MISC, "markfailed() NULL cmd parameter");
        !           238:                return;
1.1       deraadt   239:        }
1.2     ! dm        240:
        !           241:        checkcmd(cmd);
        !           242:        cmd->c_flags |= CMD_CONNFAILED;
        !           243:        for (pc = cmdlist; pc; pc = pc->c_next) {
        !           244:                checkcmd(pc);
        !           245:                if (pc->c_type == cmd->c_type &&
        !           246:                    strcmp(pc->c_name, cmd->c_name)==0)
        !           247:                        pc->c_flags |= CMD_CONNFAILED;
1.1       deraadt   248:        }
1.2     ! dm        249: }
        !           250:
        !           251: static int remotecmd(rhost, luser, ruser, cmd)
        !           252:        char *rhost;
        !           253:        char *luser, *ruser;
        !           254:        char *cmd;
        !           255: {
        !           256:        int desc;
        !           257: #if    defined(DIRECT_RCMD)
        !           258:        static int port = -1;
        !           259: #endif /* DIRECT_RCMD */
        !           260:
        !           261:        debugmsg(DM_MISC, "local user = %s remote user = %s\n", luser, ruser);
        !           262:        debugmsg(DM_MISC, "Remote command = '%s'\n", cmd);
        !           263:
        !           264:        (void) fflush(stdout);
        !           265:        (void) fflush(stderr);
        !           266:        (void) signal(SIGALRM, sighandler);
        !           267:        (void) alarm(RTIMEOUT);
        !           268:
        !           269: #if    defined(DIRECT_RCMD)
        !           270:        (void) signal(SIGPIPE, sighandler);
        !           271:
        !           272:        if (port < 0) {
        !           273:                struct servent *sp;
        !           274:
        !           275:                if ((sp = getservbyname("shell", "tcp")) == NULL)
        !           276:                                fatalerr("shell/tcp: unknown service");
        !           277:                port = sp->s_port;
1.1       deraadt   278:        }
1.2     ! dm        279:
        !           280:        if (becomeroot() != 0)
        !           281:                exit(1);
        !           282:        desc = rcmd(&rhost, port, luser, ruser, cmd, 0);
        !           283:        if (becomeuser() != 0)
        !           284:                exit(1);
        !           285: #else  /* !DIRECT_RCMD */
        !           286:        debugmsg(DM_MISC, "Remote shell command = '%s'\n", path_remsh);
        !           287:        (void) signal(SIGPIPE, SIG_IGN);
        !           288:        desc = rshrcmd(&rhost, -1, luser, ruser, cmd, 0);
        !           289:        if (desc > 0)
        !           290:                (void) signal(SIGPIPE, sighandler);
        !           291: #endif /* DIRECT_RCMD */
        !           292:
        !           293:        (void) alarm(0);
        !           294:
        !           295:        return(desc);
1.1       deraadt   296: }
                    297:
                    298: /*
                    299:  * Create a connection to the rdist server on the machine rhost.
1.2     ! dm        300:  * Return 0 if the connection fails or 1 if it succeeds.
1.1       deraadt   301:  */
1.2     ! dm        302: static int makeconn(rhost)
1.1       deraadt   303:        char *rhost;
                    304: {
                    305:        register char *ruser, *cp;
                    306:        static char *cur_host = NULL;
1.2     ! dm        307:        extern char *locuser;
        !           308:        extern long min_freefiles, min_freespace;
        !           309:        extern char *remotemsglist;
        !           310:        char tuser[BUFSIZ], buf[BUFSIZ];
        !           311:        u_char respbuff[BUFSIZ];
1.1       deraadt   312:        int n;
                    313:
1.2     ! dm        314:        debugmsg(DM_CALL, "makeconn(%s)", rhost);
1.1       deraadt   315:
1.2     ! dm        316:        /*
        !           317:         * See if we're already connected to this host
        !           318:         */
        !           319:        if (cur_host != NULL && rem_w >= 0) {
1.1       deraadt   320:                if (strcmp(cur_host, rhost) == 0)
                    321:                        return(1);
                    322:                closeconn();
                    323:        }
1.2     ! dm        324:
        !           325:        /*
        !           326:         * Determine remote user and current host names
        !           327:         */
1.1       deraadt   328:        cur_host = rhost;
1.2     ! dm        329:        cp = strchr(rhost, '@');
        !           330:
1.1       deraadt   331:        if (cp != NULL) {
                    332:                char c = *cp;
                    333:
1.2     ! dm        334:                *cp = CNULL;
        !           335:                (void) strncpy((char *)tuser, rhost, sizeof(tuser)-1);
1.1       deraadt   336:                *cp = c;
                    337:                rhost = cp + 1;
                    338:                ruser = tuser;
1.2     ! dm        339:                if (*ruser == CNULL)
        !           340:                        ruser = locuser;
1.1       deraadt   341:                else if (!okname(ruser))
                    342:                        return(0);
                    343:        } else
1.2     ! dm        344:                ruser = locuser;
        !           345:
        !           346:        if (!IS_ON(options, DO_QUIET))
        !           347:                message(MT_VERBOSE, "updating host %s", rhost);
1.1       deraadt   348:
1.2     ! dm        349:        (void) sprintf(buf, "%.*s -S", sizeof(buf)-5, path_rdistd);
        !           350:
        !           351:        if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0)
        !           352:                return(0);
1.1       deraadt   353:
1.2     ! dm        354:        /*
        !           355:         * First thing received should be S_VERSION
        !           356:         */
        !           357:        n = remline(respbuff, sizeof(respbuff), TRUE);
        !           358:        if (n <= 0 || respbuff[0] != S_VERSION) {
        !           359:                error("Unexpected input from server: \"%s\".", respbuff);
        !           360:                closeconn();
        !           361:                return(0);
1.1       deraadt   362:        }
                    363:
1.2     ! dm        364:        /*
        !           365:         * For future compatibility we check to see if the server
        !           366:         * sent it's version number to us.  If it did, we use it,
        !           367:         * otherwise, we send our version number to the server and let
        !           368:         * it decide if it can handle our protocol version.
        !           369:         */
        !           370:        if (respbuff[1] == CNULL) {
        !           371:                /*
        !           372:                 * The server wants us to send it our version number
        !           373:                 */
        !           374:                (void) sendcmd(S_VERSION, "%d", VERSION);
        !           375:                if (response() < 0)
        !           376:                        return(0);
1.1       deraadt   377:        } else {
1.2     ! dm        378:                /*
        !           379:                 * The server sent it's version number to us
        !           380:                 */
        !           381:                proto_version = atoi(&respbuff[1]);
        !           382:                if (proto_version != VERSION) {
        !           383:                        fatalerr(
        !           384:                  "Server version (%d) is not the same as local version (%d).",
        !           385:                              proto_version, VERSION);
        !           386:                        return(0);
        !           387:                }
1.1       deraadt   388:        }
                    389:
1.2     ! dm        390:        /*
        !           391:         * Send config commands
        !           392:         */
        !           393:        if (host[0]) {
        !           394:                (void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host);
        !           395:                if (response() < 0)
        !           396:                        return(0);
        !           397:        }
        !           398:        if (min_freespace) {
        !           399:                (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREESPACE,
        !           400:                               min_freespace);
        !           401:                if (response() < 0)
        !           402:                        return(0);
        !           403:        }
        !           404:        if (min_freefiles) {
        !           405:                (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREEFILES,
        !           406:                               min_freefiles);
        !           407:                if (response() < 0)
        !           408:                        return(0);
        !           409:        }
        !           410:        if (remotemsglist) {
        !           411:                (void) sendcmd(C_SETCONFIG, "%c%s", SC_LOGGING, remotemsglist);
        !           412:                if (response() < 0)
        !           413:                        return(0);
1.1       deraadt   414:        }
                    415:
                    416:        return(1);
                    417: }
                    418:
                    419: /*
1.2     ! dm        420:  * Process commands for sending files to other machines.
1.1       deraadt   421:  */
1.2     ! dm        422: static void doarrow(cmd, filev)
        !           423:        struct cmd *cmd;
1.1       deraadt   424:        char **filev;
                    425: {
1.2     ! dm        426:        register struct namelist *f;
1.1       deraadt   427:        register struct subcmd *sc;
                    428:        register char **cpp;
1.2     ! dm        429:        int n, ddir, destdir, opts = options;
        !           430:        struct namelist *files;
        !           431:        struct subcmd *sbcmds;
        !           432:        char *rhost;
        !           433:        int didupdate = 0;
        !           434:
        !           435:        if (!cmd) {
        !           436:                debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
        !           437:                return;
        !           438:        }
1.1       deraadt   439:
1.2     ! dm        440:        files = cmd->c_files;
        !           441:        sbcmds = cmd->c_cmds;
        !           442:        rhost = cmd->c_name;
1.1       deraadt   443:
                    444:        if (files == NULL) {
1.2     ! dm        445:                error("No files to be updated on %s for target \"%s\"",
        !           446:                      rhost, cmd->c_label);
1.1       deraadt   447:                return;
                    448:        }
                    449:
1.2     ! dm        450:        debugmsg(DM_CALL, "doarrow(%x, %s, %x) start",
        !           451:                 files, A(rhost), sbcmds);
        !           452:
        !           453:        if (nflag)
        !           454:                (void) printf("updating host %s\n", rhost);
1.1       deraadt   455:        else {
1.2     ! dm        456:                if (cmd->c_flags & CMD_CONNFAILED) {
        !           457:                        debugmsg(DM_MISC,
        !           458:                                 "makeconn %s failed before; skipping\n",
        !           459:                                 rhost);
        !           460:                        return;
        !           461:                }
        !           462:
        !           463:                if (setjmp(finish_jmpbuf)) {
        !           464:                        debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
        !           465:                        markfailed(cmd, cmds);
        !           466:                        return;
        !           467:                }
        !           468:
        !           469:                if (!makeconn(rhost)) {
        !           470:                        markfailed(cmd, cmds);
1.1       deraadt   471:                        return;
                    472:                }
                    473:        }
                    474:
1.2     ! dm        475:        subcmds = sbcmds;
        !           476:        filelist = files;
        !           477:
        !           478:        n = 0;
        !           479:        for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
        !           480:                if (sc->sc_type != INSTALL)
        !           481:                        continue;
        !           482:                n++;
        !           483:        /*
        !           484:         * destination is a directory if one of the following is true:
        !           485:         * a) more than one name specified on left side of -> directive
        !           486:         * b) basename of destination in "install" directive is "."
        !           487:         *    (e.g. install /tmp/.;)
        !           488:         * c) name on left side of -> directive is a directory on local system.
        !           489:         *
        !           490:         * We need 2 destdir flags (destdir and ddir) because single directory
        !           491:         * source is handled differently.  In this case, ddir is 0 (which
        !           492:         * tells install() not to send DIRTARGET directive to remote rdistd)
        !           493:         * and destdir is 1 (which tells remfilename() how to build the FILE
        !           494:         * variables correctly).  In every other case, destdir and ddir will
        !           495:         * have the same value.
        !           496:         */
        !           497:        ddir = files->n_next != NULL;   /* destination is a directory */
        !           498:        if (!ddir) {
        !           499:                struct stat s;
        !           500:                int isadir = 0;
        !           501:
        !           502:                if (lstat(files->n_name, &s) == 0)
        !           503:                        isadir = S_ISDIR(s.st_mode);
        !           504:                if (!isadir && sc->sc_name && *sc->sc_name)
        !           505:                        ddir = !strcmp(xbasename(sc->sc_name),".");
        !           506:                destdir = isadir | ddir;
        !           507:        } else
        !           508:                destdir = ddir;
        !           509:
        !           510:        debugmsg(DM_MISC,
        !           511:                 "Debug files->n_next= %d, destdir=%d, ddir=%d",
        !           512:                 files->n_next, destdir, ddir);
        !           513:
        !           514:        if (!sc->sc_name || !*sc->sc_name) {
        !           515:                destdir = 0;
        !           516:                ddir = 0;
        !           517:        }
        !           518:
        !           519:        debugmsg(DM_MISC,
        !           520:                 "Debug sc->sc_name=%x, destdir=%d, ddir=%d",
        !           521:                 sc->sc_name, destdir, ddir);
        !           522:
1.1       deraadt   523:        for (f = files; f != NULL; f = f->n_next) {
                    524:                if (filev) {
                    525:                        for (cpp = filev; *cpp; cpp++)
                    526:                                if (strcmp(f->n_name, *cpp) == 0)
                    527:                                        goto found;
                    528:                        continue;
                    529:                }
                    530:        found:
1.2     ! dm        531:                if (install(f->n_name, sc->sc_name, ddir, destdir,
        !           532:                                sc->sc_options) > 0)
        !           533:                        ++didupdate;
        !           534:                opts = sc->sc_options;
1.1       deraadt   535:        }
                    536:
1.2     ! dm        537:        } /* end loop for each INSTALL command */
        !           538:
        !           539:        /* if no INSTALL commands present, do default install */
        !           540:        if (!n) {
        !           541:                for (f = files; f != NULL; f = f->n_next) {
        !           542:                        if (filev) {
        !           543:                                for (cpp = filev; *cpp; cpp++)
        !           544:                                        if (strcmp(f->n_name, *cpp) == 0)
        !           545:                                                goto found2;
        !           546:                                continue;
        !           547:                        }
        !           548:                found2:
        !           549:                        /* ddir & destdir set to zero for default install */
        !           550:                        if (install(f->n_name, NULL, 0, 0, options) > 0)
        !           551:                                ++didupdate;
        !           552:                }
        !           553:        }
        !           554:
        !           555: done:
        !           556:        /*
        !           557:         * Run any commands for the entire cmd
        !           558:         */
        !           559:        if (didupdate > 0) {
        !           560:                runcmdspecial(cmd, filev, opts);
        !           561:                didupdate = 0;
        !           562:        }
        !           563:
        !           564:        if (!nflag)
        !           565:                (void) signal(SIGPIPE, cleanup);
        !           566:
        !           567:        for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
1.1       deraadt   568:                if (sc->sc_type == NOTIFY)
1.2     ! dm        569:                        notify(rhost, sc->sc_args, (time_t) 0);
        !           570:
        !           571:        if (!nflag) {
        !           572:                register struct linkbuf *nextl, *l;
        !           573:
        !           574:                for (l = ihead; l != NULL; free((char *)l), l = nextl) {
        !           575:                        nextl = l->nextp;
        !           576:                        if (contimedout || IS_ON(opts, DO_IGNLNKS) ||
        !           577:                            l->count == 0)
        !           578:                                continue;
        !           579:                        message(MT_WARNING, "%s: Warning: %d %s link%s",
        !           580:                                l->pathname, abs(l->count),
        !           581:                                (l->count > 0) ? "missing" : "extra",
        !           582:                                (l->count == 1) ? "" : "s");
        !           583:                }
        !           584:                ihead = NULL;
        !           585:        }
        !           586: }
        !           587:
        !           588: okname(name)
        !           589:        register char *name;
        !           590: {
        !           591:        register char *cp = name;
        !           592:        register int c, isbad;
        !           593:
        !           594:        for (isbad = FALSE; *cp && !isbad; ++cp) {
        !           595:                c = *cp;
        !           596:                if (c & 0200)
        !           597:                        isbad = TRUE;
        !           598:                if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
        !           599:                        isbad = TRUE;
        !           600:        }
        !           601:
        !           602:        if (isbad) {
        !           603:                error("Invalid user name \"%s\"\n", name);
        !           604:                return(0);
        !           605:        }
        !           606:        return(1);
        !           607: }
        !           608:
        !           609: static void rcmptime(st, sbcmds, env)
        !           610:        struct stat *st;
        !           611:        struct subcmd *sbcmds;
        !           612:        char **env;
        !           613: {
        !           614:        register DIR *d;
        !           615:        register DIRENTRY *dp;
        !           616:        register char *cp;
        !           617:        char *optarget;
        !           618:        int len;
        !           619:
        !           620:        debugmsg(DM_CALL, "rcmptime(%x) start", st);
        !           621:
        !           622:        if ((d = opendir((char *) target)) == NULL) {
        !           623:                error("%s: open directory failed: %s", target, SYSERR);
        !           624:                return;
        !           625:        }
        !           626:        optarget = ptarget;
        !           627:        len = ptarget - target;
        !           628:        while (dp = readdir(d)) {
        !           629:                if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
        !           630:                        continue;
        !           631:                if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) {
        !           632:                        error("%s/%s: Name too long\n", target, dp->d_name);
        !           633:                        continue;
        !           634:                }
        !           635:                ptarget = optarget;
        !           636:                *ptarget++ = '/';
        !           637:                cp = dp->d_name;
        !           638:                while (*ptarget++ = *cp++)
        !           639:                        ;
        !           640:                ptarget--;
        !           641:                cmptime(target, sbcmds, env);
        !           642:        }
        !           643:        (void) closedir((DIR *) d);
        !           644:        ptarget = optarget;
        !           645:        *ptarget = '\0';
1.1       deraadt   646: }
                    647:
                    648: /*
                    649:  * Compare the mtime of file to the list of time stamps.
                    650:  */
1.2     ! dm        651: static void cmptime(name, sbcmds, env)
1.1       deraadt   652:        char *name;
1.2     ! dm        653:        struct subcmd *sbcmds;
        !           654:        char **env;
1.1       deraadt   655: {
1.2     ! dm        656:        struct subcmd *sc;
1.1       deraadt   657:        struct stat stb;
1.2     ! dm        658:        int inlist();
1.1       deraadt   659:
1.2     ! dm        660:        debugmsg(DM_CALL, "cmptime(%s)", name);
1.1       deraadt   661:
                    662:        if (except(name))
                    663:                return;
                    664:
                    665:        if (nflag) {
1.2     ! dm        666:                (void) printf("comparing dates: %s\n", name);
1.1       deraadt   667:                return;
                    668:        }
                    669:
                    670:        /*
                    671:         * first time cmptime() is called?
                    672:         */
1.2     ! dm        673:        if (ptarget == NULL) {
1.1       deraadt   674:                if (exptilde(target, name) == NULL)
                    675:                        return;
1.2     ! dm        676:                ptarget = name = target;
        !           677:                while (*ptarget)
        !           678:                        ptarget++;
1.1       deraadt   679:        }
1.2     ! dm        680:        if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
        !           681:                error("%s: cannot access file: %s", name, SYSERR);
1.1       deraadt   682:                return;
                    683:        }
                    684:
1.2     ! dm        685:        if (S_ISDIR(stb.st_mode)) {
        !           686:                rcmptime(&stb, sbcmds, env);
1.1       deraadt   687:                return;
1.2     ! dm        688:        } else if (!S_ISREG(stb.st_mode)) {
        !           689:                error("%s: not a plain file", name);
1.1       deraadt   690:                return;
                    691:        }
                    692:
1.2     ! dm        693:        if (stb.st_mtime > lastmod) {
        !           694:                message(MT_INFO, "%s: file is newer", name);
        !           695:                for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
        !           696:                        char buf[BUFSIZ];
        !           697:                        if (sc->sc_type != SPECIAL)
        !           698:                                continue;
        !           699:                        if (sc->sc_args != NULL && !inlist(sc->sc_args, name))
        !           700:                                continue;
        !           701:                        (void) sprintf(buf, "%s=%s;%s",
        !           702:                                       E_LOCFILE, name, sc->sc_name);
        !           703:                        message(MT_CHANGE, "special \"%s\"", buf);
        !           704:                        if (*env) {
        !           705:                                int len = strlen(*env);
        !           706:                                *env = (char *) xrealloc(*env, len +
        !           707:                                                         strlen(name) + 2);
        !           708:                                *env[len] = CNULL;
        !           709:                                (void) strcat(*env, name);
        !           710:                                (void) strcat(*env, ":");
        !           711:                        }
        !           712:                        if (IS_ON(options, DO_VERIFY))
        !           713:                                continue;
        !           714:
        !           715:                        runcommand(buf);
        !           716:                }
        !           717:        }
1.1       deraadt   718: }
                    719:
1.2     ! dm        720: /*
        !           721:  * Process commands for comparing files to time stamp files.
        !           722:  */
        !           723: static void dodcolon(cmd, filev)
        !           724:        struct cmd *cmd;
        !           725:        char **filev;
1.1       deraadt   726: {
1.2     ! dm        727:        register struct subcmd *sc;
        !           728:        register struct namelist *f;
        !           729:        register char *cp, **cpp;
        !           730:        struct stat stb;
        !           731:        struct namelist *files = cmd->c_files;
        !           732:        struct subcmd *sbcmds = cmd->c_cmds;
        !           733:        char *env, *stamp = cmd->c_name;
1.1       deraadt   734:
1.2     ! dm        735:        debugmsg(DM_CALL, "dodcolon()");
1.1       deraadt   736:
1.2     ! dm        737:        if (files == NULL) {
        !           738:                error("No files to be updated for target \"%s\"",
        !           739:                      cmd->c_label);
1.1       deraadt   740:                return;
                    741:        }
1.2     ! dm        742:        if (stat(stamp, &stb) < 0) {
        !           743:                error("%s: stat failed: %s", stamp, SYSERR);
        !           744:                return;
        !           745:        }
        !           746:
        !           747:        debugmsg(DM_MISC, "%s: mtime %d\n", stamp, stb.st_mtime);
        !           748:
        !           749:        env = NULL;
        !           750:        for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
        !           751:                if (sc->sc_type == CMDSPECIAL) {
        !           752:                        env = (char *) xmalloc(sizeof(E_FILES) + 3);
        !           753:                        (void) sprintf(env, "%s='", E_FILES);
        !           754:                        break;
        !           755:                }
        !           756:        }
        !           757:
        !           758:        subcmds = sbcmds;
        !           759:        filelist = files;
        !           760:
        !           761:        lastmod = stb.st_mtime;
        !           762:        if (!nflag && !IS_ON(options, DO_VERIFY))
        !           763:                /*
        !           764:                 * Set atime and mtime to current time
        !           765:                 */
        !           766:                (void) setfiletime(stamp, (time_t) 0, (time_t) 0);
        !           767:
        !           768:        for (f = files; f != NULL; f = f->n_next) {
        !           769:                if (filev) {
        !           770:                        for (cpp = filev; *cpp; cpp++)
        !           771:                                if (strcmp(f->n_name, *cpp) == 0)
        !           772:                                        goto found;
1.1       deraadt   773:                        continue;
                    774:                }
1.2     ! dm        775:        found:
        !           776:                ptarget = NULL;
        !           777:                cmptime(f->n_name, sbcmds, &env);
        !           778:        }
        !           779:
        !           780:        for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
        !           781:                if (sc->sc_type == NOTIFY)
        !           782:                        notify((char *)NULL, sc->sc_args, (time_t)lastmod);
        !           783:                else if (sc->sc_type == CMDSPECIAL && env) {
        !           784:                        char *p;
        !           785:                        int len = strlen(env);
        !           786:
        !           787:                        env = xrealloc(env,
        !           788:                                       len + strlen(sc->sc_name) + 2);
        !           789:                        env[len] = CNULL;
        !           790:                        if (*(p = &env[len - 1]) == ':')
        !           791:                                *p = CNULL;
        !           792:                        (void) strcat(env, "';");
        !           793:                        (void) strcat(env, sc->sc_name);
        !           794:                        message(MT_CHANGE, "cmdspecial \"%s\"", env);
        !           795:                        if (!nflag && IS_OFF(options, DO_VERIFY))
        !           796:                                runcommand(env);
        !           797:                        (void) free(env);
        !           798:                        env = NULL;     /* so cmdspecial is only called once */
        !           799:                }
1.1       deraadt   800:        }
1.2     ! dm        801:        if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
        !           802:                (void) unlink(cp);
1.1       deraadt   803: }
                    804:
                    805: /*
1.2     ! dm        806:  * Return TRUE if file is in the exception list.
1.1       deraadt   807:  */
1.2     ! dm        808: extern int except(file)
        !           809:        char *file;
1.1       deraadt   810: {
1.2     ! dm        811:        register struct subcmd *sc;
        !           812:        register struct namelist *nl;
        !           813:
        !           814:        debugmsg(DM_CALL, "except(%s)", file);
1.1       deraadt   815:
1.2     ! dm        816:        for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
        !           817:                if (sc->sc_type == EXCEPT) {
        !           818:                        for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
        !           819:                                if (strcmp(file, nl->n_name) == 0)
        !           820:                                        return(1);
        !           821:                        continue;
        !           822:                }
        !           823:                if (sc->sc_type == PATTERN) {
        !           824:                        for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
        !           825:                                char *cp, *re_comp();
        !           826:
        !           827:                                if ((cp = re_comp(nl->n_name)) != NULL) {
        !           828:                                        error("Regex error \"%s\" for \"%s\".",
        !           829:                                              cp, nl->n_name);
        !           830:                                        return(0);
        !           831:                                }
        !           832:                                if (re_exec(file) > 0)
        !           833:                                        return(1);
        !           834:                        }
        !           835:                }
1.1       deraadt   836:        }
1.2     ! dm        837:        return(0);
        !           838: }
        !           839:
        !           840: /*
        !           841:  * Do a specific command for a specific host
        !           842:  */
        !           843: static void docmdhost(cmd, filev)
        !           844:        struct cmd *cmd;
        !           845:        char **filev;
        !           846: {
        !           847:        checkcmd(cmd);
1.1       deraadt   848:
                    849:        /*
1.2     ! dm        850:         * If we're multi-threaded and we're the parent, spawn a
        !           851:         * new child process.
1.1       deraadt   852:         */
1.2     ! dm        853:        if (do_fork && !amchild) {
        !           854:                int pid;
        !           855:
        !           856:                /*
        !           857:                 * If we're at maxchildren, wait for number of active
        !           858:                 * children to fall below max number of children.
        !           859:                 */
        !           860:                while (activechildren >= maxchildren)
        !           861:                        waitup();
        !           862:
        !           863:                pid = spawn(cmd, cmds);
        !           864:                if (pid == 0)
        !           865:                        /* Child */
        !           866:                        amchild = 1;
        !           867:                else
        !           868:                        /* Parent */
        !           869:                        return;
1.1       deraadt   870:        }
1.2     ! dm        871:
1.1       deraadt   872:        /*
1.2     ! dm        873:         * Disable NFS checks
1.1       deraadt   874:         */
1.2     ! dm        875:        if (cmd->c_flags & CMD_NOCHKNFS)
        !           876:                FLAG_OFF(options, DO_CHKNFS);
        !           877:
        !           878:        if (!nflag) {
        !           879:                currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
        !           880: #if    defined(SETARGS)
        !           881:                setproctitle("update %s", currenthost);
        !           882: #endif         /* SETARGS */
1.1       deraadt   883:        }
                    884:
1.2     ! dm        885:        switch (cmd->c_type) {
        !           886:        case ARROW:
        !           887:                doarrow(cmd, filev);
        !           888:                break;
        !           889:        case DCOLON:
        !           890:                dodcolon(cmd, filev);
        !           891:                break;
        !           892:        default:
        !           893:                fatalerr("illegal command type %d", cmd->c_type);
        !           894:        }
1.1       deraadt   895: }
                    896:
                    897: /*
1.2     ! dm        898:  * Do a specific command (cmd)
1.1       deraadt   899:  */
1.2     ! dm        900: static void docmd(cmd, argc, argv)
        !           901:        struct cmd *cmd;
        !           902:        int argc;
        !           903:        char **argv;
1.1       deraadt   904: {
1.2     ! dm        905:        register struct namelist *f;
        !           906:        register int i;
1.1       deraadt   907:
1.2     ! dm        908:        if (argc) {
        !           909:                for (i = 0; i < argc; i++) {
        !           910:                        if (cmd->c_label != NULL &&
        !           911:                            strcmp(cmd->c_label, argv[i]) == 0) {
        !           912:                                docmdhost(cmd, (char **) NULL);
        !           913:                                return;
        !           914:                        }
        !           915:                        for (f = cmd->c_files; f != NULL; f = f->n_next)
        !           916:                                if (strcmp(f->n_name, argv[i]) == 0) {
        !           917:                                        docmdhost(cmd, &argv[i]);
        !           918:                                        return;
        !           919:                                }
        !           920:                }
        !           921:        } else
        !           922:                docmdhost(cmd, (char **) NULL);
1.1       deraadt   923: }
                    924:
                    925: /*
1.2     ! dm        926:  *
        !           927:  * Multiple hosts are updated at once via a "ring" of at most
        !           928:  * maxchildren rdist processes.  The parent rdist fork()'s a child
        !           929:  * for a given host.  That child will update the given target files
        !           930:  * and then continue scanning through the remaining targets looking
        !           931:  * for more work for a given host.  Meanwhile, the parent gets the
        !           932:  * next target command and makes sure that it hasn't encountered
        !           933:  * that host yet since the children are responsible for everything
        !           934:  * for that host.  If no children have done this host, then check
        !           935:  * to see if the number of active proc's is less than maxchildren.
        !           936:  * If so, then spawn a new child for that host.  Otherwise, wait
        !           937:  * for a child to finish.
        !           938:  *
        !           939:  */
        !           940:
        !           941: /*
        !           942:  * Do the commands in cmds (initialized by yyparse).
1.1       deraadt   943:  */
1.2     ! dm        944: extern void docmds(hostlist, argc, argv)
        !           945:        struct namelist *hostlist;
        !           946:        int argc;
        !           947:        char **argv;
1.1       deraadt   948: {
1.2     ! dm        949:        register struct cmd *c;
        !           950:        register char *cp;
        !           951:        register int i;
        !           952:
        !           953:        (void) signal(SIGHUP, sighandler);
        !           954:        (void) signal(SIGINT, sighandler);
        !           955:        (void) signal(SIGQUIT, sighandler);
        !           956:        (void) signal(SIGTERM, sighandler);
        !           957:
        !           958:        if (!nflag)
        !           959:                mysetlinebuf(stdout);   /* Make output (mostly) clean */
        !           960:
        !           961: #if    defined(USE_STATDB)
        !           962:        if (!nflag && (dostatdb || juststatdb)) {
        !           963:                extern long reccount;
        !           964:                message(MT_INFO, "Making stat database [%s] ... \n",
        !           965:                               gettimestr());
        !           966:                if (mkstatdb() < 0)
        !           967:                        error("Warning: Make stat database failed.");
        !           968:                message(MT_INFO,
        !           969:                              "Stat database created: %d files stored [%s].\n",
        !           970:                               reccount, gettimestr());
        !           971:                if (juststatdb)
        !           972:                        return;
        !           973:        }
        !           974: #endif /* USE_STATDB */
        !           975:
        !           976:        /*
        !           977:         * Print errors for any command line targets we didn't find.
        !           978:         * If any errors are found, return to main() which will then exit.
        !           979:         */
        !           980:        for (i = 0; i < argc; i++) {
        !           981:                int found;
1.1       deraadt   982:
1.2     ! dm        983:                for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
        !           984:                        if (c->c_label && argv[i] &&
        !           985:                            strcmp(c->c_label, argv[i]) == 0) {
        !           986:                                found = TRUE;
        !           987:                                break;
        !           988:                        }
        !           989:                }
        !           990:                if (!found)
        !           991:                        error("Label \"%s\" is not defined in the distfile.",
        !           992:                              argv[i]);
        !           993:        }
        !           994:        if (nerrs)
        !           995:                return;
1.1       deraadt   996:
1.2     ! dm        997:        /*
        !           998:         * Main command loop.  Loop through all the commands.
        !           999:         */
        !          1000:        for (c = cmds; c != NULL; c = c->c_next) {
        !          1001:                checkcmd(c);
        !          1002:                if (do_fork) {
        !          1003:                        /*
        !          1004:                         * Let the children take care of their assigned host
        !          1005:                         */
        !          1006:                        if (amchild) {
        !          1007:                                if (strcmp(c->c_name, currenthost) != 0)
        !          1008:                                        continue;
        !          1009:                        } else if (c->c_flags & CMD_ASSIGNED) {
        !          1010:                                /* This cmd has been previously assigned */
        !          1011:                                debugmsg(DM_MISC, "prev assigned: %s\n",
        !          1012:                                         c->c_name);
1.1       deraadt  1013:                                continue;
                   1014:                        }
                   1015:                }
1.2     ! dm       1016:
        !          1017:                if (hostlist) {
        !          1018:                        /* Do specific hosts as specified on command line */
        !          1019:                        register struct namelist *nlptr;
        !          1020:
        !          1021:                        for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
        !          1022:                                /*
        !          1023:                                 * Try an exact match and then a match
        !          1024:                                 * without '@' (if present).
        !          1025:                                 */
        !          1026:                                if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
        !          1027:                                    ((cp = strchr(c->c_name, '@')) &&
        !          1028:                                     strcmp(++cp, nlptr->n_name) == 0))
        !          1029:                                        docmd(c, argc, argv);
        !          1030:                        continue;
        !          1031:                } else
        !          1032:                        /* Do all of the command */
        !          1033:                        docmd(c, argc, argv);
1.1       deraadt  1034:        }
                   1035:
1.2     ! dm       1036:        if (do_fork) {
        !          1037:                /*
        !          1038:                 * We're multi-threaded, so do appropriate shutdown
        !          1039:                 * actions based on whether we're the parent or a child.
        !          1040:                 */
        !          1041:                if (amchild) {
        !          1042:                        if (!IS_ON(options, DO_QUIET))
        !          1043:                                message(MT_VERBOSE, "updating of %s finished",
        !          1044:                                        currenthost);
        !          1045:                        closeconn();
        !          1046:                        cleanup();
        !          1047:                        exit(nerrs);
        !          1048:                }
1.1       deraadt  1049:
1.2     ! dm       1050:                /*
        !          1051:                 * Wait for all remaining active children to finish
        !          1052:                 */
        !          1053:                while (activechildren > 0) {
        !          1054:                        debugmsg(DM_MISC,
        !          1055:                                 "Waiting for %d children to finish.\n",
        !          1056:                                 activechildren);
        !          1057:                        waitup();
        !          1058:                }
        !          1059:        } else if (!nflag) {
        !          1060:                /*
        !          1061:                 * We're single-threaded so close down current connection
        !          1062:                 */
        !          1063:                closeconn();
        !          1064:                cleanup();
1.1       deraadt  1065:        }
                   1066: }