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: }