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

Annotation of src/usr.bin/cvs/cvsd.c, Revision 1.17

1.17    ! jfb         1: /*     $OpenBSD: cvsd.c,v 1.16 2004/12/14 19:53:12 xsa Exp $   */
1.1       jfb         2: /*
                      3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.14      tedu        4:  * All rights reserved.
1.1       jfb         5:  *
1.14      tedu        6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
1.1       jfb         9:  *
1.14      tedu       10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
1.1       jfb        12:  * 2. The name of the author may not be used to endorse or promote products
1.14      tedu       13:  *    derived from this software without specific prior written permission.
1.1       jfb        14:  *
                     15:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
                     16:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                     17:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
                     18:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     19:  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     20:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
                     21:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     22:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
                     23:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.14      tedu       24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1       jfb        25:  */
                     26:
                     27: #include <sys/param.h>
                     28: #include <sys/stat.h>
                     29: #include <sys/wait.h>
                     30: #include <sys/uio.h>
                     31:
                     32: #include <err.h>
                     33: #include <pwd.h>
                     34: #include <grp.h>
                     35: #include <poll.h>
                     36: #include <fcntl.h>
                     37: #include <dirent.h>
                     38: #include <stdlib.h>
                     39: #include <stdio.h>
                     40: #include <unistd.h>
                     41: #include <signal.h>
                     42: #include <errno.h>
                     43: #include <string.h>
                     44: #include <sysexits.h>
                     45:
                     46: #include "log.h"
                     47: #include "sock.h"
                     48: #include "cvs.h"
                     49: #include "cvsd.h"
                     50:
                     51:
                     52: static void  cvsd_parent_loop (void);
1.11      jfb        53: static void  cvsd_child_main  (void);
1.1       jfb        54: static int   cvsd_privdrop    (void);
1.8       jfb        55: static void  cvsd_report      (void);
1.1       jfb        56:
                     57:
                     58: extern char *__progname;
                     59:
                     60:
1.11      jfb        61: int    cvsd_fg = 0;
                     62: uid_t  cvsd_uid = -1;
                     63: gid_t  cvsd_gid = -1;
1.1       jfb        64:
1.10      jfb        65: volatile sig_atomic_t cvsd_running = 1;
                     66: volatile sig_atomic_t cvsd_restart = 0;
1.1       jfb        67:
1.9       jfb        68: static char  *cvsd_user = NULL;
                     69: static char  *cvsd_group = NULL;
1.7       jfb        70: static char  *cvsd_root = NULL;
                     71: static char  *cvsd_conffile = CVSD_CONF;
1.11      jfb        72: static char  *cvsd_moddir = NULL;
1.1       jfb        73: static int    cvsd_privfd = -1;
                     74:
                     75:
                     76: static TAILQ_HEAD(,cvsd_child) cvsd_children;
                     77: static volatile sig_atomic_t   cvsd_chnum = 0;
                     78: static volatile sig_atomic_t   cvsd_chmin = CVSD_CHILD_DEFMIN;
                     79: static volatile sig_atomic_t   cvsd_chmax = CVSD_CHILD_DEFMAX;
1.7       jfb        80: static volatile sig_atomic_t   cvsd_sigchld = 0;
1.8       jfb        81: static volatile sig_atomic_t   cvsd_siginfo = 0;
1.1       jfb        82:
                     83:
1.8       jfb        84: void   usage         (void);
                     85: void   cvsd_sighdlr  (int);
                     86: int    cvsd_msghdlr  (struct cvsd_child *, int);
1.3       jfb        87:
                     88:
1.1       jfb        89: /*
1.8       jfb        90:  * cvsd_sighdlr()
1.1       jfb        91:  *
1.8       jfb        92:  * Generic signal handler.
1.1       jfb        93:  */
                     94: void
1.8       jfb        95: cvsd_sighdlr(int signo)
1.1       jfb        96: {
1.8       jfb        97:        switch (signo) {
                     98:        case SIGHUP:
1.10      jfb        99:                cvsd_restart = 1;
1.8       jfb       100:                break;
                    101:        case SIGCHLD:
                    102:                cvsd_sigchld = 1;
                    103:                break;
                    104:        case SIGINT:
                    105:        case SIGTERM:
                    106:        case SIGQUIT:
1.10      jfb       107:                cvsd_running = 0;
1.8       jfb       108:                break;
                    109:        case SIGINFO:
                    110:                cvsd_siginfo = 1;
                    111:                break;
                    112:        }
1.1       jfb       113: }
                    114:
                    115:
                    116: /*
                    117:  * usage()
                    118:  *
                    119:  * Display program usage.
                    120:  */
                    121: void
                    122: usage(void)
                    123: {
                    124:        fprintf(stderr,
1.8       jfb       125:            "Usage: %s [-dfhpv] [-c config] [-g group] [-r root] "
                    126:            "[-s path] [-u user]\n"
1.7       jfb       127:            "\t-c config\tUse <config> as the configuration file\n"
1.1       jfb       128:            "\t-d\t\tStart the server in debugging mode (very verbose)\n"
                    129:            "\t-f\t\tStay in foreground instead of becoming a daemon\n"
1.8       jfb       130:            "\t-g group\tUse group <group> for privilege revocation\n"
1.6       jfb       131:            "\t-h\t\tPrint the usage and exit\n"
                    132:            "\t-p\t\tPerform repository sanity check on startup\n"
                    133:            "\t-r root\t\tUse <root> as the root directory of the repository\n"
                    134:            "\t-s path\t\tUse <path> as the path for the CVS server socket\n"
1.16      xsa       135:            "\t-u user\t\tUse user <user> for privilege revocation\n"
1.1       jfb       136:            "\t-v\t\tBe verbose\n",
                    137:            __progname);
                    138: }
                    139:
                    140:
                    141: int
                    142: main(int argc, char **argv)
                    143: {
                    144:        u_int i;
                    145:        int ret, checkrepo;
                    146:        struct passwd *pwd;
                    147:        struct group *grp;
                    148:
                    149:        checkrepo = 0;
1.17    ! jfb       150:        cvsd_set(CVSD_SET_SOCK, CVSD_SOCK_PATH);
1.9       jfb       151:        cvsd_set(CVSD_SET_USER, CVSD_USER);
                    152:        cvsd_set(CVSD_SET_GROUP, CVSD_GROUP);
1.1       jfb       153:
                    154:        if (cvs_log_init(LD_STD|LD_SYSLOG, LF_PID) < 0)
                    155:                err(1, "failed to initialize logging mechanism");
                    156:
1.6       jfb       157:        while ((ret = getopt(argc, argv, "a:c:dfhpr:s:v")) != -1) {
1.1       jfb       158:                switch (ret) {
1.3       jfb       159:                case 'c':
                    160:                        cvsd_conffile = optarg;
1.1       jfb       161:                        break;
                    162:                case 'd':
                    163:                        cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
                    164:                        cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
                    165:                        break;
                    166:                case 'f':
1.10      jfb       167:                        cvsd_fg = 1;
1.6       jfb       168:                        break;
1.8       jfb       169:                case 'g':
1.9       jfb       170:                        cvsd_set(CVSD_SET_GROUP, optarg);
1.8       jfb       171:                        break;
1.6       jfb       172:                case 'h':
                    173:                        usage();
                    174:                        exit(0);
                    175:                        /* NOTREACHED */
1.1       jfb       176:                        break;
                    177:                case 'p':
                    178:                        checkrepo = 1;
                    179:                        break;
                    180:                case 'r':
1.17    ! jfb       181:                        cvsd_set(CVSD_SET_ROOT, optarg);
1.1       jfb       182:                        break;
                    183:                case 's':
1.17    ! jfb       184:                        cvsd_set(CVSD_SET_SOCK, optarg);
1.1       jfb       185:                        break;
1.8       jfb       186:                case 'u':
1.9       jfb       187:                        cvsd_set(CVSD_SET_USER, optarg);
1.8       jfb       188:                        break;
1.1       jfb       189:                case 'v':
                    190:                        cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
                    191:                        break;
                    192:                default:
                    193:                        usage();
                    194:                        exit(EX_USAGE);
                    195:                }
                    196:        }
                    197:
                    198:        argc -= optind;
                    199:        argv += optind;
                    200:
1.3       jfb       201:        if (cvs_conf_read(cvsd_conffile) < 0)
                    202:                errx(1, "error parsing configuration file `%s'", cvsd_conffile);
1.1       jfb       203:
                    204:        if (cvsd_root == NULL)
                    205:                errx(1, "no CVS root directory specified");
                    206:
                    207:        if (argc > 0)
                    208:                errx(EX_USAGE, "unrecognized trailing arguments");
                    209:
                    210:        TAILQ_INIT(&cvsd_children);
                    211:
1.8       jfb       212:        pwd = getpwnam(cvsd_user);
1.1       jfb       213:        if (pwd == NULL)
1.8       jfb       214:                err(EX_NOUSER, "failed to get user `%s'", cvsd_user);
1.1       jfb       215:
1.8       jfb       216:        grp = getgrnam(cvsd_group);
1.1       jfb       217:        if (grp == NULL)
1.8       jfb       218:                err(EX_NOUSER, "failed to get group `%s'", cvsd_group);
1.1       jfb       219:
                    220:        cvsd_uid = pwd->pw_uid;
                    221:        cvsd_gid = grp->gr_gid;
                    222:
1.8       jfb       223:        signal(SIGHUP, cvsd_sighdlr);
                    224:        signal(SIGINT, cvsd_sighdlr);
                    225:        signal(SIGQUIT, cvsd_sighdlr);
                    226:        signal(SIGTERM, cvsd_sighdlr);
                    227:        signal(SIGCHLD, cvsd_sighdlr);
1.11      jfb       228:        signal(SIGPIPE, SIG_IGN);
1.1       jfb       229:
1.10      jfb       230:        if (!cvsd_fg && daemon(0, 0) == -1) {
1.1       jfb       231:                cvs_log(LP_ERRNO, "failed to become a daemon");
                    232:                exit(EX_OSERR);
                    233:        }
                    234:
                    235:        if (cvsd_sock_open() < 0) {
                    236:                exit(1);
                    237:        }
                    238:
                    239:        if (setegid(cvsd_gid) == -1) {
                    240:                cvs_log(LP_ERRNO, "failed to drop group privileges");
                    241:                exit(EX_OSERR);
                    242:        }
                    243:        if (seteuid(cvsd_uid) == -1) {
                    244:                cvs_log(LP_ERRNO, "failed to drop user privileges");
                    245:                exit(EX_OSERR);
                    246:        }
                    247:
                    248:        if (checkrepo && cvsd_checkperms("/") != 0) {
                    249:                cvs_log(LP_ERR,
                    250:                    "exiting due to permission errors on repository");
                    251:                exit(1);
                    252:        }
                    253:
                    254:        /* spawn the initial pool of children */
1.11      jfb       255:        for (i = 0; i < (u_int)cvsd_chmin; i++)
                    256:                if (cvsd_child_fork(NULL) < 0)
1.1       jfb       257:                        exit(EX_OSERR);
                    258:
1.8       jfb       259:        signal(SIGINFO, cvsd_sighdlr);
1.3       jfb       260:        cvsd_parent_loop();
1.1       jfb       261:
                    262:        cvs_log(LP_NOTICE, "shutting down");
                    263:        cvs_log_cleanup();
                    264:
                    265:        cvsd_sock_close();
                    266:
                    267:        return (0);
                    268: }
                    269:
                    270:
                    271: /*
                    272:  * cvsd_privdrop()
                    273:  *
                    274:  * Drop privileges.
                    275:  */
                    276: int
                    277: cvsd_privdrop(void)
                    278: {
1.8       jfb       279:        cvs_log(LP_INFO, "dropping privileges to %s[%d]:%s[%d]",
                    280:            cvsd_user, cvsd_uid, cvsd_group, cvsd_gid);
1.2       jfb       281:        if (setgid(cvsd_gid) == -1) {
1.1       jfb       282:                cvs_log(LP_ERRNO, "failed to drop group privileges to %s",
                    283:                    CVSD_GROUP);
                    284:                return (-1);
                    285:        }
                    286:
1.2       jfb       287:        if (setuid(cvsd_uid) == -1) {
1.1       jfb       288:                cvs_log(LP_ERRNO, "failed to drop user privileges to %s",
                    289:                    CVSD_USER);
                    290:                return (-1);
                    291:        }
                    292:
                    293:        return (0);
                    294: }
                    295:
                    296:
                    297: /*
                    298:  * cvsd_checkperms()
                    299:  *
                    300:  * Check permissions on the CVS repository and log warnings for any
                    301:  * weird of loose permissions.
                    302:  * Returns the number of warnings on success, or -1 on failure.
                    303:  */
                    304: int
                    305: cvsd_checkperms(const char *path)
                    306: {
                    307:        int fd, nbwarn, ret;
                    308:        mode_t fmode;
                    309:        long base;
1.15      xsa       310:        u_char *dp, *endp;
1.1       jfb       311:        char buf[1024], spath[MAXPATHLEN];
                    312:        struct stat st;
                    313:        struct dirent *dep;
                    314:
                    315:        nbwarn = 0;
                    316:
                    317:        cvs_log(LP_DEBUG, "checking permissions on `%s'", path);
                    318:
                    319:        if (stat(path, &st) == -1) {
                    320:                cvs_log(LP_ERRNO, "failed to stat `%s'", path);
                    321:                return (-1);
                    322:        }
                    323:
                    324:        if (S_ISDIR(st.st_mode))
                    325:                fmode = CVSD_DPERM;
                    326:        else
                    327:                fmode = CVSD_FPERM;
                    328:
                    329:        if (st.st_uid != cvsd_uid) {
                    330:                cvs_log(LP_WARN, "owner of `%s' is not %s", path, CVSD_USER);
                    331:                nbwarn++;
                    332:        }
                    333:
                    334:        if (st.st_gid != cvsd_gid) {
                    335:                cvs_log(LP_WARN, "group of `%s' is not %s", path, CVSD_GROUP);
                    336:                nbwarn++;
                    337:        }
                    338:
                    339:        if (st.st_mode & S_IWGRP) {
                    340:                cvs_log(LP_WARN, "file `%s' is group-writable", path,
                    341:                    fmode);
                    342:                nbwarn++;
                    343:        }
                    344:
                    345:        if (st.st_mode & S_IWOTH) {
                    346:                cvs_log(LP_WARN, "file `%s' is world-writable", path,
                    347:                    fmode);
                    348:                nbwarn++;
                    349:        }
                    350:
                    351:        if (S_ISDIR(st.st_mode)) {
                    352:                fd = open(path, O_RDONLY, 0);
                    353:                if (fd == -1) {
                    354:                        cvs_log(LP_ERRNO, "failed to open directory `%s'",
                    355:                            path);
                    356:                        return (nbwarn);
                    357:                }
                    358:                /* recurse */
                    359:                ret = getdirentries(fd, buf, sizeof(buf), &base);
                    360:                if (ret == -1) {
                    361:                        cvs_log(LP_ERRNO,
                    362:                            "failed to get directory entries for `%s'", path);
                    363:                        (void)close(fd);
                    364:                        return (nbwarn);
                    365:                }
                    366:
                    367:                dp = buf;
                    368:                endp = buf + ret;
                    369:
                    370:                while (dp < endp) {
                    371:                        dep = (struct dirent *)dp;
                    372:                        dp += dep->d_reclen;
                    373:
                    374:                        if ((dep->d_namlen == 1) && (dep->d_name[0] == '.'))
                    375:                                continue;
                    376:                        if ((dep->d_namlen == 2) && (dep->d_name[0] == '.') &&
                    377:                            (dep->d_name[1] == '.'))
                    378:                                continue;
                    379:
                    380:                        /* skip the CVSROOT directory */
                    381:                        if (strcmp(dep->d_name, CVS_PATH_ROOT) == 0)
                    382:                                continue;
                    383:
                    384:                        snprintf(spath, sizeof(spath), "%s/%s", path,
                    385:                            dep->d_name);
                    386:                        ret = cvsd_checkperms(spath);
                    387:                        if (ret == -1)
                    388:                                nbwarn++;
                    389:                        else
                    390:                                nbwarn += ret;
                    391:                }
                    392:                (void)close(fd);
                    393:        }
                    394:
                    395:
                    396:        return (nbwarn);
                    397: }
                    398:
                    399:
                    400: /*
1.3       jfb       401:  * cvsd_child_fork()
1.1       jfb       402:  *
                    403:  * Fork a child process which chroots to the CVS repository's root directory.
1.3       jfb       404:  * If the <chpp> argument is not NULL, a reference to the newly created child
                    405:  * structure will be returned.
                    406:  * On success, returns 0 in the child process context, 1 in the parent's
                    407:  * context, or -1 on failure.
1.1       jfb       408:  */
                    409: int
1.3       jfb       410: cvsd_child_fork(struct cvsd_child **chpp)
1.1       jfb       411: {
                    412:        int svec[2];
                    413:        pid_t pid;
                    414:        struct cvsd_child *chp;
                    415:
1.3       jfb       416:        if (cvsd_chnum == cvsd_chmax) {
                    417:                cvs_log(LP_WARN, "child pool reached limit of processes");
                    418:                return (-1);
                    419:        }
                    420:
1.1       jfb       421:        if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, svec) == -1) {
                    422:                cvs_log(LP_ERRNO, "failed to create socket pair");
                    423:                return (-1);
                    424:        }
                    425:
                    426:        /*
                    427:         * We need to temporarily regain original privileges in order for the
                    428:         * child to chroot().
                    429:         */
                    430:        if (seteuid(0) == -1) {
                    431:                cvs_log(LP_ERRNO, "failed to regain privileges");
                    432:                return (-1);
                    433:        }
                    434:
                    435:        pid = fork();
                    436:        if (pid == -1) {
                    437:                cvs_log(LP_ERRNO, "failed to fork child");
                    438:                (void)close(svec[0]);
                    439:                (void)close(svec[1]);
                    440:                return (-1);
                    441:        }
                    442:
                    443:        if (pid == 0) {
                    444:                cvsd_privfd = svec[1];
                    445:                (void)close(svec[0]);
                    446:
1.11      jfb       447:                cvsd_child_main();
                    448:                /* NOTREACHED */
1.1       jfb       449:        }
                    450:
                    451:        cvs_log(LP_INFO, "spawning child %d", pid);
                    452:
                    453:        if (seteuid(cvsd_uid) == -1) {
                    454:                cvs_log(LP_ERRNO, "failed to redrop privs");
                    455:                return (-1);
                    456:        }
                    457:
                    458:        chp = (struct cvsd_child *)malloc(sizeof(*chp));
                    459:        if (chp == NULL) {
                    460:                cvs_log(LP_ERRNO, "failed to allocate child data");
                    461:                return (-1);
                    462:        }
                    463:
                    464:        chp->ch_pid = pid;
                    465:        chp->ch_sock = svec[0];
1.3       jfb       466:        chp->ch_state = CVSD_ST_IDLE;
                    467:
1.7       jfb       468:        signal(SIGCHLD, SIG_IGN);
1.3       jfb       469:        TAILQ_INSERT_TAIL(&cvsd_children, chp, ch_list);
1.7       jfb       470:        cvsd_chnum++;
1.8       jfb       471:        signal(SIGCHLD, cvsd_sighdlr);
1.3       jfb       472:
                    473:        if (chpp != NULL)
                    474:                *chpp = chp;
1.1       jfb       475:        (void)close(svec[1]);
1.3       jfb       476:        return (1);
                    477: }
                    478:
                    479:
                    480: /*
                    481:  * cvsd_child_reap()
                    482:  *
1.7       jfb       483:  * Wait for a child's status and perform the proper actions depending on it.
                    484:  * If the child has exited or has been terminated by a signal, it will be
                    485:  * removed from the list and new children will be created until the pool has
                    486:  * at least <cvsd_chmin> children in it.
1.3       jfb       487:  * Returns 0 on success, or -1 on failure.
                    488:  */
                    489: int
1.7       jfb       490: cvsd_child_reap(void)
1.3       jfb       491: {
1.7       jfb       492:        pid_t pid;
                    493:        int status;
                    494:        struct cvsd_child *ch;
1.3       jfb       495:
1.7       jfb       496:        pid = wait(&status);
                    497:        if (pid == -1) {
                    498:                cvs_log(LP_ERRNO, "failed to wait for child");
                    499:                return (-1);
                    500:        }
                    501:
                    502:        TAILQ_FOREACH(ch, &cvsd_children, ch_list) {
                    503:                if (ch->ch_pid == pid) {
                    504:                        if (WIFEXITED(status)) {
                    505:                                cvs_log(LP_WARN,
                    506:                                    "child %d exited with status %d",
                    507:                                    pid, WEXITSTATUS(status));
1.13      deraadt   508:                        } else if (WIFSIGNALED(status)) {
1.7       jfb       509:                                cvs_log(LP_WARN,
                    510:                                    "child %d terminated with signal %d",
                    511:                                    pid, WTERMSIG(status));
1.13      deraadt   512:                        } else {
1.7       jfb       513:                                cvs_log(LP_ERR, "HOLY SHIT!");
                    514:                        }
                    515:
                    516:                        signal(SIGCHLD, SIG_IGN);
                    517:                        TAILQ_REMOVE(&cvsd_children, ch, ch_list);
                    518:                        cvsd_chnum--;
1.8       jfb       519:                        signal(SIGCHLD, cvsd_sighdlr);
1.7       jfb       520:
                    521:                        break;
                    522:                }
                    523:        }
                    524:
                    525:        while (cvsd_chnum < cvsd_chmin)
                    526:                cvsd_child_fork(NULL);
1.3       jfb       527:
                    528:        return (0);
                    529: }
                    530:
                    531:
                    532: /*
                    533:  * cvsd_child_get()
                    534:  *
                    535:  * Find a child process in idle state and return a pointer to the child's
                    536:  * structure.  If there are no available child processes, a new one will be
                    537:  * created unless the number of children has attained the maximum, in which
                    538:  * case NULL is returned.
                    539:  */
                    540: struct cvsd_child*
                    541: cvsd_child_get(void)
                    542: {
                    543:        struct cvsd_child *chp;
1.1       jfb       544:
1.3       jfb       545:        TAILQ_FOREACH(chp, &cvsd_children, ch_list)
                    546:                if (chp->ch_state == CVSD_ST_IDLE)
                    547:                        return (chp);
                    548:
                    549:        /* no available child, attempt to fork a new one */
                    550:        chp = NULL;
                    551:        if ((cvsd_chnum < cvsd_chmax) && (cvsd_child_fork(&chp) < 0))
                    552:                return (NULL);
                    553:
                    554:        return (chp);
1.1       jfb       555: }
                    556:
                    557:
1.3       jfb       558:
1.1       jfb       559: /*
                    560:  * cvsd_parent_loop()
                    561:  *
                    562:  * Main loop of the parent cvsd process, which listens on its end of the
1.3       jfb       563:  * local socket for requests from the cvs(1) program and on any outstanding
                    564:  * messages from the children.
1.1       jfb       565:  */
                    566: static void
                    567: cvsd_parent_loop(void)
                    568: {
1.3       jfb       569:        int cfd, timeout, ret;
1.1       jfb       570:        nfds_t nfds, i;
                    571:        struct pollfd *pfd;
                    572:        struct cvsd_child *chp;
                    573:
                    574:        nfds = 0;
                    575:        timeout = INFTIM;
1.3       jfb       576:        pfd = NULL;
1.1       jfb       577:
                    578:        for (;;) {
1.10      jfb       579:                if (!cvsd_running)
1.3       jfb       580:                        break;
                    581:
1.10      jfb       582:                if (cvsd_restart) {
1.3       jfb       583:                        /* restart server */
                    584:                }
1.7       jfb       585:
                    586:                if (cvsd_sigchld) {
                    587:                        cvsd_sigchld = 0;
                    588:                        cvsd_child_reap();
                    589:                }
1.8       jfb       590:                if (cvsd_siginfo) {
                    591:                        cvsd_siginfo = 0;
                    592:                        cvsd_report();
                    593:                }
1.7       jfb       594:
1.3       jfb       595:                nfds = cvsd_chnum + 1;
1.1       jfb       596:                pfd = (struct pollfd *)realloc(pfd,
                    597:                    nfds * sizeof(struct pollfd));
                    598:                if (pfd == NULL) {
                    599:                        cvs_log(LP_ERRNO, "failed to reallocate polling data");
                    600:                        return;
                    601:                }
                    602:
1.3       jfb       603:                pfd[0].fd = cvsd_sock;
                    604:                pfd[0].events = POLLIN;
                    605:                pfd[0].revents = 0;
                    606:                i = 1;
1.1       jfb       607:                TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
                    608:                        pfd[i].fd = chp->ch_sock;
                    609:                        pfd[i].events = POLLIN;
                    610:                        pfd[i].revents = 0;
                    611:                        i++;
                    612:
1.3       jfb       613:                        if (i == nfds)   /* just a precaution */
1.1       jfb       614:                                break;
                    615:                }
                    616:
                    617:                ret = poll(pfd, nfds, timeout);
                    618:                if (ret == -1) {
1.7       jfb       619:                        if (errno == EINTR)
                    620:                                continue;
1.1       jfb       621:                        cvs_log(LP_ERRNO, "poll error");
                    622:                        break;
                    623:                }
                    624:
1.3       jfb       625:                if (pfd[0].revents & (POLLERR|POLLNVAL)) {
                    626:                        cvs_log(LP_ERR, "poll error on request socket");
1.13      deraadt   627:                } else if (pfd[0].revents & POLLIN) {
1.11      jfb       628:                        uid_t uid;
                    629:                        gid_t gid;
1.13      deraadt   630:
1.3       jfb       631:                        cfd = cvsd_sock_accept(pfd[0].fd);
                    632:                        if (cfd == -1)
                    633:                        chp = cvsd_child_get();
                    634:                        if (chp == NULL) {
                    635:                                cvs_log(LP_ALERT,
                    636:                                    "request queue not implemented");
                    637:                                break;
                    638:                        }
                    639:
1.11      jfb       640:                        if (getpeereid(cfd, &uid, &gid) < 0)
                    641:                                err(1, "failed to get UID");
1.3       jfb       642:                        if (cvsd_sendmsg(chp->ch_sock, CVSD_MSG_PASSFD,
                    643:                            &cfd, sizeof(cfd)) < 0)
                    644:                                break;
                    645:
                    646:                        /* mark the child as busy */
                    647:                        chp->ch_state = CVSD_ST_BUSY;
                    648:                }
                    649:
1.1       jfb       650:                chp = TAILQ_FIRST(&cvsd_children);
1.3       jfb       651:                for (i = 1; i < nfds; i++) {
1.1       jfb       652:                        if (pfd[i].revents & (POLLERR|POLLNVAL)) {
                    653:                                cvs_log(LP_ERR,
                    654:                                    "poll error on child socket (PID %d)",
                    655:                                    chp->ch_pid);
1.13      deraadt   656:                        } else if (pfd[i].revents & POLLIN)
1.1       jfb       657:                                cvsd_msghdlr(chp, pfd[i].fd);
                    658:
                    659:                        chp = TAILQ_NEXT(chp, ch_list);
                    660:                }
                    661:
                    662:        }
1.7       jfb       663:
                    664:        /* broadcast a shutdown message to children */
                    665:        TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
                    666:                (void)cvsd_sendmsg(chp->ch_sock, CVSD_MSG_SHUTDOWN, NULL, 0);
                    667:        }
1.1       jfb       668: }
                    669:
                    670:
                    671: /*
1.11      jfb       672:  * cvsd_child_main()
1.1       jfb       673:  *
                    674:  */
                    675: static void
1.11      jfb       676: cvsd_child_main(void)
1.1       jfb       677: {
                    678:        int ret, timeout;
1.7       jfb       679:        u_int mtype;
                    680:        size_t mlen;
                    681:        char mbuf[CVSD_MSG_MAXLEN];
1.1       jfb       682:        struct pollfd pfd[1];
1.11      jfb       683:        struct cvsd_sess *sessp;
                    684:
                    685:        cvs_log(LP_INFO, "changing root to %s", cvsd_root);
                    686:        if (chroot(cvsd_root) == -1) {
                    687:                cvs_log(LP_ERRNO, "failed to chroot to `%s'", cvsd_root);
                    688:                exit(EX_OSERR);
                    689:        }
                    690:        (void)chdir("/");
                    691:
                    692:        if (cvsd_privdrop() < 0)
                    693:                exit(EX_OSERR);
                    694:
                    695:        setproctitle("%s [child %d]", __progname, getpid());
1.1       jfb       696:
                    697:        pfd[0].fd = cvsd_privfd;
                    698:        pfd[0].events = POLLIN;
                    699:        timeout = INFTIM;
1.11      jfb       700:        sessp = NULL;
1.1       jfb       701:
1.10      jfb       702:        while (cvsd_running) {
1.1       jfb       703:                ret = poll(pfd, 1, timeout);
                    704:                if (ret == -1) {
1.7       jfb       705:                        if (errno == EINTR)
                    706:                                continue;
                    707:                        cvs_log(LP_ERRNO, "poll error");
1.1       jfb       708:                        break;
1.13      deraadt   709:                } else if (ret == 0)
1.7       jfb       710:                        continue;
1.1       jfb       711:
1.3       jfb       712:                if (pfd[0].revents & (POLLERR|POLLNVAL)) {
                    713:                        cvs_log(LP_ERR, "poll error");
                    714:                        break;
                    715:                }
1.7       jfb       716:
                    717:                mlen = sizeof(mbuf);
                    718:                ret = cvsd_recvmsg(pfd[0].fd, &mtype, mbuf, &mlen);
                    719:                if (ret == -1) {
                    720:                        continue;
1.13      deraadt   721:                } else if (ret == 0)
1.7       jfb       722:                        break;
                    723:
                    724:                switch (mtype) {
1.8       jfb       725:                case CVSD_MSG_PASSFD:
1.11      jfb       726:                        sessp = cvsd_sess_alloc(*(int *)mbuf);
1.8       jfb       727:                        break;
1.7       jfb       728:                case CVSD_MSG_SHUTDOWN:
1.10      jfb       729:                        cvsd_running = 0;
1.7       jfb       730:                        break;
                    731:                default:
                    732:                        cvs_log(LP_ERR,
1.11      jfb       733:                            "unexpected message type %u from parent", mtype);
1.7       jfb       734:                        break;
                    735:                }
                    736:
1.1       jfb       737:        }
                    738:
                    739:        exit(0);
                    740: }
                    741:
                    742:
                    743: /*
                    744:  * cvsd_msghdlr()
1.3       jfb       745:  *
                    746:  * Handler for messages received from child processes.
                    747:  * Returns 0 on success, or -1 on failure.
1.1       jfb       748:  */
                    749: int
                    750: cvsd_msghdlr(struct cvsd_child *child, int fd)
                    751: {
                    752:        uid_t uid;
                    753:        ssize_t ret;
                    754:        char rbuf[CVSD_MSG_MAXLEN];
                    755:        struct group *gr;
                    756:        struct passwd *pw;
                    757:        struct iovec iov[2];
                    758:        struct cvsd_msg msg;
                    759:
                    760:        ret = read(fd, &msg, sizeof(msg));
                    761:        if (ret == -1) {
                    762:                cvs_log(LP_ERRNO, "failed to read CVS message");
                    763:                return (-1);
1.13      deraadt   764:        } else if (ret == 0) {
1.1       jfb       765:                cvs_log(LP_WARN, "child closed socket pair");
                    766:                return (0);
                    767:        }
                    768:
                    769:        if (msg.cm_len > 0) {
                    770:                ret = read(fd, rbuf, msg.cm_len);
                    771:                if (ret != (ssize_t)msg.cm_len) {
                    772:                        cvs_log(LP_ERR, "failed to read entire msg");
                    773:                        return (-1);
                    774:                }
                    775:        }
                    776:
                    777:        /* setup the I/O vector for the reply */
                    778:        iov[0].iov_base = &msg;
                    779:        iov[0].iov_len = sizeof(msg);
                    780:
                    781:        msg.cm_type = CVSD_MSG_ERROR;
                    782:        msg.cm_len = 0;
                    783:
                    784:        switch (msg.cm_type) {
                    785:        case CVSD_MSG_GETUID:
                    786:                rbuf[ret] = '\0';
1.14      tedu      787:                cvs_log(LP_INFO, "getting UID for `%s'", rbuf);
1.1       jfb       788:
                    789:                pw = getpwnam(rbuf);
                    790:                if (pw != NULL) {
                    791:                        msg.cm_type = CVSD_MSG_UID;
                    792:                        msg.cm_len = sizeof(uid_t);
                    793:                        iov[1].iov_len = msg.cm_len;
                    794:                        iov[1].iov_base = &(pw->pw_uid);
                    795:                }
                    796:                break;
                    797:        case CVSD_MSG_GETUNAME:
                    798:                memcpy(&uid, rbuf, sizeof(uid));
1.14      tedu      799:                cvs_log(LP_INFO, "getting username for UID %u", uid);
1.1       jfb       800:                pw = getpwuid(uid);
                    801:                if (pw != NULL) {
                    802:                        msg.cm_type = CVSD_MSG_UNAME;
                    803:                        msg.cm_len = strlen(pw->pw_name);
                    804:                        iov[1].iov_len = msg.cm_len;
                    805:                        iov[1].iov_base = pw->pw_name;
                    806:                }
                    807:                break;
1.3       jfb       808:        case CVSD_MSG_GETGID:
                    809:                rbuf[ret] = '\0';
1.14      tedu      810:                cvs_log(LP_INFO, "getting GID for `%s'", rbuf);
1.3       jfb       811:
                    812:                gr = getgrnam(rbuf);
                    813:                if (gr != NULL) {
                    814:                        msg.cm_type = CVSD_MSG_GID;
                    815:                        msg.cm_len = sizeof(gid_t);
                    816:                        iov[1].iov_len = msg.cm_len;
                    817:                        iov[1].iov_base = &(gr->gr_gid);
                    818:                }
                    819:                break;
                    820:        case CVSD_MSG_SETIDLE:
                    821:                child->ch_state = CVSD_ST_IDLE;
                    822:                break;
1.1       jfb       823:        default:
                    824:                cvs_log(LP_ERR, "unknown command type %u", msg.cm_type);
                    825:                return (-1);
                    826:        }
                    827:
                    828:        ret = writev(fd, iov, 2);
                    829:
                    830:        return (ret);
1.3       jfb       831: }
                    832:
                    833:
                    834: /*
                    835:  * cvsd_set()
                    836:  *
                    837:  * Generic interface to set some of the parameters of the cvs server.
1.7       jfb       838:  * When a string is set using cvsd_set(), the original string is copied into
                    839:  * a new buffer.
1.3       jfb       840:  * Returns 0 on success, or -1 on failure.
                    841:  */
                    842: int
                    843: cvsd_set(int what, ...)
                    844: {
                    845:        char *str;
1.12      pat       846:        int error = 0;
1.3       jfb       847:        va_list vap;
                    848:
1.9       jfb       849:        str = NULL;
                    850:
1.3       jfb       851:        va_start(vap, what);
                    852:
1.9       jfb       853:        if ((what == CVSD_SET_ROOT) || (what == CVSD_SET_SOCK) ||
1.11      jfb       854:            (what == CVSD_SET_USER) || (what == CVSD_SET_GROUP) ||
                    855:            (what == CVSD_SET_MODDIR)) {
1.7       jfb       856:                str = strdup(va_arg(vap, char *));
                    857:                if (str == NULL) {
1.9       jfb       858:                        cvs_log(LP_ERRNO, "failed to set string");
1.12      pat       859:                        va_end(vap);
1.7       jfb       860:                        return (-1);
                    861:                }
1.9       jfb       862:        }
                    863:
                    864:        switch (what) {
                    865:        case CVSD_SET_ROOT:
1.7       jfb       866:                if (cvsd_root != NULL)
                    867:                        free(cvsd_root);
1.3       jfb       868:                cvsd_root = str;
                    869:                break;
1.9       jfb       870:        case CVSD_SET_SOCK:
                    871:                if (cvsd_sock_path != NULL)
                    872:                        free(cvsd_sock_path);
                    873:                cvsd_sock_path = str;
                    874:                break;
                    875:        case CVSD_SET_USER:
                    876:                if (cvsd_user != NULL)
                    877:                        free(cvsd_user);
                    878:                cvsd_user = str;
                    879:                break;
                    880:        case CVSD_SET_GROUP:
                    881:                if (cvsd_group != NULL)
                    882:                        free(cvsd_group);
                    883:                cvsd_group = str;
1.11      jfb       884:                break;
                    885:        case CVSD_SET_MODDIR:
                    886:                if (cvsd_moddir != NULL)
                    887:                        free(cvsd_moddir);
                    888:                cvsd_moddir = str;
1.9       jfb       889:                break;
1.3       jfb       890:        case CVSD_SET_CHMIN:
                    891:                cvsd_chmin = va_arg(vap, int);
                    892:                /* we should increase the number of children accordingly */
                    893:                break;
                    894:        case CVSD_SET_CHMAX:
                    895:                cvsd_chmax = va_arg(vap, int);
                    896:                /* we should decrease the number of children accordingly */
                    897:                break;
                    898:        case CVSD_SET_ADDR:
1.7       jfb       899:                /* this is more like an add than a set */
1.3       jfb       900:                break;
                    901:        default:
                    902:                cvs_log(LP_ERR, "invalid field to set");
1.12      pat       903:                error = -1;
                    904:                break;
1.3       jfb       905:        }
                    906:
                    907:        va_end(vap);
                    908:
1.12      pat       909:        return (error);
1.8       jfb       910: }
                    911:
                    912:
                    913: /*
                    914:  * cvsd_report()
                    915:  */
                    916: static void
                    917: cvsd_report(void)
                    918: {
                    919:        u_int nb_idle, nb_busy, nb_unknown;
                    920:        struct cvsd_child *ch;
                    921:
                    922:        nb_idle = 0;
                    923:        nb_busy = 0;
                    924:        nb_unknown = 0;
                    925:
                    926:        signal(SIGCHLD, SIG_IGN);
                    927:        TAILQ_FOREACH(ch, &cvsd_children, ch_list) {
                    928:                if (ch->ch_state == CVSD_ST_IDLE)
                    929:                        nb_idle++;
                    930:                else if (ch->ch_state == CVSD_ST_BUSY)
                    931:                        nb_busy++;
                    932:                else if (ch->ch_state == CVSD_ST_UNKNOWN)
                    933:                        nb_unknown++;
                    934:        }
                    935:
                    936:        cvs_log(LP_WARN, "%u children, %u idle, %u busy, %u unknown",
                    937:            cvsd_chnum, nb_idle, nb_busy, nb_unknown);
                    938:
                    939:        TAILQ_FOREACH(ch, &cvsd_children, ch_list)
                    940:                cvs_log(LP_WARN, "");
                    941:        signal(SIGCHLD, cvsd_sighdlr);
1.1       jfb       942: }