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

1.18    ! jfb         1: /*     $OpenBSD: cvsd.c,v 1.17 2005/02/15 15:17:34 jfb 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"
1.18    ! jfb        49: #include "repo.h"
1.1       jfb        50: #include "cvsd.h"
                     51:
                     52:
                     53: static void  cvsd_parent_loop (void);
1.8       jfb        54: static void  cvsd_report      (void);
1.1       jfb        55:
                     56:
                     57: extern char *__progname;
                     58:
                     59:
1.11      jfb        60: int    cvsd_fg = 0;
1.18    ! jfb        61: uid_t  cvsd_uid = 0;
        !            62: gid_t  cvsd_gid = 0;
1.1       jfb        63:
1.10      jfb        64: volatile sig_atomic_t cvsd_running = 1;
                     65: volatile sig_atomic_t cvsd_restart = 0;
1.1       jfb        66:
1.9       jfb        67: static char  *cvsd_user = NULL;
                     68: static char  *cvsd_group = NULL;
1.7       jfb        69: static char  *cvsd_root = NULL;
1.18    ! jfb        70: static char  *cvsd_conffile = CVSD_PATH_CONF;
1.11      jfb        71: static char  *cvsd_moddir = NULL;
1.1       jfb        72: static int    cvsd_privfd = -1;
                     73:
1.18    ! jfb        74: static CVSREPO *cvsd_repo;
        !            75:
1.1       jfb        76:
                     77: static TAILQ_HEAD(,cvsd_child) cvsd_children;
                     78: static volatile sig_atomic_t   cvsd_chnum = 0;
                     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.18    ! jfb        84: void   usage    (void);
1.8       jfb        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: {
1.18    ! jfb       144:        int ret, repo_flags;
1.1       jfb       145:        struct passwd *pwd;
                    146:        struct group *grp;
                    147:
1.18    ! jfb       148:        repo_flags = 0;
1.17      jfb       149:        cvsd_set(CVSD_SET_SOCK, CVSD_SOCK_PATH);
1.9       jfb       150:        cvsd_set(CVSD_SET_USER, CVSD_USER);
                    151:        cvsd_set(CVSD_SET_GROUP, CVSD_GROUP);
1.1       jfb       152:
                    153:        if (cvs_log_init(LD_STD|LD_SYSLOG, LF_PID) < 0)
                    154:                err(1, "failed to initialize logging mechanism");
                    155:
1.18    ! jfb       156:        while ((ret = getopt(argc, argv, "c:dfg:hpr:s:u:v")) != -1) {
1.1       jfb       157:                switch (ret) {
1.3       jfb       158:                case 'c':
                    159:                        cvsd_conffile = optarg;
1.1       jfb       160:                        break;
                    161:                case 'd':
                    162:                        cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
                    163:                        cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
                    164:                        break;
                    165:                case 'f':
1.10      jfb       166:                        cvsd_fg = 1;
1.6       jfb       167:                        break;
1.8       jfb       168:                case 'g':
1.9       jfb       169:                        cvsd_set(CVSD_SET_GROUP, optarg);
1.8       jfb       170:                        break;
1.6       jfb       171:                case 'h':
                    172:                        usage();
                    173:                        exit(0);
                    174:                        /* NOTREACHED */
1.1       jfb       175:                        break;
                    176:                case 'p':
1.18    ! jfb       177:                        repo_flags |= CVS_REPO_CHKPERM;
1.1       jfb       178:                        break;
                    179:                case 'r':
1.17      jfb       180:                        cvsd_set(CVSD_SET_ROOT, optarg);
1.1       jfb       181:                        break;
                    182:                case 's':
1.17      jfb       183:                        cvsd_set(CVSD_SET_SOCK, optarg);
1.1       jfb       184:                        break;
1.8       jfb       185:                case 'u':
1.9       jfb       186:                        cvsd_set(CVSD_SET_USER, optarg);
1.8       jfb       187:                        break;
1.1       jfb       188:                case 'v':
                    189:                        cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
                    190:                        break;
                    191:                default:
                    192:                        usage();
                    193:                        exit(EX_USAGE);
                    194:                }
                    195:        }
                    196:
                    197:        argc -= optind;
                    198:        argv += optind;
                    199:
1.3       jfb       200:        if (cvs_conf_read(cvsd_conffile) < 0)
                    201:                errx(1, "error parsing configuration file `%s'", cvsd_conffile);
1.1       jfb       202:
                    203:        if (cvsd_root == NULL)
                    204:                errx(1, "no CVS root directory specified");
                    205:
                    206:        if (argc > 0)
                    207:                errx(EX_USAGE, "unrecognized trailing arguments");
                    208:
                    209:        TAILQ_INIT(&cvsd_children);
                    210:
1.8       jfb       211:        pwd = getpwnam(cvsd_user);
1.1       jfb       212:        if (pwd == NULL)
1.8       jfb       213:                err(EX_NOUSER, "failed to get user `%s'", cvsd_user);
1.1       jfb       214:
1.8       jfb       215:        grp = getgrnam(cvsd_group);
1.1       jfb       216:        if (grp == NULL)
1.8       jfb       217:                err(EX_NOUSER, "failed to get group `%s'", cvsd_group);
1.1       jfb       218:
1.18    ! jfb       219:        endpwent();
        !           220:        endgrent();
        !           221:
1.1       jfb       222:        cvsd_uid = pwd->pw_uid;
                    223:        cvsd_gid = grp->gr_gid;
                    224:
1.8       jfb       225:        signal(SIGHUP, cvsd_sighdlr);
                    226:        signal(SIGINT, cvsd_sighdlr);
                    227:        signal(SIGQUIT, cvsd_sighdlr);
                    228:        signal(SIGTERM, cvsd_sighdlr);
                    229:        signal(SIGCHLD, cvsd_sighdlr);
1.11      jfb       230:        signal(SIGPIPE, SIG_IGN);
1.1       jfb       231:
1.10      jfb       232:        if (!cvsd_fg && daemon(0, 0) == -1) {
1.1       jfb       233:                cvs_log(LP_ERRNO, "failed to become a daemon");
                    234:                exit(EX_OSERR);
                    235:        }
                    236:
1.18    ! jfb       237:        if ((cvsd_repo = cvs_repo_load(cvsd_root, repo_flags)) == NULL) {
        !           238:                cvs_log(LP_ERR, "failed to load repository");
        !           239:                exit(EX_OSERR);
        !           240:        };
        !           241:
1.1       jfb       242:        if (cvsd_sock_open() < 0) {
                    243:                exit(1);
                    244:        }
                    245:
                    246:        if (setegid(cvsd_gid) == -1) {
                    247:                cvs_log(LP_ERRNO, "failed to drop group privileges");
                    248:                exit(EX_OSERR);
                    249:        }
                    250:        if (seteuid(cvsd_uid) == -1) {
                    251:                cvs_log(LP_ERRNO, "failed to drop user privileges");
                    252:                exit(EX_OSERR);
                    253:        }
                    254:
1.8       jfb       255:        signal(SIGINFO, cvsd_sighdlr);
1.3       jfb       256:        cvsd_parent_loop();
1.1       jfb       257:
                    258:        cvsd_sock_close();
                    259:
1.18    ! jfb       260:        cvs_repo_free(cvsd_repo);
1.1       jfb       261:
1.18    ! jfb       262:        cvs_log(LP_NOTICE, "shutting down");
        !           263:        cvs_log_cleanup();
1.1       jfb       264:        return (0);
                    265: }
                    266:
                    267:
                    268: /*
1.3       jfb       269:  * cvsd_child_fork()
1.1       jfb       270:  *
1.18    ! jfb       271:  * Fork a child process which chroots to the CVS repository's root directory,
        !           272:  * drops all privileges, and then executes the cvsd-child process, which will
        !           273:  * handle the incoming CVS requests.
        !           274:  * On success, returns a pointer to the new child structure,
        !           275:  * or NULL on failure.
1.1       jfb       276:  */
1.18    ! jfb       277: struct cvsd_child*
        !           278: cvsd_child_fork(int sock)
1.1       jfb       279: {
1.18    ! jfb       280:        int argc, svec[2];
1.1       jfb       281:        pid_t pid;
1.18    ! jfb       282:        char *argv[16], ubuf[8], gbuf[8];
1.1       jfb       283:        struct cvsd_child *chp;
                    284:
1.3       jfb       285:        if (cvsd_chnum == cvsd_chmax) {
                    286:                cvs_log(LP_WARN, "child pool reached limit of processes");
1.18    ! jfb       287:                return (NULL);
1.3       jfb       288:        }
                    289:
1.1       jfb       290:        if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, svec) == -1) {
                    291:                cvs_log(LP_ERRNO, "failed to create socket pair");
1.18    ! jfb       292:                return (NULL);
1.1       jfb       293:        }
                    294:
                    295:        /*
                    296:         * We need to temporarily regain original privileges in order for the
                    297:         * child to chroot().
                    298:         */
                    299:        if (seteuid(0) == -1) {
                    300:                cvs_log(LP_ERRNO, "failed to regain privileges");
1.18    ! jfb       301:                return (NULL);
1.1       jfb       302:        }
                    303:
                    304:        pid = fork();
                    305:        if (pid == -1) {
                    306:                cvs_log(LP_ERRNO, "failed to fork child");
                    307:                (void)close(svec[0]);
                    308:                (void)close(svec[1]);
1.18    ! jfb       309:                return (NULL);
1.1       jfb       310:        }
                    311:
                    312:        if (pid == 0) {
                    313:                cvsd_privfd = svec[1];
                    314:                (void)close(svec[0]);
                    315:
1.18    ! jfb       316:                /*
        !           317:                 * Move the accepted socket to descriptor 3, where the child
        !           318:                 * expects it to be.  This could become troublesome if the
        !           319:                 * descriptor is already taken, but then again, the child
        !           320:                 * shouldn't have access to other descriptors except the
        !           321:                 * connection and its side of the socket pair it shares with
        !           322:                 * the parent.
        !           323:                 */
        !           324:                if (dup2(sock, CVSD_CHILD_SOCKFD) == -1) {
        !           325:                        cvs_log(LP_ERRNO, "failed to dup child socket");
        !           326:                        exit(EX_OSERR);
        !           327:                }
        !           328:                (void)close(sock);
        !           329:
        !           330:                argc = 0;
        !           331:                argv[argc++] = CVSD_PATH_CHILD;
        !           332:                argv[argc++] = "-r";
        !           333:                argv[argc++] = cvsd_root;
        !           334:                if (cvsd_uid != 0) {
        !           335:                        snprintf(ubuf, sizeof(ubuf), "%d", cvsd_uid);
        !           336:                        argv[argc++] = "-u";
        !           337:                        argv[argc++] = ubuf;
        !           338:                }
        !           339:                if (cvsd_gid != 0) {
        !           340:                        snprintf(gbuf, sizeof(gbuf), "%d", cvsd_gid);
        !           341:                        argv[argc++] = "-g";
        !           342:                        argv[argc++] = gbuf;
        !           343:                }
        !           344:                argv[argc] = NULL;
        !           345:
        !           346:                execv(CVSD_PATH_CHILD, argv);
        !           347:                err(1, "FUCK");
        !           348:                exit(EX_OSERR);
1.1       jfb       349:        }
                    350:
                    351:        cvs_log(LP_INFO, "spawning child %d", pid);
                    352:
1.18    ! jfb       353:        (void)close(svec[1]);
        !           354:
        !           355:        if (seteuid(cvsd_uid) == -1)
1.1       jfb       356:                cvs_log(LP_ERRNO, "failed to redrop privs");
                    357:
                    358:        chp = (struct cvsd_child *)malloc(sizeof(*chp));
                    359:        if (chp == NULL) {
1.18    ! jfb       360:                /* XXX kill child */
1.1       jfb       361:                cvs_log(LP_ERRNO, "failed to allocate child data");
1.18    ! jfb       362:                return (NULL);
1.1       jfb       363:        }
                    364:
                    365:        chp->ch_pid = pid;
                    366:        chp->ch_sock = svec[0];
1.3       jfb       367:        chp->ch_state = CVSD_ST_IDLE;
                    368:
                    369:        TAILQ_INSERT_TAIL(&cvsd_children, chp, ch_list);
1.7       jfb       370:        cvsd_chnum++;
1.3       jfb       371:
1.18    ! jfb       372:        return (chp);
1.3       jfb       373: }
                    374:
                    375:
                    376: /*
                    377:  * cvsd_child_reap()
                    378:  *
1.7       jfb       379:  * Wait for a child's status and perform the proper actions depending on it.
                    380:  * If the child has exited or has been terminated by a signal, it will be
1.18    ! jfb       381:  * removed from the list.
1.3       jfb       382:  * Returns 0 on success, or -1 on failure.
                    383:  */
                    384: int
1.7       jfb       385: cvsd_child_reap(void)
1.3       jfb       386: {
1.7       jfb       387:        pid_t pid;
                    388:        int status;
                    389:        struct cvsd_child *ch;
1.3       jfb       390:
1.7       jfb       391:        pid = wait(&status);
                    392:        if (pid == -1) {
                    393:                cvs_log(LP_ERRNO, "failed to wait for child");
                    394:                return (-1);
                    395:        }
                    396:
                    397:        TAILQ_FOREACH(ch, &cvsd_children, ch_list) {
                    398:                if (ch->ch_pid == pid) {
                    399:                        if (WIFEXITED(status)) {
                    400:                                cvs_log(LP_WARN,
                    401:                                    "child %d exited with status %d",
                    402:                                    pid, WEXITSTATUS(status));
1.13      deraadt   403:                        } else if (WIFSIGNALED(status)) {
1.7       jfb       404:                                cvs_log(LP_WARN,
                    405:                                    "child %d terminated with signal %d",
                    406:                                    pid, WTERMSIG(status));
1.13      deraadt   407:                        } else {
1.7       jfb       408:                                cvs_log(LP_ERR, "HOLY SHIT!");
                    409:                        }
                    410:
                    411:                        signal(SIGCHLD, SIG_IGN);
                    412:                        TAILQ_REMOVE(&cvsd_children, ch, ch_list);
                    413:                        cvsd_chnum--;
1.8       jfb       414:                        signal(SIGCHLD, cvsd_sighdlr);
1.7       jfb       415:
                    416:                        break;
                    417:                }
                    418:        }
                    419:
1.3       jfb       420:        return (0);
                    421: }
                    422:
                    423:
                    424: /*
1.1       jfb       425:  * cvsd_parent_loop()
                    426:  *
                    427:  * Main loop of the parent cvsd process, which listens on its end of the
1.3       jfb       428:  * local socket for requests from the cvs(1) program and on any outstanding
                    429:  * messages from the children.
1.1       jfb       430:  */
                    431: static void
                    432: cvsd_parent_loop(void)
                    433: {
1.3       jfb       434:        int cfd, timeout, ret;
1.1       jfb       435:        nfds_t nfds, i;
                    436:        struct pollfd *pfd;
                    437:        struct cvsd_child *chp;
                    438:
                    439:        nfds = 0;
                    440:        timeout = INFTIM;
1.3       jfb       441:        pfd = NULL;
1.1       jfb       442:
                    443:        for (;;) {
1.10      jfb       444:                if (!cvsd_running)
1.3       jfb       445:                        break;
                    446:
1.10      jfb       447:                if (cvsd_restart) {
1.3       jfb       448:                        /* restart server */
                    449:                }
1.7       jfb       450:
                    451:                if (cvsd_sigchld) {
                    452:                        cvsd_sigchld = 0;
                    453:                        cvsd_child_reap();
                    454:                }
1.8       jfb       455:                if (cvsd_siginfo) {
                    456:                        cvsd_siginfo = 0;
                    457:                        cvsd_report();
                    458:                }
1.7       jfb       459:
1.3       jfb       460:                nfds = cvsd_chnum + 1;
1.1       jfb       461:                pfd = (struct pollfd *)realloc(pfd,
                    462:                    nfds * sizeof(struct pollfd));
                    463:                if (pfd == NULL) {
                    464:                        cvs_log(LP_ERRNO, "failed to reallocate polling data");
                    465:                        return;
                    466:                }
                    467:
1.3       jfb       468:                pfd[0].fd = cvsd_sock;
                    469:                pfd[0].events = POLLIN;
                    470:                pfd[0].revents = 0;
                    471:                i = 1;
1.1       jfb       472:                TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
                    473:                        pfd[i].fd = chp->ch_sock;
                    474:                        pfd[i].events = POLLIN;
                    475:                        pfd[i].revents = 0;
                    476:                        i++;
                    477:
1.3       jfb       478:                        if (i == nfds)   /* just a precaution */
1.1       jfb       479:                                break;
                    480:                }
                    481:
                    482:                ret = poll(pfd, nfds, timeout);
                    483:                if (ret == -1) {
1.7       jfb       484:                        if (errno == EINTR)
                    485:                                continue;
1.1       jfb       486:                        cvs_log(LP_ERRNO, "poll error");
                    487:                        break;
                    488:                }
                    489:
1.3       jfb       490:                if (pfd[0].revents & (POLLERR|POLLNVAL)) {
                    491:                        cvs_log(LP_ERR, "poll error on request socket");
1.13      deraadt   492:                } else if (pfd[0].revents & POLLIN) {
1.11      jfb       493:                        uid_t uid;
                    494:                        gid_t gid;
1.13      deraadt   495:
1.18    ! jfb       496:                        if ((cfd = cvsd_sock_accept(pfd[0].fd)) == -1)
        !           497:                                continue;
        !           498:
        !           499:                        if ((chp = cvsd_child_fork(cfd)) == NULL) {
1.3       jfb       500:                                cvs_log(LP_ALERT,
                    501:                                    "request queue not implemented");
                    502:                                break;
                    503:                        }
                    504:
1.11      jfb       505:                        if (getpeereid(cfd, &uid, &gid) < 0)
                    506:                                err(1, "failed to get UID");
1.3       jfb       507:                        if (cvsd_sendmsg(chp->ch_sock, CVSD_MSG_PASSFD,
                    508:                            &cfd, sizeof(cfd)) < 0)
                    509:                                break;
                    510:
                    511:                        /* mark the child as busy */
                    512:                        chp->ch_state = CVSD_ST_BUSY;
                    513:                }
                    514:
1.1       jfb       515:                chp = TAILQ_FIRST(&cvsd_children);
1.3       jfb       516:                for (i = 1; i < nfds; i++) {
1.1       jfb       517:                        if (pfd[i].revents & (POLLERR|POLLNVAL)) {
                    518:                                cvs_log(LP_ERR,
                    519:                                    "poll error on child socket (PID %d)",
                    520:                                    chp->ch_pid);
1.13      deraadt   521:                        } else if (pfd[i].revents & POLLIN)
1.1       jfb       522:                                cvsd_msghdlr(chp, pfd[i].fd);
                    523:
                    524:                        chp = TAILQ_NEXT(chp, ch_list);
                    525:                }
                    526:
                    527:        }
1.7       jfb       528:
                    529:        /* broadcast a shutdown message to children */
                    530:        TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
                    531:                (void)cvsd_sendmsg(chp->ch_sock, CVSD_MSG_SHUTDOWN, NULL, 0);
                    532:        }
1.1       jfb       533: }
                    534:
                    535:
                    536: /*
                    537:  * cvsd_msghdlr()
1.3       jfb       538:  *
                    539:  * Handler for messages received from child processes.
                    540:  * Returns 0 on success, or -1 on failure.
1.1       jfb       541:  */
                    542: int
                    543: cvsd_msghdlr(struct cvsd_child *child, int fd)
                    544: {
                    545:        uid_t uid;
                    546:        ssize_t ret;
                    547:        char rbuf[CVSD_MSG_MAXLEN];
                    548:        struct group *gr;
                    549:        struct passwd *pw;
                    550:        struct iovec iov[2];
                    551:        struct cvsd_msg msg;
                    552:
                    553:        ret = read(fd, &msg, sizeof(msg));
                    554:        if (ret == -1) {
                    555:                cvs_log(LP_ERRNO, "failed to read CVS message");
                    556:                return (-1);
1.13      deraadt   557:        } else if (ret == 0) {
1.1       jfb       558:                cvs_log(LP_WARN, "child closed socket pair");
                    559:                return (0);
                    560:        }
                    561:
                    562:        if (msg.cm_len > 0) {
                    563:                ret = read(fd, rbuf, msg.cm_len);
                    564:                if (ret != (ssize_t)msg.cm_len) {
                    565:                        cvs_log(LP_ERR, "failed to read entire msg");
                    566:                        return (-1);
                    567:                }
                    568:        }
                    569:
                    570:        /* setup the I/O vector for the reply */
                    571:        iov[0].iov_base = &msg;
                    572:        iov[0].iov_len = sizeof(msg);
                    573:
                    574:        msg.cm_type = CVSD_MSG_ERROR;
                    575:        msg.cm_len = 0;
                    576:
                    577:        switch (msg.cm_type) {
                    578:        case CVSD_MSG_GETUID:
                    579:                rbuf[ret] = '\0';
1.14      tedu      580:                cvs_log(LP_INFO, "getting UID for `%s'", rbuf);
1.1       jfb       581:
                    582:                pw = getpwnam(rbuf);
                    583:                if (pw != NULL) {
                    584:                        msg.cm_type = CVSD_MSG_UID;
                    585:                        msg.cm_len = sizeof(uid_t);
                    586:                        iov[1].iov_len = msg.cm_len;
                    587:                        iov[1].iov_base = &(pw->pw_uid);
                    588:                }
                    589:                break;
                    590:        case CVSD_MSG_GETUNAME:
                    591:                memcpy(&uid, rbuf, sizeof(uid));
1.14      tedu      592:                cvs_log(LP_INFO, "getting username for UID %u", uid);
1.1       jfb       593:                pw = getpwuid(uid);
                    594:                if (pw != NULL) {
                    595:                        msg.cm_type = CVSD_MSG_UNAME;
                    596:                        msg.cm_len = strlen(pw->pw_name);
                    597:                        iov[1].iov_len = msg.cm_len;
                    598:                        iov[1].iov_base = pw->pw_name;
                    599:                }
                    600:                break;
1.3       jfb       601:        case CVSD_MSG_GETGID:
                    602:                rbuf[ret] = '\0';
1.14      tedu      603:                cvs_log(LP_INFO, "getting GID for `%s'", rbuf);
1.3       jfb       604:
                    605:                gr = getgrnam(rbuf);
                    606:                if (gr != NULL) {
                    607:                        msg.cm_type = CVSD_MSG_GID;
                    608:                        msg.cm_len = sizeof(gid_t);
                    609:                        iov[1].iov_len = msg.cm_len;
                    610:                        iov[1].iov_base = &(gr->gr_gid);
                    611:                }
                    612:                break;
                    613:        case CVSD_MSG_SETIDLE:
                    614:                child->ch_state = CVSD_ST_IDLE;
                    615:                break;
1.1       jfb       616:        default:
                    617:                cvs_log(LP_ERR, "unknown command type %u", msg.cm_type);
                    618:                return (-1);
                    619:        }
                    620:
                    621:        ret = writev(fd, iov, 2);
                    622:
                    623:        return (ret);
1.3       jfb       624: }
                    625:
                    626:
                    627: /*
                    628:  * cvsd_set()
                    629:  *
                    630:  * Generic interface to set some of the parameters of the cvs server.
1.7       jfb       631:  * When a string is set using cvsd_set(), the original string is copied into
                    632:  * a new buffer.
1.3       jfb       633:  * Returns 0 on success, or -1 on failure.
                    634:  */
                    635: int
                    636: cvsd_set(int what, ...)
                    637: {
                    638:        char *str;
1.12      pat       639:        int error = 0;
1.3       jfb       640:        va_list vap;
                    641:
1.9       jfb       642:        str = NULL;
                    643:
1.3       jfb       644:        va_start(vap, what);
                    645:
1.9       jfb       646:        if ((what == CVSD_SET_ROOT) || (what == CVSD_SET_SOCK) ||
1.11      jfb       647:            (what == CVSD_SET_USER) || (what == CVSD_SET_GROUP) ||
                    648:            (what == CVSD_SET_MODDIR)) {
1.7       jfb       649:                str = strdup(va_arg(vap, char *));
                    650:                if (str == NULL) {
1.9       jfb       651:                        cvs_log(LP_ERRNO, "failed to set string");
1.12      pat       652:                        va_end(vap);
1.7       jfb       653:                        return (-1);
                    654:                }
1.9       jfb       655:        }
                    656:
                    657:        switch (what) {
                    658:        case CVSD_SET_ROOT:
1.7       jfb       659:                if (cvsd_root != NULL)
                    660:                        free(cvsd_root);
1.3       jfb       661:                cvsd_root = str;
                    662:                break;
1.9       jfb       663:        case CVSD_SET_SOCK:
                    664:                if (cvsd_sock_path != NULL)
                    665:                        free(cvsd_sock_path);
                    666:                cvsd_sock_path = str;
                    667:                break;
                    668:        case CVSD_SET_USER:
                    669:                if (cvsd_user != NULL)
                    670:                        free(cvsd_user);
                    671:                cvsd_user = str;
                    672:                break;
                    673:        case CVSD_SET_GROUP:
                    674:                if (cvsd_group != NULL)
                    675:                        free(cvsd_group);
                    676:                cvsd_group = str;
1.11      jfb       677:                break;
                    678:        case CVSD_SET_MODDIR:
                    679:                if (cvsd_moddir != NULL)
                    680:                        free(cvsd_moddir);
                    681:                cvsd_moddir = str;
1.9       jfb       682:                break;
1.3       jfb       683:        case CVSD_SET_CHMAX:
                    684:                cvsd_chmax = va_arg(vap, int);
                    685:                /* we should decrease the number of children accordingly */
                    686:                break;
                    687:        case CVSD_SET_ADDR:
1.7       jfb       688:                /* this is more like an add than a set */
1.3       jfb       689:                break;
                    690:        default:
                    691:                cvs_log(LP_ERR, "invalid field to set");
1.12      pat       692:                error = -1;
                    693:                break;
1.3       jfb       694:        }
                    695:
                    696:        va_end(vap);
                    697:
1.12      pat       698:        return (error);
1.8       jfb       699: }
                    700:
                    701:
                    702: /*
                    703:  * cvsd_report()
1.18    ! jfb       704:  *
        !           705:  * Report about the current state of child processes on the repository.
1.8       jfb       706:  */
                    707: static void
                    708: cvsd_report(void)
                    709: {
                    710:        u_int nb_idle, nb_busy, nb_unknown;
                    711:        struct cvsd_child *ch;
                    712:
                    713:        nb_idle = 0;
                    714:        nb_busy = 0;
                    715:        nb_unknown = 0;
                    716:
                    717:        signal(SIGCHLD, SIG_IGN);
                    718:        TAILQ_FOREACH(ch, &cvsd_children, ch_list) {
                    719:                if (ch->ch_state == CVSD_ST_IDLE)
                    720:                        nb_idle++;
                    721:                else if (ch->ch_state == CVSD_ST_BUSY)
                    722:                        nb_busy++;
                    723:                else if (ch->ch_state == CVSD_ST_UNKNOWN)
                    724:                        nb_unknown++;
                    725:        }
                    726:
                    727:        cvs_log(LP_WARN, "%u children, %u idle, %u busy, %u unknown",
                    728:            cvsd_chnum, nb_idle, nb_busy, nb_unknown);
                    729:
                    730:        TAILQ_FOREACH(ch, &cvsd_children, ch_list)
                    731:                cvs_log(LP_WARN, "");
                    732:        signal(SIGCHLD, cvsd_sighdlr);
1.1       jfb       733: }