Annotation of src/usr.bin/cvs/cvsd.c, Revision 1.9
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);
53: static void cvsd_child_loop (void);
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:
62: int foreground = 0;
63:
1.7 jfb 64: volatile sig_atomic_t running = 1;
1.1 jfb 65: volatile sig_atomic_t restart = 0;
66:
67:
1.8 jfb 68: uid_t cvsd_uid = -1;
69: gid_t cvsd_gid = -1;
1.1 jfb 70:
1.9 ! jfb 71: static char *cvsd_user = NULL;
! 72: static char *cvsd_group = NULL;
1.7 jfb 73: static char *cvsd_root = NULL;
74: static char *cvsd_conffile = CVSD_CONF;
1.1 jfb 75: static int cvsd_privfd = -1;
76:
77:
78:
79: static TAILQ_HEAD(,cvsd_child) cvsd_children;
80: static volatile sig_atomic_t cvsd_chnum = 0;
81: static volatile sig_atomic_t cvsd_chmin = CVSD_CHILD_DEFMIN;
82: static volatile sig_atomic_t cvsd_chmax = CVSD_CHILD_DEFMAX;
1.7 jfb 83: static volatile sig_atomic_t cvsd_sigchld = 0;
1.8 jfb 84: static volatile sig_atomic_t cvsd_siginfo = 0;
1.1 jfb 85:
86:
1.8 jfb 87: void usage (void);
88: void cvsd_sighdlr (int);
89: int cvsd_msghdlr (struct cvsd_child *, int);
1.3 jfb 90:
91:
1.1 jfb 92: /*
1.8 jfb 93: * cvsd_sighdlr()
1.1 jfb 94: *
1.8 jfb 95: * Generic signal handler.
1.1 jfb 96: */
97:
98: void
1.8 jfb 99: cvsd_sighdlr(int signo)
1.1 jfb 100: {
1.8 jfb 101: switch (signo) {
102: case SIGHUP:
103: restart = 1;
104: break;
105: case SIGCHLD:
106: cvsd_sigchld = 1;
107: break;
108: case SIGINT:
109: case SIGTERM:
110: case SIGQUIT:
111: running = 0;
112: break;
113: case SIGINFO:
114: cvsd_siginfo = 1;
115: break;
116: }
1.1 jfb 117: }
118:
119:
120: /*
121: * usage()
122: *
123: * Display program usage.
124: */
125:
126: void
127: usage(void)
128: {
129: fprintf(stderr,
1.8 jfb 130: "Usage: %s [-dfhpv] [-c config] [-g group] [-r root] "
131: "[-s path] [-u user]\n"
1.7 jfb 132: "\t-c config\tUse <config> as the configuration file\n"
1.1 jfb 133: "\t-d\t\tStart the server in debugging mode (very verbose)\n"
134: "\t-f\t\tStay in foreground instead of becoming a daemon\n"
1.8 jfb 135: "\t-g group\tUse group <group> for privilege revocation\n"
1.6 jfb 136: "\t-h\t\tPrint the usage and exit\n"
137: "\t-p\t\tPerform repository sanity check on startup\n"
138: "\t-r root\t\tUse <root> as the root directory of the repository\n"
139: "\t-s path\t\tUse <path> as the path for the CVS server socket\n"
1.8 jfb 140: "\t-u user\tUse user <user> for privilege revocation\n"
1.1 jfb 141: "\t-v\t\tBe verbose\n",
142: __progname);
143: }
144:
145:
146: int
147: main(int argc, char **argv)
148: {
149: u_int i;
150: int ret, checkrepo;
151: struct passwd *pwd;
152: struct group *grp;
153:
154: checkrepo = 0;
1.9 ! jfb 155: cvsd_set(CVSD_SET_USER, CVSD_USER);
! 156: cvsd_set(CVSD_SET_GROUP, CVSD_GROUP);
1.1 jfb 157:
158: if (cvs_log_init(LD_STD|LD_SYSLOG, LF_PID) < 0)
159: err(1, "failed to initialize logging mechanism");
160:
1.6 jfb 161: while ((ret = getopt(argc, argv, "a:c:dfhpr:s:v")) != -1) {
1.1 jfb 162: switch (ret) {
1.3 jfb 163: case 'c':
164: cvsd_conffile = optarg;
1.1 jfb 165: break;
166: case 'd':
167: cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
168: cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
169: break;
170: case 'f':
171: foreground = 1;
1.6 jfb 172: break;
1.8 jfb 173: case 'g':
1.9 ! jfb 174: cvsd_set(CVSD_SET_GROUP, optarg);
1.8 jfb 175: break;
1.6 jfb 176: case 'h':
177: usage();
178: exit(0);
179: /* NOTREACHED */
1.1 jfb 180: break;
181: case 'p':
182: checkrepo = 1;
183: break;
184: case 'r':
185: cvsd_root = optarg;
186: break;
187: case 's':
188: cvsd_sock_path = optarg;
189: break;
1.8 jfb 190: case 'u':
1.9 ! jfb 191: cvsd_set(CVSD_SET_USER, optarg);
1.8 jfb 192: break;
1.1 jfb 193: case 'v':
194: cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
195: break;
196: default:
197: usage();
198: exit(EX_USAGE);
199: }
200: }
201:
202: argc -= optind;
203: argv += optind;
204:
1.3 jfb 205: if (cvs_conf_read(cvsd_conffile) < 0)
206: errx(1, "error parsing configuration file `%s'", cvsd_conffile);
1.1 jfb 207:
208: if (cvsd_root == NULL)
209: errx(1, "no CVS root directory specified");
210:
211: if (argc > 0)
212: errx(EX_USAGE, "unrecognized trailing arguments");
213:
214: TAILQ_INIT(&cvsd_children);
215:
1.8 jfb 216: pwd = getpwnam(cvsd_user);
1.1 jfb 217: if (pwd == NULL)
1.8 jfb 218: err(EX_NOUSER, "failed to get user `%s'", cvsd_user);
1.1 jfb 219:
1.8 jfb 220: grp = getgrnam(cvsd_group);
1.1 jfb 221: if (grp == NULL)
1.8 jfb 222: err(EX_NOUSER, "failed to get group `%s'", cvsd_group);
1.1 jfb 223:
224: cvsd_uid = pwd->pw_uid;
225: cvsd_gid = grp->gr_gid;
226:
1.8 jfb 227: signal(SIGHUP, cvsd_sighdlr);
228: signal(SIGINT, cvsd_sighdlr);
229: signal(SIGQUIT, cvsd_sighdlr);
230: signal(SIGTERM, cvsd_sighdlr);
231: signal(SIGCHLD, cvsd_sighdlr);
1.1 jfb 232:
233: if (!foreground && daemon(0, 0) == -1) {
234: cvs_log(LP_ERRNO, "failed to become a daemon");
235: exit(EX_OSERR);
236: }
237:
238: if (cvsd_sock_open() < 0) {
239: exit(1);
240: }
241:
242: if (setegid(cvsd_gid) == -1) {
243: cvs_log(LP_ERRNO, "failed to drop group privileges");
244: exit(EX_OSERR);
245: }
246: if (seteuid(cvsd_uid) == -1) {
247: cvs_log(LP_ERRNO, "failed to drop user privileges");
248: exit(EX_OSERR);
249: }
250:
251: if (checkrepo && cvsd_checkperms("/") != 0) {
252: cvs_log(LP_ERR,
253: "exiting due to permission errors on repository");
254: exit(1);
255: }
256:
257: /* spawn the initial pool of children */
1.3 jfb 258: for (i = 0; i < (u_int)cvsd_chmin; i++) {
259: ret = cvsd_child_fork(NULL);
1.1 jfb 260: if (ret == -1) {
261: cvs_log(LP_ERR, "failed to spawn child");
262: exit(EX_OSERR);
263: }
264: }
265:
1.8 jfb 266: signal(SIGINFO, cvsd_sighdlr);
1.3 jfb 267: cvsd_parent_loop();
1.1 jfb 268:
269: cvs_log(LP_NOTICE, "shutting down");
270: cvs_log_cleanup();
271:
272: cvsd_sock_close();
273:
274: return (0);
275: }
276:
277:
278: /*
279: * cvsd_privdrop()
280: *
281: * Drop privileges.
282: */
283:
284: int
285: cvsd_privdrop(void)
286: {
1.8 jfb 287: cvs_log(LP_INFO, "dropping privileges to %s[%d]:%s[%d]",
288: cvsd_user, cvsd_uid, cvsd_group, cvsd_gid);
1.2 jfb 289: if (setgid(cvsd_gid) == -1) {
1.1 jfb 290: cvs_log(LP_ERRNO, "failed to drop group privileges to %s",
291: CVSD_GROUP);
292: return (-1);
293: }
294:
1.2 jfb 295: if (setuid(cvsd_uid) == -1) {
1.1 jfb 296: cvs_log(LP_ERRNO, "failed to drop user privileges to %s",
297: CVSD_USER);
298: return (-1);
299: }
300:
301: return (0);
302: }
303:
304:
305: /*
306: * cvsd_checkperms()
307: *
308: * Check permissions on the CVS repository and log warnings for any
309: * weird of loose permissions.
310: * Returns the number of warnings on success, or -1 on failure.
311: */
312:
313: int
314: cvsd_checkperms(const char *path)
315: {
316: int fd, nbwarn, ret;
317: mode_t fmode;
318: long base;
319: void *dp, *endp;
320: char buf[1024], spath[MAXPATHLEN];
321: struct stat st;
322: struct dirent *dep;
323:
324: nbwarn = 0;
325:
326: cvs_log(LP_DEBUG, "checking permissions on `%s'", path);
327:
328: if (stat(path, &st) == -1) {
329: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
330: return (-1);
331: }
332:
333: if (S_ISDIR(st.st_mode))
334: fmode = CVSD_DPERM;
335: else
336: fmode = CVSD_FPERM;
337:
338: if (st.st_uid != cvsd_uid) {
339: cvs_log(LP_WARN, "owner of `%s' is not %s", path, CVSD_USER);
340: nbwarn++;
341: }
342:
343: if (st.st_gid != cvsd_gid) {
344: cvs_log(LP_WARN, "group of `%s' is not %s", path, CVSD_GROUP);
345: nbwarn++;
346: }
347:
348: if (st.st_mode & S_IWGRP) {
349: cvs_log(LP_WARN, "file `%s' is group-writable", path,
350: fmode);
351: nbwarn++;
352: }
353:
354: if (st.st_mode & S_IWOTH) {
355: cvs_log(LP_WARN, "file `%s' is world-writable", path,
356: fmode);
357: nbwarn++;
358: }
359:
360: if (S_ISDIR(st.st_mode)) {
361: fd = open(path, O_RDONLY, 0);
362: if (fd == -1) {
363: cvs_log(LP_ERRNO, "failed to open directory `%s'",
364: path);
365: return (nbwarn);
366: }
367: /* recurse */
368: ret = getdirentries(fd, buf, sizeof(buf), &base);
369: if (ret == -1) {
370: cvs_log(LP_ERRNO,
371: "failed to get directory entries for `%s'", path);
372: (void)close(fd);
373: return (nbwarn);
374: }
375:
376: dp = buf;
377: endp = buf + ret;
378:
379: while (dp < endp) {
380: dep = (struct dirent *)dp;
381: dp += dep->d_reclen;
382:
383: if ((dep->d_namlen == 1) && (dep->d_name[0] == '.'))
384: continue;
385: if ((dep->d_namlen == 2) && (dep->d_name[0] == '.') &&
386: (dep->d_name[1] == '.'))
387: continue;
388:
389: /* skip the CVSROOT directory */
390: if (strcmp(dep->d_name, CVS_PATH_ROOT) == 0)
391: continue;
392:
393: snprintf(spath, sizeof(spath), "%s/%s", path,
394: dep->d_name);
395: ret = cvsd_checkperms(spath);
396: if (ret == -1)
397: nbwarn++;
398: else
399: nbwarn += ret;
400: }
401: (void)close(fd);
402: }
403:
404:
405: return (nbwarn);
406: }
407:
408:
409: /*
1.3 jfb 410: * cvsd_child_fork()
1.1 jfb 411: *
412: * Fork a child process which chroots to the CVS repository's root directory.
1.3 jfb 413: * If the <chpp> argument is not NULL, a reference to the newly created child
414: * structure will be returned.
415: * On success, returns 0 in the child process context, 1 in the parent's
416: * context, or -1 on failure.
1.1 jfb 417: */
418:
419: int
1.3 jfb 420: cvsd_child_fork(struct cvsd_child **chpp)
1.1 jfb 421: {
422: int svec[2];
423: pid_t pid;
424: struct cvsd_child *chp;
425:
1.3 jfb 426: if (cvsd_chnum == cvsd_chmax) {
427: cvs_log(LP_WARN, "child pool reached limit of processes");
428: return (-1);
429: }
430:
1.1 jfb 431: if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, svec) == -1) {
432: cvs_log(LP_ERRNO, "failed to create socket pair");
433: return (-1);
434: }
435:
436: /*
437: * We need to temporarily regain original privileges in order for the
438: * child to chroot().
439: */
440: if (seteuid(0) == -1) {
441: cvs_log(LP_ERRNO, "failed to regain privileges");
442: return (-1);
443: }
444:
445: pid = fork();
446: if (pid == -1) {
447: cvs_log(LP_ERRNO, "failed to fork child");
448: (void)close(svec[0]);
449: (void)close(svec[1]);
450: return (-1);
451: }
452:
453: if (pid == 0) {
454: cvsd_privfd = svec[1];
455: (void)close(svec[0]);
456:
457: cvs_log(LP_INFO, "changing root to %s", cvsd_root);
458: if (chroot(cvsd_root) == -1) {
459: cvs_log(LP_ERRNO, "failed to chroot to `%s'",
460: cvsd_root);
461: exit(EX_OSERR);
462: }
463: (void)chdir("/");
464:
1.2 jfb 465: if (cvsd_privdrop() < 0)
1.1 jfb 466: exit(EX_OSERR);
467:
468: setproctitle("%s [child %d]", __progname, getpid());
469:
470: cvsd_child_loop();
1.7 jfb 471: exit(0);
1.1 jfb 472: }
473:
474: cvs_log(LP_INFO, "spawning child %d", pid);
475:
476: if (seteuid(cvsd_uid) == -1) {
477: cvs_log(LP_ERRNO, "failed to redrop privs");
478: return (-1);
479: }
480:
481: chp = (struct cvsd_child *)malloc(sizeof(*chp));
482: if (chp == NULL) {
483: cvs_log(LP_ERRNO, "failed to allocate child data");
484: return (-1);
485: }
486:
487: chp->ch_pid = pid;
488: chp->ch_sock = svec[0];
1.3 jfb 489: chp->ch_state = CVSD_ST_IDLE;
490:
1.7 jfb 491: signal(SIGCHLD, SIG_IGN);
1.3 jfb 492: TAILQ_INSERT_TAIL(&cvsd_children, chp, ch_list);
1.7 jfb 493: cvsd_chnum++;
1.8 jfb 494: signal(SIGCHLD, cvsd_sighdlr);
1.3 jfb 495:
496: if (chpp != NULL)
497: *chpp = chp;
1.1 jfb 498: (void)close(svec[1]);
1.3 jfb 499: return (1);
500: }
501:
502:
503: /*
504: * cvsd_child_reap()
505: *
1.7 jfb 506: * Wait for a child's status and perform the proper actions depending on it.
507: * If the child has exited or has been terminated by a signal, it will be
508: * removed from the list and new children will be created until the pool has
509: * at least <cvsd_chmin> children in it.
1.3 jfb 510: * Returns 0 on success, or -1 on failure.
511: */
512:
513: int
1.7 jfb 514: cvsd_child_reap(void)
1.3 jfb 515: {
1.7 jfb 516: pid_t pid;
517: int status;
518: struct cvsd_child *ch;
1.3 jfb 519:
1.7 jfb 520: pid = wait(&status);
521: if (pid == -1) {
522: cvs_log(LP_ERRNO, "failed to wait for child");
523: return (-1);
524: }
525:
526: TAILQ_FOREACH(ch, &cvsd_children, ch_list) {
527: if (ch->ch_pid == pid) {
528: if (WIFEXITED(status)) {
529: cvs_log(LP_WARN,
530: "child %d exited with status %d",
531: pid, WEXITSTATUS(status));
532: }
533: else if (WIFSIGNALED(status)) {
534: cvs_log(LP_WARN,
535: "child %d terminated with signal %d",
536: pid, WTERMSIG(status));
537: }
538: else {
539: cvs_log(LP_ERR, "HOLY SHIT!");
540: }
541:
542: signal(SIGCHLD, SIG_IGN);
543: TAILQ_REMOVE(&cvsd_children, ch, ch_list);
544: cvsd_chnum--;
1.8 jfb 545: signal(SIGCHLD, cvsd_sighdlr);
1.7 jfb 546:
547: break;
548: }
549: }
550:
551: while (cvsd_chnum < cvsd_chmin)
552: cvsd_child_fork(NULL);
1.3 jfb 553:
554: return (0);
555: }
556:
557:
558: /*
559: * cvsd_child_get()
560: *
561: * Find a child process in idle state and return a pointer to the child's
562: * structure. If there are no available child processes, a new one will be
563: * created unless the number of children has attained the maximum, in which
564: * case NULL is returned.
565: */
566:
567: struct cvsd_child*
568: cvsd_child_get(void)
569: {
570: struct cvsd_child *chp;
1.1 jfb 571:
1.3 jfb 572: TAILQ_FOREACH(chp, &cvsd_children, ch_list)
573: if (chp->ch_state == CVSD_ST_IDLE)
574: return (chp);
575:
576: /* no available child, attempt to fork a new one */
577: chp = NULL;
578: if ((cvsd_chnum < cvsd_chmax) && (cvsd_child_fork(&chp) < 0))
579: return (NULL);
580:
581: return (chp);
1.1 jfb 582: }
583:
584:
1.3 jfb 585:
1.1 jfb 586: /*
587: * cvsd_parent_loop()
588: *
589: * Main loop of the parent cvsd process, which listens on its end of the
1.3 jfb 590: * local socket for requests from the cvs(1) program and on any outstanding
591: * messages from the children.
1.1 jfb 592: */
593:
594: static void
595: cvsd_parent_loop(void)
596: {
1.3 jfb 597: int cfd, timeout, ret;
1.1 jfb 598: nfds_t nfds, i;
599: struct pollfd *pfd;
600: struct cvsd_child *chp;
601:
602: nfds = 0;
603: timeout = INFTIM;
1.3 jfb 604: pfd = NULL;
1.1 jfb 605:
606: for (;;) {
1.3 jfb 607: if (!running)
608: break;
609:
610: if (restart) {
611: /* restart server */
612: }
1.7 jfb 613:
614: if (cvsd_sigchld) {
615: cvsd_sigchld = 0;
616: cvsd_child_reap();
617: }
1.8 jfb 618: if (cvsd_siginfo) {
619: cvsd_siginfo = 0;
620: cvsd_report();
621: }
1.7 jfb 622:
1.3 jfb 623: nfds = cvsd_chnum + 1;
1.1 jfb 624: pfd = (struct pollfd *)realloc(pfd,
625: nfds * sizeof(struct pollfd));
626: if (pfd == NULL) {
627: cvs_log(LP_ERRNO, "failed to reallocate polling data");
628: return;
629: }
630:
1.3 jfb 631: pfd[0].fd = cvsd_sock;
632: pfd[0].events = POLLIN;
633: pfd[0].revents = 0;
634: i = 1;
1.1 jfb 635: TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
636: pfd[i].fd = chp->ch_sock;
637: pfd[i].events = POLLIN;
638: pfd[i].revents = 0;
639: i++;
640:
1.3 jfb 641: if (i == nfds) /* just a precaution */
1.1 jfb 642: break;
643: }
644:
645: ret = poll(pfd, nfds, timeout);
646: if (ret == -1) {
1.7 jfb 647: if (errno == EINTR)
648: continue;
1.1 jfb 649: cvs_log(LP_ERRNO, "poll error");
650: break;
651: }
652:
1.3 jfb 653: if (pfd[0].revents & (POLLERR|POLLNVAL)) {
654: cvs_log(LP_ERR, "poll error on request socket");
655: }
656: else if (pfd[0].revents & POLLIN) {
657: cfd = cvsd_sock_accept(pfd[0].fd);
658: if (cfd == -1)
659: chp = cvsd_child_get();
660: if (chp == NULL) {
661: cvs_log(LP_ALERT,
662: "request queue not implemented");
663: break;
664: }
665:
666: if (cvsd_sendmsg(chp->ch_sock, CVSD_MSG_PASSFD,
667: &cfd, sizeof(cfd)) < 0)
668: break;
669:
670: /* mark the child as busy */
671: chp->ch_state = CVSD_ST_BUSY;
672: }
673:
1.1 jfb 674: chp = TAILQ_FIRST(&cvsd_children);
1.3 jfb 675: for (i = 1; i < nfds; i++) {
1.1 jfb 676: if (pfd[i].revents & (POLLERR|POLLNVAL)) {
677: cvs_log(LP_ERR,
678: "poll error on child socket (PID %d)",
679: chp->ch_pid);
680: }
1.3 jfb 681: else if (pfd[i].revents & POLLIN)
1.1 jfb 682: cvsd_msghdlr(chp, pfd[i].fd);
683:
684: chp = TAILQ_NEXT(chp, ch_list);
685: }
686:
687: }
1.7 jfb 688:
689: /* broadcast a shutdown message to children */
690: TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
691: (void)cvsd_sendmsg(chp->ch_sock, CVSD_MSG_SHUTDOWN, NULL, 0);
692: }
1.1 jfb 693: }
694:
695:
696: /*
697: * cvsd_child_loop()
698: *
699: */
700:
701: static void
702: cvsd_child_loop(void)
703: {
704: int ret, timeout;
1.7 jfb 705: u_int mtype;
706: size_t mlen;
707: char mbuf[CVSD_MSG_MAXLEN];
1.1 jfb 708: struct pollfd pfd[1];
709:
710: pfd[0].fd = cvsd_privfd;
711: pfd[0].events = POLLIN;
712: timeout = INFTIM;
713:
1.7 jfb 714: while (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:
740: break;
1.7 jfb 741: case CVSD_MSG_SHUTDOWN:
742: running = 0;
743: break;
744: default:
745: cvs_log(LP_ERR,
746: "unexpected message type %u from parent",
747: mtype);
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) ||
! 870: (what == CVSD_SET_USER) || (what == CVSD_SET_GROUP)) {
1.7 jfb 871: str = strdup(va_arg(vap, char *));
872: if (str == NULL) {
1.9 ! jfb 873: cvs_log(LP_ERRNO, "failed to set string");
1.7 jfb 874: return (-1);
875: }
1.9 ! jfb 876: }
! 877:
! 878: switch (what) {
! 879: case CVSD_SET_ROOT:
1.7 jfb 880: if (cvsd_root != NULL)
881: free(cvsd_root);
1.3 jfb 882: cvsd_root = str;
883: break;
1.9 ! jfb 884: case CVSD_SET_SOCK:
! 885: if (cvsd_sock_path != NULL)
! 886: free(cvsd_sock_path);
! 887: cvsd_sock_path = str;
! 888: if (cvsd_sock_open() < 0)
! 889: return (-1);
! 890: break;
! 891: case CVSD_SET_USER:
! 892: if (cvsd_user != NULL)
! 893: free(cvsd_user);
! 894: cvsd_user = str;
! 895: break;
! 896: case CVSD_SET_GROUP:
! 897: if (cvsd_group != NULL)
! 898: free(cvsd_group);
! 899: cvsd_group = str;
! 900: break;
1.3 jfb 901: case CVSD_SET_CHMIN:
902: cvsd_chmin = va_arg(vap, int);
903: /* we should increase the number of children accordingly */
904: break;
905: case CVSD_SET_CHMAX:
906: cvsd_chmax = va_arg(vap, int);
907: /* we should decrease the number of children accordingly */
908: break;
909: case CVSD_SET_ADDR:
1.7 jfb 910: /* this is more like an add than a set */
1.3 jfb 911: break;
912: default:
913: cvs_log(LP_ERR, "invalid field to set");
914: return (-1);
915: }
916:
917: va_end(vap);
918:
919: return (0);
1.8 jfb 920: }
921:
922:
923: /*
924: * cvsd_report()
925: */
926:
927: static void
928: cvsd_report(void)
929: {
930: u_int nb_idle, nb_busy, nb_unknown;
931: struct cvsd_child *ch;
932:
933: nb_idle = 0;
934: nb_busy = 0;
935: nb_unknown = 0;
936:
937: signal(SIGCHLD, SIG_IGN);
938: TAILQ_FOREACH(ch, &cvsd_children, ch_list) {
939: if (ch->ch_state == CVSD_ST_IDLE)
940: nb_idle++;
941: else if (ch->ch_state == CVSD_ST_BUSY)
942: nb_busy++;
943: else if (ch->ch_state == CVSD_ST_UNKNOWN)
944: nb_unknown++;
945: }
946:
947: cvs_log(LP_WARN, "%u children, %u idle, %u busy, %u unknown",
948: cvsd_chnum, nb_idle, nb_busy, nb_unknown);
949:
950: TAILQ_FOREACH(ch, &cvsd_children, ch_list)
951: cvs_log(LP_WARN, "");
952: signal(SIGCHLD, cvsd_sighdlr);
1.1 jfb 953: }