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

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