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

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