[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.16

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