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

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