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