Annotation of src/usr.bin/cvs/cvsd.c, Revision 1.14
1.14 ! tedu 1: /* $OpenBSD: cvsd.c,v 1.13 2004/12/06 21:03:12 deraadt 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.8 jfb 135: "\t-u user\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.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.6 jfb 156: while ((ret = getopt(argc, argv, "a:c:dfhpr:s: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':
177: checkrepo = 1;
178: break;
179: case 'r':
180: cvsd_root = optarg;
181: break;
182: case 's':
183: cvsd_sock_path = optarg;
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:
219: cvsd_uid = pwd->pw_uid;
220: cvsd_gid = grp->gr_gid;
221:
1.8 jfb 222: signal(SIGHUP, cvsd_sighdlr);
223: signal(SIGINT, cvsd_sighdlr);
224: signal(SIGQUIT, cvsd_sighdlr);
225: signal(SIGTERM, cvsd_sighdlr);
226: signal(SIGCHLD, cvsd_sighdlr);
1.11 jfb 227: signal(SIGPIPE, SIG_IGN);
1.1 jfb 228:
1.10 jfb 229: if (!cvsd_fg && daemon(0, 0) == -1) {
1.1 jfb 230: cvs_log(LP_ERRNO, "failed to become a daemon");
231: exit(EX_OSERR);
232: }
233:
234: if (cvsd_sock_open() < 0) {
235: exit(1);
236: }
237:
238: if (setegid(cvsd_gid) == -1) {
239: cvs_log(LP_ERRNO, "failed to drop group privileges");
240: exit(EX_OSERR);
241: }
242: if (seteuid(cvsd_uid) == -1) {
243: cvs_log(LP_ERRNO, "failed to drop user privileges");
244: exit(EX_OSERR);
245: }
246:
247: if (checkrepo && cvsd_checkperms("/") != 0) {
248: cvs_log(LP_ERR,
249: "exiting due to permission errors on repository");
250: exit(1);
251: }
252:
253: /* spawn the initial pool of children */
1.11 jfb 254: for (i = 0; i < (u_int)cvsd_chmin; i++)
255: if (cvsd_child_fork(NULL) < 0)
1.1 jfb 256: exit(EX_OSERR);
257:
1.8 jfb 258: signal(SIGINFO, cvsd_sighdlr);
1.3 jfb 259: cvsd_parent_loop();
1.1 jfb 260:
261: cvs_log(LP_NOTICE, "shutting down");
262: cvs_log_cleanup();
263:
264: cvsd_sock_close();
265:
266: return (0);
267: }
268:
269:
270: /*
271: * cvsd_privdrop()
272: *
273: * Drop privileges.
274: */
275: int
276: cvsd_privdrop(void)
277: {
1.8 jfb 278: cvs_log(LP_INFO, "dropping privileges to %s[%d]:%s[%d]",
279: cvsd_user, cvsd_uid, cvsd_group, cvsd_gid);
1.2 jfb 280: if (setgid(cvsd_gid) == -1) {
1.1 jfb 281: cvs_log(LP_ERRNO, "failed to drop group privileges to %s",
282: CVSD_GROUP);
283: return (-1);
284: }
285:
1.2 jfb 286: if (setuid(cvsd_uid) == -1) {
1.1 jfb 287: cvs_log(LP_ERRNO, "failed to drop user privileges to %s",
288: CVSD_USER);
289: return (-1);
290: }
291:
292: return (0);
293: }
294:
295:
296: /*
297: * cvsd_checkperms()
298: *
299: * Check permissions on the CVS repository and log warnings for any
300: * weird of loose permissions.
301: * Returns the number of warnings on success, or -1 on failure.
302: */
303: int
304: cvsd_checkperms(const char *path)
305: {
306: int fd, nbwarn, ret;
307: mode_t fmode;
308: long base;
309: void *dp, *endp;
310: char buf[1024], spath[MAXPATHLEN];
311: struct stat st;
312: struct dirent *dep;
313:
314: nbwarn = 0;
315:
316: cvs_log(LP_DEBUG, "checking permissions on `%s'", path);
317:
318: if (stat(path, &st) == -1) {
319: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
320: return (-1);
321: }
322:
323: if (S_ISDIR(st.st_mode))
324: fmode = CVSD_DPERM;
325: else
326: fmode = CVSD_FPERM;
327:
328: if (st.st_uid != cvsd_uid) {
329: cvs_log(LP_WARN, "owner of `%s' is not %s", path, CVSD_USER);
330: nbwarn++;
331: }
332:
333: if (st.st_gid != cvsd_gid) {
334: cvs_log(LP_WARN, "group of `%s' is not %s", path, CVSD_GROUP);
335: nbwarn++;
336: }
337:
338: if (st.st_mode & S_IWGRP) {
339: cvs_log(LP_WARN, "file `%s' is group-writable", path,
340: fmode);
341: nbwarn++;
342: }
343:
344: if (st.st_mode & S_IWOTH) {
345: cvs_log(LP_WARN, "file `%s' is world-writable", path,
346: fmode);
347: nbwarn++;
348: }
349:
350: if (S_ISDIR(st.st_mode)) {
351: fd = open(path, O_RDONLY, 0);
352: if (fd == -1) {
353: cvs_log(LP_ERRNO, "failed to open directory `%s'",
354: path);
355: return (nbwarn);
356: }
357: /* recurse */
358: ret = getdirentries(fd, buf, sizeof(buf), &base);
359: if (ret == -1) {
360: cvs_log(LP_ERRNO,
361: "failed to get directory entries for `%s'", path);
362: (void)close(fd);
363: return (nbwarn);
364: }
365:
366: dp = buf;
367: endp = buf + ret;
368:
369: while (dp < endp) {
370: dep = (struct dirent *)dp;
371: dp += dep->d_reclen;
372:
373: if ((dep->d_namlen == 1) && (dep->d_name[0] == '.'))
374: continue;
375: if ((dep->d_namlen == 2) && (dep->d_name[0] == '.') &&
376: (dep->d_name[1] == '.'))
377: continue;
378:
379: /* skip the CVSROOT directory */
380: if (strcmp(dep->d_name, CVS_PATH_ROOT) == 0)
381: continue;
382:
383: snprintf(spath, sizeof(spath), "%s/%s", path,
384: dep->d_name);
385: ret = cvsd_checkperms(spath);
386: if (ret == -1)
387: nbwarn++;
388: else
389: nbwarn += ret;
390: }
391: (void)close(fd);
392: }
393:
394:
395: return (nbwarn);
396: }
397:
398:
399: /*
1.3 jfb 400: * cvsd_child_fork()
1.1 jfb 401: *
402: * Fork a child process which chroots to the CVS repository's root directory.
1.3 jfb 403: * If the <chpp> argument is not NULL, a reference to the newly created child
404: * structure will be returned.
405: * On success, returns 0 in the child process context, 1 in the parent's
406: * context, or -1 on failure.
1.1 jfb 407: */
408: int
1.3 jfb 409: cvsd_child_fork(struct cvsd_child **chpp)
1.1 jfb 410: {
411: int svec[2];
412: pid_t pid;
413: struct cvsd_child *chp;
414:
1.3 jfb 415: if (cvsd_chnum == cvsd_chmax) {
416: cvs_log(LP_WARN, "child pool reached limit of processes");
417: return (-1);
418: }
419:
1.1 jfb 420: if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, svec) == -1) {
421: cvs_log(LP_ERRNO, "failed to create socket pair");
422: return (-1);
423: }
424:
425: /*
426: * We need to temporarily regain original privileges in order for the
427: * child to chroot().
428: */
429: if (seteuid(0) == -1) {
430: cvs_log(LP_ERRNO, "failed to regain privileges");
431: return (-1);
432: }
433:
434: pid = fork();
435: if (pid == -1) {
436: cvs_log(LP_ERRNO, "failed to fork child");
437: (void)close(svec[0]);
438: (void)close(svec[1]);
439: return (-1);
440: }
441:
442: if (pid == 0) {
443: cvsd_privfd = svec[1];
444: (void)close(svec[0]);
445:
1.11 jfb 446: cvsd_child_main();
447: /* NOTREACHED */
1.1 jfb 448: }
449:
450: cvs_log(LP_INFO, "spawning child %d", pid);
451:
452: if (seteuid(cvsd_uid) == -1) {
453: cvs_log(LP_ERRNO, "failed to redrop privs");
454: return (-1);
455: }
456:
457: chp = (struct cvsd_child *)malloc(sizeof(*chp));
458: if (chp == NULL) {
459: cvs_log(LP_ERRNO, "failed to allocate child data");
460: return (-1);
461: }
462:
463: chp->ch_pid = pid;
464: chp->ch_sock = svec[0];
1.3 jfb 465: chp->ch_state = CVSD_ST_IDLE;
466:
1.7 jfb 467: signal(SIGCHLD, SIG_IGN);
1.3 jfb 468: TAILQ_INSERT_TAIL(&cvsd_children, chp, ch_list);
1.7 jfb 469: cvsd_chnum++;
1.8 jfb 470: signal(SIGCHLD, cvsd_sighdlr);
1.3 jfb 471:
472: if (chpp != NULL)
473: *chpp = chp;
1.1 jfb 474: (void)close(svec[1]);
1.3 jfb 475: return (1);
476: }
477:
478:
479: /*
480: * cvsd_child_reap()
481: *
1.7 jfb 482: * Wait for a child's status and perform the proper actions depending on it.
483: * If the child has exited or has been terminated by a signal, it will be
484: * removed from the list and new children will be created until the pool has
485: * at least <cvsd_chmin> children in it.
1.3 jfb 486: * Returns 0 on success, or -1 on failure.
487: */
488: int
1.7 jfb 489: cvsd_child_reap(void)
1.3 jfb 490: {
1.7 jfb 491: pid_t pid;
492: int status;
493: struct cvsd_child *ch;
1.3 jfb 494:
1.7 jfb 495: pid = wait(&status);
496: if (pid == -1) {
497: cvs_log(LP_ERRNO, "failed to wait for child");
498: return (-1);
499: }
500:
501: TAILQ_FOREACH(ch, &cvsd_children, ch_list) {
502: if (ch->ch_pid == pid) {
503: if (WIFEXITED(status)) {
504: cvs_log(LP_WARN,
505: "child %d exited with status %d",
506: pid, WEXITSTATUS(status));
1.13 deraadt 507: } else if (WIFSIGNALED(status)) {
1.7 jfb 508: cvs_log(LP_WARN,
509: "child %d terminated with signal %d",
510: pid, WTERMSIG(status));
1.13 deraadt 511: } else {
1.7 jfb 512: cvs_log(LP_ERR, "HOLY SHIT!");
513: }
514:
515: signal(SIGCHLD, SIG_IGN);
516: TAILQ_REMOVE(&cvsd_children, ch, ch_list);
517: cvsd_chnum--;
1.8 jfb 518: signal(SIGCHLD, cvsd_sighdlr);
1.7 jfb 519:
520: break;
521: }
522: }
523:
524: while (cvsd_chnum < cvsd_chmin)
525: cvsd_child_fork(NULL);
1.3 jfb 526:
527: return (0);
528: }
529:
530:
531: /*
532: * cvsd_child_get()
533: *
534: * Find a child process in idle state and return a pointer to the child's
535: * structure. If there are no available child processes, a new one will be
536: * created unless the number of children has attained the maximum, in which
537: * case NULL is returned.
538: */
539: struct cvsd_child*
540: cvsd_child_get(void)
541: {
542: struct cvsd_child *chp;
1.1 jfb 543:
1.3 jfb 544: TAILQ_FOREACH(chp, &cvsd_children, ch_list)
545: if (chp->ch_state == CVSD_ST_IDLE)
546: return (chp);
547:
548: /* no available child, attempt to fork a new one */
549: chp = NULL;
550: if ((cvsd_chnum < cvsd_chmax) && (cvsd_child_fork(&chp) < 0))
551: return (NULL);
552:
553: return (chp);
1.1 jfb 554: }
555:
556:
1.3 jfb 557:
1.1 jfb 558: /*
559: * cvsd_parent_loop()
560: *
561: * Main loop of the parent cvsd process, which listens on its end of the
1.3 jfb 562: * local socket for requests from the cvs(1) program and on any outstanding
563: * messages from the children.
1.1 jfb 564: */
565: static void
566: cvsd_parent_loop(void)
567: {
1.3 jfb 568: int cfd, timeout, ret;
1.1 jfb 569: nfds_t nfds, i;
570: struct pollfd *pfd;
571: struct cvsd_child *chp;
572:
573: nfds = 0;
574: timeout = INFTIM;
1.3 jfb 575: pfd = NULL;
1.1 jfb 576:
577: for (;;) {
1.10 jfb 578: if (!cvsd_running)
1.3 jfb 579: break;
580:
1.10 jfb 581: if (cvsd_restart) {
1.3 jfb 582: /* restart server */
583: }
1.7 jfb 584:
585: if (cvsd_sigchld) {
586: cvsd_sigchld = 0;
587: cvsd_child_reap();
588: }
1.8 jfb 589: if (cvsd_siginfo) {
590: cvsd_siginfo = 0;
591: cvsd_report();
592: }
1.7 jfb 593:
1.3 jfb 594: nfds = cvsd_chnum + 1;
1.1 jfb 595: pfd = (struct pollfd *)realloc(pfd,
596: nfds * sizeof(struct pollfd));
597: if (pfd == NULL) {
598: cvs_log(LP_ERRNO, "failed to reallocate polling data");
599: return;
600: }
601:
1.3 jfb 602: pfd[0].fd = cvsd_sock;
603: pfd[0].events = POLLIN;
604: pfd[0].revents = 0;
605: i = 1;
1.1 jfb 606: TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
607: pfd[i].fd = chp->ch_sock;
608: pfd[i].events = POLLIN;
609: pfd[i].revents = 0;
610: i++;
611:
1.3 jfb 612: if (i == nfds) /* just a precaution */
1.1 jfb 613: break;
614: }
615:
616: ret = poll(pfd, nfds, timeout);
617: if (ret == -1) {
1.7 jfb 618: if (errno == EINTR)
619: continue;
1.1 jfb 620: cvs_log(LP_ERRNO, "poll error");
621: break;
622: }
623:
1.3 jfb 624: if (pfd[0].revents & (POLLERR|POLLNVAL)) {
625: cvs_log(LP_ERR, "poll error on request socket");
1.13 deraadt 626: } else if (pfd[0].revents & POLLIN) {
1.11 jfb 627: uid_t uid;
628: gid_t gid;
1.13 deraadt 629:
1.3 jfb 630: cfd = cvsd_sock_accept(pfd[0].fd);
631: if (cfd == -1)
632: chp = cvsd_child_get();
633: if (chp == NULL) {
634: cvs_log(LP_ALERT,
635: "request queue not implemented");
636: break;
637: }
638:
1.11 jfb 639: if (getpeereid(cfd, &uid, &gid) < 0)
640: err(1, "failed to get UID");
1.3 jfb 641: if (cvsd_sendmsg(chp->ch_sock, CVSD_MSG_PASSFD,
642: &cfd, sizeof(cfd)) < 0)
643: break;
644:
645: /* mark the child as busy */
646: chp->ch_state = CVSD_ST_BUSY;
647: }
648:
1.1 jfb 649: chp = TAILQ_FIRST(&cvsd_children);
1.3 jfb 650: for (i = 1; i < nfds; i++) {
1.1 jfb 651: if (pfd[i].revents & (POLLERR|POLLNVAL)) {
652: cvs_log(LP_ERR,
653: "poll error on child socket (PID %d)",
654: chp->ch_pid);
1.13 deraadt 655: } else if (pfd[i].revents & POLLIN)
1.1 jfb 656: cvsd_msghdlr(chp, pfd[i].fd);
657:
658: chp = TAILQ_NEXT(chp, ch_list);
659: }
660:
661: }
1.7 jfb 662:
663: /* broadcast a shutdown message to children */
664: TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
665: (void)cvsd_sendmsg(chp->ch_sock, CVSD_MSG_SHUTDOWN, NULL, 0);
666: }
1.1 jfb 667: }
668:
669:
670: /*
1.11 jfb 671: * cvsd_child_main()
1.1 jfb 672: *
673: */
674: static void
1.11 jfb 675: cvsd_child_main(void)
1.1 jfb 676: {
677: int ret, timeout;
1.7 jfb 678: u_int mtype;
679: size_t mlen;
680: char mbuf[CVSD_MSG_MAXLEN];
1.1 jfb 681: struct pollfd pfd[1];
1.11 jfb 682: struct cvsd_sess *sessp;
683:
684: cvs_log(LP_INFO, "changing root to %s", cvsd_root);
685: if (chroot(cvsd_root) == -1) {
686: cvs_log(LP_ERRNO, "failed to chroot to `%s'", cvsd_root);
687: exit(EX_OSERR);
688: }
689: (void)chdir("/");
690:
691: if (cvsd_privdrop() < 0)
692: exit(EX_OSERR);
693:
694: setproctitle("%s [child %d]", __progname, getpid());
1.1 jfb 695:
696: pfd[0].fd = cvsd_privfd;
697: pfd[0].events = POLLIN;
698: timeout = INFTIM;
1.11 jfb 699: sessp = NULL;
1.1 jfb 700:
1.10 jfb 701: while (cvsd_running) {
1.1 jfb 702: ret = poll(pfd, 1, timeout);
703: if (ret == -1) {
1.7 jfb 704: if (errno == EINTR)
705: continue;
706: cvs_log(LP_ERRNO, "poll error");
1.1 jfb 707: break;
1.13 deraadt 708: } else if (ret == 0)
1.7 jfb 709: continue;
1.1 jfb 710:
1.3 jfb 711: if (pfd[0].revents & (POLLERR|POLLNVAL)) {
712: cvs_log(LP_ERR, "poll error");
713: break;
714: }
1.7 jfb 715:
716: mlen = sizeof(mbuf);
717: ret = cvsd_recvmsg(pfd[0].fd, &mtype, mbuf, &mlen);
718: if (ret == -1) {
719: continue;
1.13 deraadt 720: } else if (ret == 0)
1.7 jfb 721: break;
722:
723: switch (mtype) {
1.8 jfb 724: case CVSD_MSG_PASSFD:
1.11 jfb 725: sessp = cvsd_sess_alloc(*(int *)mbuf);
1.8 jfb 726: break;
1.7 jfb 727: case CVSD_MSG_SHUTDOWN:
1.10 jfb 728: cvsd_running = 0;
1.7 jfb 729: break;
730: default:
731: cvs_log(LP_ERR,
1.11 jfb 732: "unexpected message type %u from parent", mtype);
1.7 jfb 733: break;
734: }
735:
1.1 jfb 736: }
737:
738: exit(0);
739: }
740:
741:
742: /*
743: * cvsd_msghdlr()
1.3 jfb 744: *
745: * Handler for messages received from child processes.
746: * Returns 0 on success, or -1 on failure.
1.1 jfb 747: */
748: int
749: cvsd_msghdlr(struct cvsd_child *child, int fd)
750: {
751: uid_t uid;
752: ssize_t ret;
753: char rbuf[CVSD_MSG_MAXLEN];
754: struct group *gr;
755: struct passwd *pw;
756: struct iovec iov[2];
757: struct cvsd_msg msg;
758:
759: ret = read(fd, &msg, sizeof(msg));
760: if (ret == -1) {
761: cvs_log(LP_ERRNO, "failed to read CVS message");
762: return (-1);
1.13 deraadt 763: } else if (ret == 0) {
1.1 jfb 764: cvs_log(LP_WARN, "child closed socket pair");
765: return (0);
766: }
767:
768: if (msg.cm_len > 0) {
769: ret = read(fd, rbuf, msg.cm_len);
770: if (ret != (ssize_t)msg.cm_len) {
771: cvs_log(LP_ERR, "failed to read entire msg");
772: return (-1);
773: }
774: }
775:
776: /* setup the I/O vector for the reply */
777: iov[0].iov_base = &msg;
778: iov[0].iov_len = sizeof(msg);
779:
780: msg.cm_type = CVSD_MSG_ERROR;
781: msg.cm_len = 0;
782:
783: switch (msg.cm_type) {
784: case CVSD_MSG_GETUID:
785: rbuf[ret] = '\0';
1.14 ! tedu 786: cvs_log(LP_INFO, "getting UID for `%s'", rbuf);
1.1 jfb 787:
788: pw = getpwnam(rbuf);
789: if (pw != NULL) {
790: msg.cm_type = CVSD_MSG_UID;
791: msg.cm_len = sizeof(uid_t);
792: iov[1].iov_len = msg.cm_len;
793: iov[1].iov_base = &(pw->pw_uid);
794: }
795: break;
796: case CVSD_MSG_GETUNAME:
797: memcpy(&uid, rbuf, sizeof(uid));
1.14 ! tedu 798: cvs_log(LP_INFO, "getting username for UID %u", uid);
1.1 jfb 799: pw = getpwuid(uid);
800: if (pw != NULL) {
801: msg.cm_type = CVSD_MSG_UNAME;
802: msg.cm_len = strlen(pw->pw_name);
803: iov[1].iov_len = msg.cm_len;
804: iov[1].iov_base = pw->pw_name;
805: }
806: break;
1.3 jfb 807: case CVSD_MSG_GETGID:
808: rbuf[ret] = '\0';
1.14 ! tedu 809: cvs_log(LP_INFO, "getting GID for `%s'", rbuf);
1.3 jfb 810:
811: gr = getgrnam(rbuf);
812: if (gr != NULL) {
813: msg.cm_type = CVSD_MSG_GID;
814: msg.cm_len = sizeof(gid_t);
815: iov[1].iov_len = msg.cm_len;
816: iov[1].iov_base = &(gr->gr_gid);
817: }
818: break;
819: case CVSD_MSG_SETIDLE:
820: child->ch_state = CVSD_ST_IDLE;
821: break;
1.1 jfb 822: default:
823: cvs_log(LP_ERR, "unknown command type %u", msg.cm_type);
824: return (-1);
825: }
826:
827: ret = writev(fd, iov, 2);
828:
829: return (ret);
1.3 jfb 830: }
831:
832:
833: /*
834: * cvsd_set()
835: *
836: * Generic interface to set some of the parameters of the cvs server.
1.7 jfb 837: * When a string is set using cvsd_set(), the original string is copied into
838: * a new buffer.
1.3 jfb 839: * Returns 0 on success, or -1 on failure.
840: */
841: int
842: cvsd_set(int what, ...)
843: {
844: char *str;
1.12 pat 845: int error = 0;
1.3 jfb 846: va_list vap;
847:
1.9 jfb 848: str = NULL;
849:
1.3 jfb 850: va_start(vap, what);
851:
1.9 jfb 852: if ((what == CVSD_SET_ROOT) || (what == CVSD_SET_SOCK) ||
1.11 jfb 853: (what == CVSD_SET_USER) || (what == CVSD_SET_GROUP) ||
854: (what == CVSD_SET_MODDIR)) {
1.7 jfb 855: str = strdup(va_arg(vap, char *));
856: if (str == NULL) {
1.9 jfb 857: cvs_log(LP_ERRNO, "failed to set string");
1.12 pat 858: va_end(vap);
1.7 jfb 859: return (-1);
860: }
1.9 jfb 861: }
862:
863: switch (what) {
864: case CVSD_SET_ROOT:
1.7 jfb 865: if (cvsd_root != NULL)
866: free(cvsd_root);
1.3 jfb 867: cvsd_root = str;
868: break;
1.9 jfb 869: case CVSD_SET_SOCK:
870: if (cvsd_sock_path != NULL)
871: free(cvsd_sock_path);
872: cvsd_sock_path = str;
1.12 pat 873: error = cvsd_sock_open();
1.9 jfb 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: }