commit 26678adde25a1fb7fa3e4b78c82a888cc3b767e4 from: Omar Polo date: Thu Nov 16 21:40:04 2023 UTC gotwebd: get rid of proc.[ch] proc.c really shines when there's a network of different types of processes, potentially with a various number of instances each, that needs to exchange messages. Gotwebd instead has a much simpler design, and using proc.c causes more overhead (/headaches) than it solves. So, this attempts to provide the same functionalities but with a much simpler implementation that fits gotwebd better. ok stsp@ commit - 03e70dd4d41906645718ee5b780d7e948404f292 commit + 26678adde25a1fb7fa3e4b78c82a888cc3b767e4 blob - 8a18107cdc7ef357964ae5a8c38a6e1205874e12 blob + 258caeda8f63d90382ebfba64f426fcbc541a389 --- gotwebd/Makefile +++ gotwebd/Makefile @@ -7,7 +7,7 @@ SUBDIR = libexec .include "Makefile.inc" PROG = gotwebd -SRCS = config.c sockets.c log.c gotwebd.c parse.y proc.c \ +SRCS = config.c sockets.c log.c gotwebd.c parse.y \ fcgi.c gotweb.c got_operations.c tmpl.c pages.c SRCS += blame.c commit_graph.c delta.c diff.c \ diffreg.c error.c object.c object_cache.c \ blob - 1c8057cb99a030e7a3b45871f35124b82101e918 blob + 775d88bd837ed1c57363aea14c95b1793c999afa --- gotwebd/config.c +++ gotwebd/config.c @@ -39,31 +39,18 @@ #include "got_opentemp.h" #include "got_reference.h" -#include "proc.h" #include "gotwebd.h" int config_init(struct gotwebd *env) { - struct privsep *ps = env->gotwebd_ps; - unsigned int what; - strlcpy(env->httpd_chroot, D_HTTPD_CHROOT, sizeof(env->httpd_chroot)); - /* Global configuration. */ - if (privsep_process == PROC_GOTWEBD) - env->prefork_gotwebd = GOTWEBD_NUMPROC; + env->prefork_gotwebd = GOTWEBD_NUMPROC; + env->server_cnt = 0; + TAILQ_INIT(&env->servers); + TAILQ_INIT(&env->sockets); - ps->ps_what[PROC_GOTWEBD] = CONFIG_ALL; - ps->ps_what[PROC_SOCKS] = CONFIG_SOCKS; - - /* Other configuration. */ - what = ps->ps_what[privsep_process]; - if (what & CONFIG_SOCKS) { - env->server_cnt = 0; - TAILQ_INIT(&env->servers); - TAILQ_INIT(&env->sockets); - } return 0; } @@ -71,23 +58,17 @@ int config_getcfg(struct gotwebd *env, struct imsg *imsg) { /* nothing to do but tell gotwebd configuration is done */ - if (privsep_process != PROC_GOTWEBD) - proc_compose(env->gotwebd_ps, PROC_GOTWEBD, - IMSG_CFG_DONE, NULL, 0); - + if (sockets_compose_main(env, IMSG_CFG_DONE, NULL, 0) == -1) + fatal("sockets_compose_main IMSG_CFG_DONE"); return 0; } int config_setserver(struct gotwebd *env, struct server *srv) { - struct server ssrv; - struct privsep *ps = env->gotwebd_ps; - - memcpy(&ssrv, srv, sizeof(ssrv)); - if (proc_compose(ps, PROC_SOCKS, IMSG_CFG_SRV, &ssrv, sizeof(ssrv)) + if (main_compose_sockets(env, IMSG_CFG_SRV, -1, srv, sizeof(*srv)) == -1) - fatal("proc_compose"); + fatal("main_compose_sockets IMSG_CFG_SRV"); return 0; } @@ -97,17 +78,11 @@ config_getserver(struct gotwebd *env, struct imsg *ims struct server *srv; uint8_t *p = imsg->data; - IMSG_SIZE_CHECK(imsg, &srv); - srv = calloc(1, sizeof(*srv)); if (srv == NULL) fatalx("%s: calloc", __func__); - if (IMSG_DATA_SIZE(imsg) != sizeof(*srv)) { - log_debug("%s: imsg size error", __func__); - free(srv); - return 1; - } + IMSG_SIZE_CHECK(imsg, srv); memcpy(srv, p, sizeof(*srv)); srv->cached_repos = calloc(GOTWEBD_REPO_CACHESIZE, @@ -129,62 +104,15 @@ config_getserver(struct gotwebd *env, struct imsg *ims int config_setsock(struct gotwebd *env, struct socket *sock) { - struct privsep *ps = env->gotwebd_ps; - struct socket_conf s; - int id; - int fd = -1, n, m; - struct iovec iov[6]; - size_t c; - unsigned int what; - /* open listening sockets */ if (sockets_privinit(env, sock) == -1) return -1; - for (id = 0; id < PROC_MAX; id++) { - what = ps->ps_what[id]; + if (main_compose_sockets(env, IMSG_CFG_SOCK, sock->fd, + &sock->conf, sizeof(sock->conf)) == -1) + fatal("main_compose_sockets IMSG_CFG_SOCK"); - if ((what & CONFIG_SOCKS) == 0 || id == privsep_process) - continue; - - memcpy(&s, &sock->conf, sizeof(s)); - - c = 0; - iov[c].iov_base = &s; - iov[c++].iov_len = sizeof(s); - - if (id == PROC_SOCKS) { - /* XXX imsg code will close the fd after 1st call */ - n = -1; - proc_range(ps, id, &n, &m); - for (n = 0; n < m; n++) { - if (sock->fd == -1) - fd = -1; - else if ((fd = dup(sock->fd)) == -1) - return 1; - if (proc_composev_imsg(ps, id, n, IMSG_CFG_SOCK, - -1, fd, iov, c) != 0) { - log_warn("%s: failed to compose " - "IMSG_CFG_SOCK imsg", - __func__); - return 1; - } - if (proc_flush_imsg(ps, id, n) == -1) { - log_warn("%s: failed to flush " - "IMSG_CFG_SOCK imsg", - __func__); - return 1; - } - } - } - } - - /* Close socket early to prevent fd exhaustion in gotwebd. */ - if (sock->fd != -1) { - close(sock->fd); - sock->fd = -1; - } - + sock->fd = -1; return 0; } @@ -237,60 +165,20 @@ config_getsock(struct gotwebd *env, struct imsg *imsg) int config_setfd(struct gotwebd *env, struct socket *sock) { - struct privsep *ps = env->gotwebd_ps; - int id, s; - int fd = -1, n, m, j; - struct iovec iov[6]; - size_t c; - unsigned int what; + int i, fd; log_debug("%s: Allocating %d file descriptors", __func__, PRIV_FDS__MAX + GOTWEB_PACK_NUM_TEMPFILES); - for (j = 0; j < PRIV_FDS__MAX + GOTWEB_PACK_NUM_TEMPFILES; j++) { - for (id = 0; id < PROC_MAX; id++) { - what = ps->ps_what[id]; - - if ((what & CONFIG_SOCKS) == 0 || id == privsep_process) - continue; - - s = sock->conf.id; - c = 0; - iov[c].iov_base = &s; - iov[c++].iov_len = sizeof(s); - - if (id == PROC_SOCKS) { - /* - * XXX imsg code will close the fd - * after 1st call - */ - n = -1; - proc_range(ps, id, &n, &m); - for (n = 0; n < m; n++) { - fd = got_opentempfd(); - if (fd == -1) - return 1; - if (proc_composev_imsg(ps, id, n, - IMSG_CFG_FD, -1, fd, iov, c) != 0) { - log_warn("%s: failed to compose " - "IMSG_CFG_FD imsg", - __func__); - return 1; - } - if (proc_flush_imsg(ps, id, n) == -1) { - log_warn("%s: failed to flush " - "IMSG_CFG_FD imsg", - __func__); - return 1; - } - } - } - } - - /* Close fd early to prevent fd exhaustion in gotwebd. */ - if (fd != -1) - close(fd); + for (i = 0; i < PRIV_FDS__MAX + GOTWEB_PACK_NUM_TEMPFILES; i++) { + fd = got_opentempfd(); + if (fd == -1) + fatal("got_opentemp"); + if (main_compose_sockets(env, IMSG_CFG_FD, fd, + &sock->conf.id, sizeof(sock->conf.id)) == -1) + fatal("main_compose_sockets IMSG_CFG_FD"); } + return 0; } blob - b6905f7fda842f60988300371ff61ab63db68a4c blob + 202f1af662fe84b6759cc1a1c53efca43470da98 --- gotwebd/fcgi.c +++ gotwebd/fcgi.c @@ -35,7 +35,6 @@ #include "got_error.h" #include "got_reference.h" -#include "proc.h" #include "gotwebd.h" #include "tmpl.h" blob - 6d9d5cf8b5507c4d545eca925df5fe128789c73a blob + 45a8a36ea39f6f705bfc81c145d550389dd82872 --- gotwebd/got_operations.c +++ gotwebd/got_operations.c @@ -39,7 +39,6 @@ #include "got_blame.h" #include "got_privsep.h" -#include "proc.h" #include "gotwebd.h" static const struct got_error *got_init_repo_commit(struct repo_commit **); blob - 072c326702282d6c37c250bb57b593f5ce07776f blob + 807b59726fb1bd60b655c7536a08f348058f1da3 --- gotwebd/gotweb.c +++ gotwebd/gotweb.c @@ -49,7 +49,6 @@ #include "got_blame.h" #include "got_privsep.h" -#include "proc.h" #include "gotwebd.h" #include "tmpl.h" blob - 9b7b6853658f6996141e0355f9a0a6f08fca0c7c blob + 4017a8fd50fbe849af03accbf3ef5e16ba4eb232 --- gotwebd/gotwebd.c +++ gotwebd/gotwebd.c @@ -42,7 +42,6 @@ #include "got_opentemp.h" #include "got_reference.h" -#include "proc.h" #include "gotwebd.h" __dead void usage(void); @@ -52,40 +51,138 @@ int gotwebd_configure(struct gotwebd *); void gotwebd_configure_done(struct gotwebd *); void gotwebd_sighdlr(int sig, short event, void *arg); void gotwebd_shutdown(void); -int gotwebd_dispatch_sockets(int, struct privsep_proc *, struct imsg *); +void gotwebd_dispatch_sockets(int, short, void *); struct gotwebd *gotwebd_env; -static struct privsep_proc procs[] = { - { "sockets", PROC_SOCKS, gotwebd_dispatch_sockets, sockets, - sockets_shutdown }, -}; +void +imsg_event_add(struct imsgev *iev) +{ + if (iev->handler == NULL) { + imsg_flush(&iev->ibuf); + return; + } + iev->events = EV_READ; + if (iev->ibuf.w.queued) + iev->events |= EV_WRITE; + + event_del(&iev->ev); + event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); + event_add(&iev->ev, NULL); +} + int -gotwebd_dispatch_sockets(int fd, struct privsep_proc *p, struct imsg *imsg) +imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, + pid_t pid, int fd, const void *data, uint16_t datalen) { - struct privsep *ps = p->p_ps; - struct gotwebd *env = ps->ps_env; + int ret; - switch (imsg->hdr.type) { - case IMSG_CFG_DONE: - gotwebd_configure_done(env); - break; - default: - return (-1); + ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen); + if (ret == -1) + return (ret); + imsg_event_add(iev); + return (ret); +} + +int +main_compose_sockets(struct gotwebd *env, uint32_t type, int fd, + const void *data, uint16_t len) +{ + size_t i; + int ret, d; + + for (i = 0; i < env->nserver; ++i) { + d = -1; + if (fd != -1 && (d = dup(fd)) == -1) + return (-1); + + ret = imsg_compose_event(&env->iev_server[i], type, 0, -1, + d, data, len); + if (ret == -1) + return (-1); + + /* prevent fd exhaustion */ + if (d != -1) { + do { + ret = imsg_flush(&env->iev_server[i].ibuf); + } while (ret == -1 && errno == EAGAIN); + if (ret == -1) + return (-1); + imsg_event_add(&env->iev_server[i]); + } } - return (0); + if (fd != -1) + close(fd); + + return 0; } +int +sockets_compose_main(struct gotwebd *env, uint32_t type, const void *d, + uint16_t len) +{ + return (imsg_compose_event(env->iev_parent, type, 0, -1, -1, d, len)); +} + void +gotwebd_dispatch_sockets(int fd, short event, void *arg) +{ + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + struct gotwebd *env = gotwebd_env; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_CFG_DONE: + gotwebd_configure_done(env); + break; + default: + fatalx("%s: unknown imsg type %d", __func__, + imsg.hdr.type); + } + + imsg_free(&imsg); + } + + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void gotwebd_sighdlr(int sig, short event, void *arg) { /* struct privsep *ps = arg; */ - if (privsep_process != PROC_GOTWEBD) - return; - switch (sig) { case SIGHUP: log_info("%s: ignoring SIGHUP", __func__); @@ -105,6 +202,54 @@ gotwebd_sighdlr(int sig, short event, void *arg) } } +static int +spawn_socket_process(struct gotwebd *env, const char *argv0, int n) +{ + const char *argv[5]; + int argc = 0; + int p[2]; + pid_t pid; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1) + fatal("socketpair"); + + switch (pid = fork()) { + case -1: + fatal("fork"); + case 0: /* child */ + break; + default: /* parent */ + close(p[0]); + imsg_init(&env->iev_server[n].ibuf, p[1]); + env->iev_server[n].handler = gotwebd_dispatch_sockets; + env->iev_server[n].data = &env->iev_server[n]; + event_set(&env->iev_server[n].ev, p[1], EV_READ, + gotwebd_dispatch_sockets, &env->iev_server[n]); + event_add(&env->iev_server[n].ev, NULL); + return 0; + } + + close(p[1]); + + argv[argc++] = argv0; + argv[argc++] = "-S"; + if (env->gotwebd_debug) + argv[argc++] = "-d"; + if (env->gotwebd_verbose) + argv[argc++] = "-v"; + argv[argc] = NULL; + + if (p[0] != 3) { + if (dup2(p[0], 3) == -1) + fatal("dup2"); + } else if (fcntl(p[0], F_SETFD, 0) == -1) + fatal("fcntl"); + + /* obnoxious cast */ + execvp(argv0, (char * const *)argv); + fatal("execvp %s", argv0); +} + __dead void usage(void) { @@ -116,25 +261,27 @@ usage(void) int main(int argc, char **argv) { - struct gotwebd *env; - struct privsep *ps; - unsigned int proc; - int ch; - const char *conffile = GOTWEBD_CONF; - enum privsep_procid proc_id = PROC_GOTWEBD; - int proc_instance = 0; - const char *errp, *title = NULL; - int argc0 = argc; + struct event sigint, sigterm, sighup, sigpipe, sigusr1; + struct gotwebd *env; + struct passwd *pw; + int ch, i; + int no_action = 0; + int server_proc = 0; + const char *conffile = GOTWEBD_CONF; + const char *argv0; + if ((argv0 = argv[0]) == NULL) + argv0 = "gotwebd"; + env = calloc(1, sizeof(*env)); if (env == NULL) fatal("%s: calloc", __func__); + config_init(env); /* log to stderr until daemonized */ log_init(1, LOG_DAEMON); - /* XXX: add s and S for both sockets */ - while ((ch = getopt(argc, argv, "D:df:I:nP:v")) != -1) { + while ((ch = getopt(argc, argv, "D:df:nSv")) != -1) { switch (ch) { case 'D': if (cmdline_symset(optarg) < 0) @@ -142,26 +289,16 @@ main(int argc, char **argv) optarg); break; case 'd': - env->gotwebd_debug = 2; + env->gotwebd_debug = 1; break; case 'f': conffile = optarg; break; - case 'I': - proc_instance = strtonum(optarg, 0, - PROC_MAX_INSTANCES, &errp); - if (errp) - fatalx("invalid process instance"); - break; case 'n': - env->gotwebd_debug = 2; - env->gotwebd_noaction = 1; + no_action = 1; break; - case 'P': - title = optarg; - proc_id = proc_getid(procs, nitems(procs), title); - if (proc_id == PROC_MAX) - fatalx("invalid process name"); + case 'S': + server_proc = 1; break; case 'v': env->gotwebd_verbose++; @@ -175,72 +312,77 @@ main(int argc, char **argv) if (argc > 0) usage(); - ps = calloc(1, sizeof(*ps)); - if (ps == NULL) - fatal("%s: calloc:", __func__); - gotwebd_env = env; - env->gotwebd_ps = ps; - ps->ps_env = env; env->gotwebd_conffile = conffile; if (parse_config(env->gotwebd_conffile, env) == -1) exit(1); - if (env->gotwebd_noaction && !env->gotwebd_debug) - env->gotwebd_debug = 1; + if (no_action) { + fprintf(stderr, "configuration OK\n"); + exit(0); + } /* check for root privileges */ - if (env->gotwebd_noaction == 0) { - if (geteuid()) - fatalx("need root privileges"); - } + if (geteuid()) + fatalx("need root privileges"); - ps->ps_pw = getpwnam(GOTWEBD_USER); - if (ps->ps_pw == NULL) + pw = getpwnam(GOTWEBD_USER); + if (pw == NULL) fatalx("unknown user %s", GOTWEBD_USER); + env->pw = pw; log_init(env->gotwebd_debug, LOG_DAEMON); log_setverbose(env->gotwebd_verbose); - if (env->gotwebd_noaction) - ps->ps_noaction = 1; + if (server_proc) { + setproctitle("sockets"); + log_procinit("sockets"); - ps->ps_instances[PROC_SOCKS] = env->prefork_gotwebd; - ps->ps_instance = proc_instance; - if (title != NULL) - ps->ps_title[proc_id] = title; + if (chroot(pw->pw_dir) == -1) + fatal("chroot %s", pw->pw_dir); + if (chdir("/") == -1) + fatal("chdir /"); + if (setgroups(1, &pw->pw_gid) == -1 || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) + fatal("failed to drop privileges"); - for (proc = 0; proc < nitems(procs); proc++) - procs[proc].p_chroot = env->httpd_chroot; + sockets(env, 3); + return 1; + } - /* only the gotwebd returns */ - proc_init(ps, procs, nitems(procs), argc0, argv, proc_id); - - log_procinit("gotwebd"); if (!env->gotwebd_debug && daemon(0, 0) == -1) - fatal("can't daemonize"); + fatal("daemon"); - if (ps->ps_noaction == 0) - log_info("%s startup", getprogname()); - event_init(); - signal_set(&ps->ps_evsigint, SIGINT, gotwebd_sighdlr, ps); - signal_set(&ps->ps_evsigterm, SIGTERM, gotwebd_sighdlr, ps); - signal_set(&ps->ps_evsighup, SIGHUP, gotwebd_sighdlr, ps); - signal_set(&ps->ps_evsigpipe, SIGPIPE, gotwebd_sighdlr, ps); - signal_set(&ps->ps_evsigusr1, SIGUSR1, gotwebd_sighdlr, ps); + env->nserver = env->prefork_gotwebd; + env->iev_server = calloc(env->nserver, sizeof(*env->iev_server)); + if (env->iev_server == NULL) + fatal("calloc"); - signal_add(&ps->ps_evsigint, NULL); - signal_add(&ps->ps_evsigterm, NULL); - signal_add(&ps->ps_evsighup, NULL); - signal_add(&ps->ps_evsigpipe, NULL); - signal_add(&ps->ps_evsigusr1, NULL); + for (i = 0; i < env->nserver; ++i) { + if (spawn_socket_process(env, argv0, i) == -1) + fatal("spawn_socket_process"); + } - if (!env->gotwebd_noaction) - proc_connect(ps); + log_procinit("gotwebd"); + log_info("%s startup", getprogname()); + + signal_set(&sigint, SIGINT, gotwebd_sighdlr, env); + signal_set(&sigterm, SIGTERM, gotwebd_sighdlr, env); + signal_set(&sighup, SIGHUP, gotwebd_sighdlr, env); + signal_set(&sigpipe, SIGPIPE, gotwebd_sighdlr, env); + signal_set(&sigusr1, SIGUSR1, gotwebd_sighdlr, env); + + signal_add(&sigint, NULL); + signal_add(&sigterm, NULL); + signal_add(&sighup, NULL); + signal_add(&sigpipe, NULL); + signal_add(&sigusr1, NULL); + if (gotwebd_configure(env) == -1) fatalx("configuration failed"); @@ -275,14 +417,7 @@ gotwebd_configure(struct gotwebd *env) { struct server *srv; struct socket *sock; - int id; - if (env->gotwebd_noaction) { - fprintf(stderr, "configuration OK\n"); - proc_kill(env->gotwebd_ps); - exit(0); - } - /* gotweb need to reload its config. */ env->gotwebd_reload = env->prefork_gotwebd; @@ -300,11 +435,8 @@ gotwebd_configure(struct gotwebd *env) fatalx("%s: send priv_fd error", __func__); } - for (id = 0; id < PROC_MAX; id++) { - if (id == privsep_process) - continue; - proc_compose(env->gotwebd_ps, id, IMSG_CFG_DONE, NULL, 0); - } + if (main_compose_sockets(env, IMSG_CFG_DONE, -1, NULL, 0) == -1) + fatal("main_compose_sockets IMSG_CFG_DONE"); return (0); } @@ -312,31 +444,44 @@ gotwebd_configure(struct gotwebd *env) void gotwebd_configure_done(struct gotwebd *env) { - int id; - if (env->gotwebd_reload == 0) { log_warnx("%s: configuration already finished", __func__); return; } env->gotwebd_reload--; - if (env->gotwebd_reload == 0) { - for (id = 0; id < PROC_MAX; id++) { - if (id == privsep_process) - continue; - proc_compose(env->gotwebd_ps, id, IMSG_CTL_START, - NULL, 0); - } - } + if (env->gotwebd_reload == 0 && + main_compose_sockets(env, IMSG_CTL_START, -1, NULL, 0) == -1) + fatal("main_compose_sockets IMSG_CTL_START"); } void gotwebd_shutdown(void) { - proc_kill(gotwebd_env->gotwebd_ps); + struct gotwebd *env = gotwebd_env; + pid_t pid; + int i, status; - /* unlink(gotwebd_env->gotweb->gotweb_conf.gotweb_unix_socket_name); */ - /* free(gotwebd_env->gotweb); */ + for (i = 0; i < env->nserver; ++i) { + event_del(&env->iev_server[i].ev); + imsg_clear(&env->iev_server[i].ibuf); + close(env->iev_server[i].ibuf.fd); + env->iev_server[i].ibuf.fd = -1; + } + + do { + pid = waitpid(WAIT_ANY, &status, 0); + if (pid <= 0) + continue; + + if (WIFSIGNALED(status)) + log_warnx("lost child: pid %u terminated; signal %d", + pid, WTERMSIG(status)); + else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) + log_warnx("lost child: pid %u exited abnormally", + pid); + } while (pid != -1 || (pid == -1 && errno == EINTR)); + free(gotwebd_env); log_warnx("gotwebd terminating"); blob - f2e1d1c756973046a69181f403f1187c8b7432cc blob + d47297375a5f9fe3888d5e964455fbc3a3d9416a --- gotwebd/gotwebd.h +++ gotwebd/gotwebd.h @@ -49,6 +49,8 @@ #define GOTWEBD_NUMPROC 3 #define GOTWEBD_REPO_CACHESIZE 4 +#define PROC_MAX_INSTANCES 32 + /* GOTWEB DEFAULTS */ #define MAX_QUERYSTRING 2048 #define MAX_DOCUMENT_URI 255 @@ -120,13 +122,28 @@ struct got_tree_entry; struct got_reflist_head; enum imsg_type { - IMSG_CFG_SRV = IMSG_PROC_MAX, + IMSG_CFG_SRV, IMSG_CFG_SOCK, IMSG_CFG_FD, IMSG_CFG_DONE, IMSG_CTL_START, }; +struct imsgev { + struct imsgbuf ibuf; + void (*handler)(int, short, void *); + struct event ev; + void *data; + short events; +}; + +#define IMSG_SIZE_CHECK(imsg, p) do { \ + if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \ + fatalx("bad length imsg received (%s)", #p); \ +} while (0) + +#define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE) + struct env_val { SLIST_ENTRY(env_val) entry; char *val; @@ -345,17 +362,22 @@ struct socket { }; TAILQ_HEAD(socketlist, socket); +struct passwd; struct gotwebd { struct serverlist servers; struct socketlist sockets; - struct privsep *gotwebd_ps; const char *gotwebd_conffile; int gotwebd_debug; int gotwebd_verbose; - int gotwebd_noaction; + struct imsgev *iev_parent; + struct imsgev *iev_server; + size_t nserver; + + struct passwd *pw; + uint16_t prefork_gotwebd; int gotwebd_reload; @@ -439,10 +461,18 @@ extern struct gotwebd *gotwebd_env; typedef int (*got_render_blame_line_cb)(struct template *, const char *, struct blame_line *, int, int); + +/* gotwebd.c */ +void imsg_event_add(struct imsgev *); +int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, + pid_t, int, const void *, uint16_t); +int main_compose_sockets(struct gotwebd *, uint32_t, int, + const void *, uint16_t); +int sockets_compose_main(struct gotwebd *, uint32_t, + const void *, uint16_t); /* sockets.c */ -void sockets(struct privsep *, struct privsep_proc *); -void sockets_shutdown(void); +void sockets(struct gotwebd *, int); void sockets_parse_sockets(struct gotwebd *); void sockets_socket_accept(int, short, void *); int sockets_privinit(struct gotwebd *, struct socket *); @@ -517,3 +547,25 @@ int config_setfd(struct gotwebd *, struct socket *); int config_getfd(struct gotwebd *, struct imsg *); int config_getcfg(struct gotwebd *, struct imsg *); int config_init(struct gotwebd *); + +/* log.c */ +void log_init(int, int); +void log_procinit(const char *); +void log_setverbose(int); +int log_getverbose(void); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void vlog(int, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))); +__dead void fatal(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +__dead void fatalx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); blob - 79d3d334581eddd8ffcc0f02f067e5c64d0be72e blob + 73cbf47a766ba764769018c3e5833cb0414ed1f8 --- gotwebd/log.c +++ gotwebd/log.c @@ -164,7 +164,7 @@ log_debug(const char *emsg, ...) { va_list ap; - if (verbose > 1) { + if (verbose) { va_start(ap, emsg); vlog(LOG_DEBUG, emsg, ap); va_end(ap); blob - a51f74abb8dfc67479ed3558a8c7efd83400e068 blob + d04e49122cc951bb2815072249af493a694624f3 --- gotwebd/pages.tmpl +++ gotwebd/pages.tmpl @@ -33,8 +33,6 @@ #include "got_error.h" #include "got_object.h" #include "got_reference.h" - -#include "proc.h" #include "gotwebd.h" #include "tmpl.h" blob - b02dbb86b14c946386f468402580e3119a1f1e61 blob + 207d9f20026688bdc8849f0125e73b94a285504e --- gotwebd/parse.y +++ gotwebd/parse.y @@ -49,7 +49,6 @@ #include "got_reference.h" -#include "proc.h" #include "gotwebd.h" TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); blob - af1919d98925cc1f5ca97aed9a73ce4eb60bad00 (mode 644) blob + /dev/null --- gotwebd/proc.c +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Copyright (c) 2010 - 2016 Reyk Floeter - * Copyright (c) 2008 Pierre-Yves Ritschard - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "proc.h" - -void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, - int, char **); -void proc_setup(struct privsep *, struct privsep_proc *, unsigned int); -void proc_open(struct privsep *, int, int); -void proc_accept(struct privsep *, int, enum privsep_procid, - unsigned int); -void proc_close(struct privsep *); -void proc_shutdown(struct privsep_proc *); -void proc_sig_handler(int, short, void *); -int proc_dispatch_null(int, struct privsep_proc *, struct imsg *); - -enum privsep_procid privsep_process; - -enum privsep_procid -proc_getid(struct privsep_proc *procs, unsigned int nproc, - const char *proc_name) -{ - struct privsep_proc *p; - unsigned int proc; - - for (proc = 0; proc < nproc; proc++) { - p = &procs[proc]; - if (strcmp(p->p_title, proc_name)) - continue; - - return (p->p_id); - } - - return (PROC_MAX); -} - -void -proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, - int argc, char **argv) -{ - unsigned int proc, nargc, i, proc_i; - char **nargv; - struct privsep_proc *p; - char num[32]; - int fd; - - /* Prepare the new process argv. */ - nargv = calloc(argc + 5, sizeof(char *)); - if (nargv == NULL) - fatal("%s: calloc", __func__); - - /* Copy call argument first. */ - nargc = 0; - nargv[nargc++] = argv[0]; - - /* Set process name argument and save the position. */ - nargv[nargc] = strdup("-P"); - if (nargv[nargc] == NULL) - fatal("%s: strdup", __func__); - nargc++; - proc_i = nargc; - nargc++; - - /* Point process instance arg to stack and copy the original args. */ - nargv[nargc] = strdup("-I"); - if (nargv[nargc] == NULL) - fatal("%s: strdup", __func__); - nargc++; - nargv[nargc++] = num; - for (i = 1; i < (unsigned int) argc; i++) - nargv[nargc++] = argv[i]; - - nargv[nargc] = NULL; - - for (proc = 0; proc < nproc; proc++) { - p = &procs[proc]; - - /* Update args with process title. */ - nargv[proc_i] = (char *)(uintptr_t)p->p_title; - - /* Fire children processes. */ - for (i = 0; i < ps->ps_instances[p->p_id]; i++) { - /* Update the process instance number. */ - snprintf(num, sizeof(num), "%u", i); - - fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_GOTWEBD][0]; - ps->ps_pipes[p->p_id][i].pp_pipes[PROC_GOTWEBD][0] = -1; - - switch (fork()) { - case -1: - fatal("%s: fork", __func__); - break; - case 0: - /* First create a new session */ - if (setsid() == -1) - fatal("setsid"); - - /* Prepare parent socket. */ - if (fd != PROC_GOTWEBD_SOCK_FILENO) { - if (dup2(fd, PROC_GOTWEBD_SOCK_FILENO) - == -1) - fatal("dup2"); - } else if (fcntl(fd, F_SETFD, 0) == -1) - fatal("fcntl"); - - execvp(argv[0], nargv); - fatal("%s: execvp", __func__); - break; - default: - /* Close child end. */ - close(fd); - break; - } - } - } - - free(nargv); -} - -void -proc_connect(struct privsep *ps) -{ - struct imsgev *iev; - unsigned int src, dst, inst; - - /* Don't distribute any sockets if we are not really going to run. */ - if (ps->ps_noaction) - return; - - for (dst = 0; dst < PROC_MAX; dst++) { - /* We don't communicate with ourselves. */ - if (dst == PROC_GOTWEBD) - continue; - - for (inst = 0; inst < ps->ps_instances[dst]; inst++) { - iev = &ps->ps_ievs[dst][inst]; - imsg_init(&iev->ibuf, ps->ps_pp->pp_pipes[dst][inst]); - event_set(&iev->ev, iev->ibuf.fd, iev->events, - iev->handler, iev->data); - event_add(&iev->ev, NULL); - } - } - - /* Distribute the socketpair()s for everyone. */ - for (src = 0; src < PROC_MAX; src++) - for (dst = src; dst < PROC_MAX; dst++) { - /* Parent already distributed its fds. */ - if (src == PROC_GOTWEBD || dst == PROC_GOTWEBD) - continue; - - proc_open(ps, src, dst); - } -} - -void -proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, - int argc, char **argv, enum privsep_procid proc_id) -{ - struct privsep_proc *p = NULL; - struct privsep_pipes *pa, *pb; - unsigned int proc; - unsigned int dst; - int fds[2]; - - /* Don't initiate anything if we are not really going to run. */ - if (ps->ps_noaction) - return; - - if (proc_id == PROC_GOTWEBD) { - privsep_process = PROC_GOTWEBD; - proc_setup(ps, procs, nproc); - - /* - * Create the children sockets so we can use them - * to distribute the rest of the socketpair()s using - * proc_connect() later. - */ - for (dst = 0; dst < PROC_MAX; dst++) { - /* Don't create socket for ourselves. */ - if (dst == PROC_GOTWEBD) - continue; - - for (proc = 0; proc < ps->ps_instances[dst]; proc++) { - pa = &ps->ps_pipes[PROC_GOTWEBD][0]; - pb = &ps->ps_pipes[dst][proc]; - if (socketpair(AF_UNIX, - SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, - PF_UNSPEC, fds) == -1) - fatal("%s: socketpair", __func__); - - pa->pp_pipes[dst][proc] = fds[0]; - pb->pp_pipes[PROC_GOTWEBD][0] = fds[1]; - } - } - - /* Engage! */ - proc_exec(ps, procs, nproc, argc, argv); - return; - } - - /* Initialize a child */ - for (proc = 0; proc < nproc; proc++) { - if (procs[proc].p_id != proc_id) - continue; - p = &procs[proc]; - break; - } - if (p == NULL || p->p_init == NULL) - fatalx("%s: process %d missing process initialization", - __func__, proc_id); - - p->p_init(ps, p); - - fatalx("failed to initiate child process"); -} - -void -proc_accept(struct privsep *ps, int fd, enum privsep_procid dst, - unsigned int n) -{ - struct privsep_pipes *pp = ps->ps_pp; - struct imsgev *iev; - - if (ps->ps_ievs[dst] == NULL) { -#if DEBUG > 1 - log_debug("%s: %s src %d %d to dst %d %d not connected", - __func__, ps->ps_title[privsep_process], - privsep_process, ps->ps_instance + 1, - dst, n + 1); -#endif - close(fd); - return; - } - - if (pp->pp_pipes[dst][n] != -1) { - log_warnx("%s: duplicated descriptor", __func__); - close(fd); - return; - } else - pp->pp_pipes[dst][n] = fd; - - iev = &ps->ps_ievs[dst][n]; - imsg_init(&iev->ibuf, fd); - event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); - event_add(&iev->ev, NULL); -} - -void -proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) -{ - unsigned int i, j, src, dst, id; - struct privsep_pipes *pp; - - /* Initialize parent title, ps_instances and procs. */ - ps->ps_title[PROC_GOTWEBD] = "parent"; - - for (src = 0; src < PROC_MAX; src++) - /* Default to 1 process instance */ - if (ps->ps_instances[src] < 1) - ps->ps_instances[src] = 1; - - for (src = 0; src < nproc; src++) { - procs[src].p_ps = ps; - if (procs[src].p_cb == NULL) - procs[src].p_cb = proc_dispatch_null; - - id = procs[src].p_id; - ps->ps_title[id] = procs[src].p_title; - ps->ps_ievs[id] = calloc(ps->ps_instances[id], - sizeof(struct imsgev)); - if (ps->ps_ievs[id] == NULL) - fatal("%s: calloc", __func__); - - /* With this set up, we are ready to call imsg_init(). */ - for (i = 0; i < ps->ps_instances[id]; i++) { - ps->ps_ievs[id][i].handler = proc_dispatch; - ps->ps_ievs[id][i].events = EV_READ; - ps->ps_ievs[id][i].proc = &procs[src]; - ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i]; - } - } - - /* - * Allocate pipes for all process instances (incl. parent) - * - * - ps->ps_pipes: N:M mapping - * N source processes connected to M destination processes: - * [src][instances][dst][instances], for example - * [PROC_RELAY][3][PROC_CA][3] - * - * - ps->ps_pp: per-process 1:M part of ps->ps_pipes - * Each process instance has a destination array of socketpair fds: - * [dst][instances], for example - * [PROC_GOTWEBD][0] - */ - for (src = 0; src < PROC_MAX; src++) { - /* Allocate destination array for each process */ - ps->ps_pipes[src] = calloc(ps->ps_instances[src], - sizeof(struct privsep_pipes)); - if (ps->ps_pipes[src] == NULL) - fatal("%s: calloc", __func__); - - for (i = 0; i < ps->ps_instances[src]; i++) { - pp = &ps->ps_pipes[src][i]; - - for (dst = 0; dst < PROC_MAX; dst++) { - /* Allocate maximum fd integers */ - pp->pp_pipes[dst] = - calloc(ps->ps_instances[dst], - sizeof(int)); - if (pp->pp_pipes[dst] == NULL) - fatal("%s: calloc", __func__); - - /* Mark fd as unused */ - for (j = 0; j < ps->ps_instances[dst]; j++) - pp->pp_pipes[dst][j] = -1; - } - } - } - - ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance]; -} - -void -proc_kill(struct privsep *ps) -{ - char *cause; - pid_t pid; - int len, status; - - if (privsep_process != PROC_GOTWEBD) - return; - - proc_close(ps); - - do { - pid = waitpid(WAIT_ANY, &status, 0); - if (pid <= 0) - continue; - - if (WIFSIGNALED(status)) { - len = asprintf(&cause, "terminated; signal %d", - WTERMSIG(status)); - } else if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) - len = asprintf(&cause, "exited abnormally"); - else - len = 0; - } else - len = -1; - - if (len == 0) { - /* child exited OK, don't print a warning message */ - } else if (len != -1) { - log_warnx("lost child: pid %u %s", pid, cause); - free(cause); - } else - log_warnx("lost child: pid %u", pid); - } while (pid != -1 || (pid == -1 && errno == EINTR)); -} - -void -proc_open(struct privsep *ps, int src, int dst) -{ - struct privsep_pipes *pa, *pb; - struct privsep_fd pf; - int fds[2]; - unsigned int i, j; - - /* Exchange pipes between process. */ - for (i = 0; i < ps->ps_instances[src]; i++) { - for (j = 0; j < ps->ps_instances[dst]; j++) { - /* Don't create sockets for ourself. */ - if (src == dst && i == j) - continue; - - pa = &ps->ps_pipes[src][i]; - pb = &ps->ps_pipes[dst][j]; - if (socketpair(AF_UNIX, - SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, - PF_UNSPEC, fds) == -1) - fatal("%s: socketpair", __func__); - - pa->pp_pipes[dst][j] = fds[0]; - pb->pp_pipes[src][i] = fds[1]; - - pf.pf_procid = src; - pf.pf_instance = i; - if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD, - -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1) - fatal("%s: proc_compose_imsg", __func__); - - pf.pf_procid = dst; - pf.pf_instance = j; - if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD, - -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1) - fatal("%s: proc_compose_imsg", __func__); - - /* - * We have to flush to send the descriptors and close - * them to avoid the fd ramp on startup. - */ - if (proc_flush_imsg(ps, src, i) == -1 || - proc_flush_imsg(ps, dst, j) == -1) - fatal("%s: imsg_flush", __func__); - } - } -} - -void -proc_close(struct privsep *ps) -{ - unsigned int dst, n; - struct privsep_pipes *pp; - - if (ps == NULL) - return; - - pp = ps->ps_pp; - - for (dst = 0; dst < PROC_MAX; dst++) { - if (ps->ps_ievs[dst] == NULL) - continue; - - for (n = 0; n < ps->ps_instances[dst]; n++) { - if (pp->pp_pipes[dst][n] == -1) - continue; - - /* Cancel the fd, close and invalidate the fd */ - event_del(&(ps->ps_ievs[dst][n].ev)); - imsg_clear(&(ps->ps_ievs[dst][n].ibuf)); - close(pp->pp_pipes[dst][n]); - pp->pp_pipes[dst][n] = -1; - } - free(ps->ps_ievs[dst]); - } -} - -void -proc_shutdown(struct privsep_proc *p) -{ - struct privsep *ps = p->p_ps; - - if (p->p_shutdown != NULL) - (*p->p_shutdown)(); - - proc_close(ps); - - log_info("%s, %s exiting, pid %d", getprogname(), p->p_title, getpid()); - - exit(0); -} - -void -proc_sig_handler(int sig, short event, void *arg) -{ - struct privsep_proc *p = arg; - - switch (sig) { - case SIGINT: - case SIGTERM: - proc_shutdown(p); - break; - case SIGCHLD: - case SIGHUP: - case SIGPIPE: - case SIGUSR1: - /* ignore */ - break; - default: - fatalx("proc_sig_handler: unexpected signal"); - /* NOTREACHED */ - } -} - -void -proc_run(struct privsep *ps, struct privsep_proc *p, - struct privsep_proc *procs, unsigned int nproc, - void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) -{ - struct passwd *pw; - const char *root; - - log_procinit(p->p_title); - - /* Set the process group of the current process */ - setpgid(0, 0); - - /* Use non-standard user */ - if (p->p_pw != NULL) - pw = p->p_pw; - else - pw = ps->ps_pw; - - /* Change root directory */ - if (p->p_chroot != NULL) - root = p->p_chroot; - else - root = pw->pw_dir; - - if (chroot(root) == -1) - fatal("proc_run: chroot"); - if (chdir("/") == -1) - fatal("proc_run: chdir(\"/\")"); - - privsep_process = p->p_id; - - setproctitle("%s", p->p_title); - - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - fatal("proc_run: cannot drop privileges"); - - event_init(); - - signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); - signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); - signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); - signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); - signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); - signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); - - signal_add(&ps->ps_evsigint, NULL); - signal_add(&ps->ps_evsigterm, NULL); - signal_add(&ps->ps_evsigchld, NULL); - signal_add(&ps->ps_evsighup, NULL); - signal_add(&ps->ps_evsigpipe, NULL); - signal_add(&ps->ps_evsigusr1, NULL); - - proc_setup(ps, procs, nproc); - proc_accept(ps, PROC_GOTWEBD_SOCK_FILENO, PROC_GOTWEBD, 0); - - DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title, - ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); - - if (run != NULL) - run(ps, p, arg); - - event_dispatch(); - - proc_shutdown(p); -} - -void -proc_dispatch(int fd, short event, void *arg) -{ - struct imsgev *iev = arg; - struct privsep_proc *p = iev->proc; - struct privsep *ps = p->p_ps; - struct imsgbuf *ibuf; - struct imsg imsg; - ssize_t n; - int verbose; - const char *title; - struct privsep_fd pf; - - title = ps->ps_title[privsep_process]; - ibuf = &iev->ibuf; - - if (event & EV_READ) { - n = imsg_read(ibuf); - if (n == -1 && errno != EAGAIN) - fatal("%s: imsg_read", __func__); - if (n == 0) { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); - return; - } - } - - if (event & EV_WRITE) { - n = msgbuf_write(&ibuf->w); - if (n == -1 && errno != EAGAIN) - fatal("%s: msgbuf_write", __func__); - if (n == 0) { - /* this pipe is dead, so remove the event handler */ - event_del(&iev->ev); - event_loopexit(NULL); - return; - } - } - - for (;;) { - n = imsg_get(ibuf, &imsg); - if (n == -1) - fatal("%s: imsg_get", __func__); - if (n == 0) - break; - -#if DEBUG > 1 - log_debug("%s: %s %d got imsg %d peerid %d from %s %d", - __func__, title, ps->ps_instance + 1, - imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); -#endif - - /* - * Check the message with the program callback - */ - if ((p->p_cb)(fd, p, &imsg) == 0) { - /* Message was handled by the callback, continue */ - imsg_free(&imsg); - continue; - } - - /* - * Generic message handling - */ - switch (imsg.hdr.type) { - case IMSG_CTL_VERBOSE: - log_info("%s", __func__); - IMSG_SIZE_CHECK(&imsg, &verbose); - memcpy(&verbose, imsg.data, sizeof(verbose)); - log_setverbose(verbose); - break; - case IMSG_CTL_PROCFD: - IMSG_SIZE_CHECK(&imsg, &pf); - memcpy(&pf, imsg.data, sizeof(pf)); - proc_accept(ps, imsg.fd, pf.pf_procid, - pf.pf_instance); - break; - default: - log_warnx("%s: %s %d got invalid imsg %d peerid %d " - "from %s %d", - __func__, title, ps->ps_instance + 1, - imsg.hdr.type, imsg.hdr.peerid, - p->p_title, imsg.hdr.pid); - } - imsg_free(&imsg); - } - imsg_event_add(iev); -} - -int -proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) -{ - return (-1); -} - -/* - * imsg helper functions - */ - -void -imsg_event_add(struct imsgev *iev) -{ - if (iev->handler == NULL) { - imsg_flush(&iev->ibuf); - return; - } - - iev->events = EV_READ; - if (iev->ibuf.w.queued) - iev->events |= EV_WRITE; - - event_del(&iev->ev); - event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); - event_add(&iev->ev, NULL); -} - -int -imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, - pid_t pid, int fd, void *data, uint16_t datalen) -{ - int ret; - - ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen); - if (ret == -1) - return (ret); - imsg_event_add(iev); - return (ret); -} - -int -imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, - pid_t pid, int fd, const struct iovec *iov, int iovcnt) -{ - int ret; - - ret = imsg_composev(&iev->ibuf, type, peerid, pid, fd, iov, iovcnt); - if (ret == -1) - return (ret); - imsg_event_add(iev); - return (ret); -} - -void -proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) -{ - if (*n == -1) { - /* Use a range of all target instances */ - *n = 0; - *m = ps->ps_instances[id]; - } else { - /* Use only a single slot of the specified peer process */ - *m = *n + 1; - } -} - -int -proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, - uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) -{ - int m; - - proc_range(ps, id, &n, &m); - for (; n < m; n++) { - if (imsg_compose_event(&ps->ps_ievs[id][n], - type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1) - return (-1); - } - - return (0); -} - -int -proc_compose(struct privsep *ps, enum privsep_procid id, - uint16_t type, void *data, uint16_t datalen) -{ - return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); -} - -int -proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, - uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) -{ - int m; - - proc_range(ps, id, &n, &m); - for (; n < m; n++) - if (imsg_composev_event(&ps->ps_ievs[id][n], - type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1) - return (-1); - - return (0); -} - -int -proc_composev(struct privsep *ps, enum privsep_procid id, - uint16_t type, const struct iovec *iov, int iovcnt) -{ - return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); -} - -int -proc_forward_imsg(struct privsep *ps, struct imsg *imsg, - enum privsep_procid id, int n) -{ - return (proc_compose_imsg(ps, id, n, imsg->hdr.type, - imsg->hdr.peerid, imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg))); -} - -struct imsgbuf * -proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) -{ - int m; - - proc_range(ps, id, &n, &m); - return (&ps->ps_ievs[id][n].ibuf); -} - -struct imsgev * -proc_iev(struct privsep *ps, enum privsep_procid id, int n) -{ - int m; - - proc_range(ps, id, &n, &m); - return (&ps->ps_ievs[id][n]); -} - -/* This function should only be called with care as it breaks async I/O */ -int -proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n) -{ - struct imsgbuf *ibuf; - int m, ret = 0; - - proc_range(ps, id, &n, &m); - for (; n < m; n++) { - ibuf = proc_ibuf(ps, id, n); - if (ibuf == NULL) - return (-1); - do { - ret = imsg_flush(ibuf); - } while (ret == -1 && errno == EAGAIN); - if (ret == -1) - break; - imsg_event_add(&ps->ps_ievs[id][n]); - } - - return (ret); -} blob - 524112fcf4b0c72a25c6f5a4cb85c16bd33ef3a9 (mode 644) blob + /dev/null --- gotwebd/proc.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2010-2015 Reyk Floeter - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -enum { - IMSG_NONE, - IMSG_CTL_OK, - IMSG_CTL_FAIL, - IMSG_CTL_VERBOSE, - IMSG_CTL_NOTIFY, - IMSG_CTL_RESET, - IMSG_CTL_PROCFD, - IMSG_PROC_MAX -}; - -/* imsg */ -struct imsgev { - struct imsgbuf ibuf; - void (*handler)(int, short, void *); - struct event ev; - struct privsep_proc *proc; - void *data; - short events; -}; - -#define IMSG_SIZE_CHECK(imsg, p) do { \ - if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \ - fatalx("bad length imsg received (%s)", #p); \ -} while (0) -#define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE) - -struct ctl_conn { - TAILQ_ENTRY(ctl_conn) entry; - uint8_t flags; - unsigned int waiting; -#define CTL_CONN_NOTIFY 0x01 - struct imsgev iev; - uid_t uid; -}; -TAILQ_HEAD(ctl_connlist, ctl_conn); -extern struct ctl_connlist ctl_conns; - -/* privsep */ -enum privsep_procid { - PROC_GOTWEBD = 0, - PROC_SOCKS, - PROC_MAX, -}; -extern enum privsep_procid privsep_process; - -#define CONFIG_RELOAD 0x00 -#define CONFIG_SOCKS 0x01 -#define CONFIG_ALL 0xff - -struct privsep_pipes { - int *pp_pipes[PROC_MAX]; -}; - -struct privsep { - struct privsep_pipes *ps_pipes[PROC_MAX]; - struct privsep_pipes *ps_pp; - - struct imsgev *ps_ievs[PROC_MAX]; - const char *ps_title[PROC_MAX]; - uint8_t ps_what[PROC_MAX]; - - struct passwd *ps_pw; - int ps_noaction; - - unsigned int ps_instances[PROC_MAX]; - unsigned int ps_instance; - - /* Event and signal handlers */ - struct event ps_evsigint; - struct event ps_evsigterm; - struct event ps_evsigchld; - struct event ps_evsighup; - struct event ps_evsigpipe; - struct event ps_evsigusr1; - - void *ps_env; -}; - -struct privsep_proc { - const char *p_title; - enum privsep_procid p_id; - int (*p_cb)(int, struct privsep_proc *, - struct imsg *); - void (*p_init)(struct privsep *, - struct privsep_proc *); - void (*p_shutdown)(void); - const char *p_chroot; - struct passwd *p_pw; - struct privsep *p_ps; -}; - -struct privsep_fd { - enum privsep_procid pf_procid; - unsigned int pf_instance; -}; - -#if DEBUG -#define DPRINTF log_debug -#else -#define DPRINTF(x...) do {} while(0) -#endif - -#define PROC_GOTWEBD_SOCK_FILENO 3 -#define PROC_MAX_INSTANCES 32 - -/* proc.c */ -void proc_init(struct privsep *, struct privsep_proc *, unsigned int, - int, char **, enum privsep_procid); -void proc_kill(struct privsep *); -void proc_connect(struct privsep *ps); -void proc_dispatch(int, short event, void *); -void proc_range(struct privsep *, enum privsep_procid, int *, int *); -void proc_run(struct privsep *, struct privsep_proc *, - struct privsep_proc *, unsigned int, - void (*)(struct privsep *, struct privsep_proc *, void *), void *); -void imsg_event_add(struct imsgev *); -int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, - pid_t, int, void *, uint16_t); -int imsg_composev_event(struct imsgev *, uint16_t, uint32_t, - pid_t, int, const struct iovec *, int); -int proc_compose_imsg(struct privsep *, enum privsep_procid, int, - uint16_t, uint32_t, int, void *, uint16_t); -int proc_compose(struct privsep *, enum privsep_procid, - uint16_t, void *data, uint16_t); -int proc_composev_imsg(struct privsep *, enum privsep_procid, int, - uint16_t, uint32_t, int, const struct iovec *, int); -int proc_composev(struct privsep *, enum privsep_procid, - uint16_t, const struct iovec *, int); -int proc_forward_imsg(struct privsep *, struct imsg *, - enum privsep_procid, int); -struct imsgbuf * - proc_ibuf(struct privsep *, enum privsep_procid, int); -struct imsgev * - proc_iev(struct privsep *, enum privsep_procid, int); -enum privsep_procid - proc_getid(struct privsep_proc *, unsigned int, const char *); -int proc_flush_imsg(struct privsep *, enum privsep_procid, int); - -/* log.c */ -void log_init(int, int); -void log_procinit(const char *); -void log_setverbose(int); -int log_getverbose(void); -void log_warn(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_warnx(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_info(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void log_debug(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -void logit(int, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void vlog(int, const char *, va_list) - __attribute__((__format__ (printf, 2, 0))); -__dead void fatal(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -__dead void fatalx(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); blob - 16a74076b94ffabf45821936ff713dacb0e1dff0 blob + dd100773cb3593bb4f04e31d024acf711bdc912e --- gotwebd/sockets.c +++ gotwebd/sockets.c @@ -55,28 +55,25 @@ #include "got_repository.h" #include "got_privsep.h" -#include "proc.h" #include "gotwebd.h" #include "tmpl.h" #define SOCKS_BACKLOG 5 #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) - volatile int client_cnt; static struct timeval timeout = { TIMEOUT_DEFAULT, 0 }; static void sockets_sighdlr(int, short, void *); -static void sockets_run(struct privsep *, struct privsep_proc *, void *); +static void sockets_shutdown(void); static void sockets_launch(void); static void sockets_purge(struct gotwebd *); static void sockets_accept_paused(int, short, void *); static void sockets_rlimit(int); -static int sockets_dispatch_gotwebd(int, struct privsep_proc *, - struct imsg *); -static int sockets_unix_socket_listen(struct privsep *, struct socket *); +static void sockets_dispatch_main(int, short, void *); +static int sockets_unix_socket_listen(struct gotwebd *, struct socket *); static int sockets_create_socket(struct address *); static int sockets_accept_reserve(int, struct sockaddr *, socklen_t *, int, volatile int *); @@ -88,35 +85,44 @@ static struct socket *sockets_conf_new_socket_fcgi(str int cgi_inflight = 0; -static struct privsep_proc procs[] = { - { "gotwebd", PROC_GOTWEBD, sockets_dispatch_gotwebd }, -}; - void -sockets(struct privsep *ps, struct privsep_proc *p) +sockets(struct gotwebd *env, int fd) { - proc_run(ps, p, procs, nitems(procs), sockets_run, NULL); -} + struct event sighup, sigpipe, sigusr1, sigchld; -static void -sockets_run(struct privsep *ps, struct privsep_proc *p, void *arg) -{ - if (config_init(ps->ps_env) == -1) - fatal("failed to initialize configuration"); + event_init(); - p->p_shutdown = sockets_shutdown; - sockets_rlimit(-1); - signal_del(&ps->ps_evsigchld); - signal_set(&ps->ps_evsigchld, SIGCHLD, sockets_sighdlr, ps); - signal_add(&ps->ps_evsigchld, NULL); + if (config_init(env) == -1) + fatal("failed to initialize configuration"); + if ((env->iev_parent = malloc(sizeof(*env->iev_parent))) == NULL) + fatal("malloc"); + imsg_init(&env->iev_parent->ibuf, fd); + env->iev_parent->handler = sockets_dispatch_main; + env->iev_parent->data = env->iev_parent; + event_set(&env->iev_parent->ev, fd, EV_READ, sockets_dispatch_main, + env->iev_parent); + event_add(&env->iev_parent->ev, NULL); + + signal_set(&sighup, SIGCHLD, sockets_sighdlr, env); + signal_add(&sighup, NULL); + signal_set(&sigpipe, SIGCHLD, sockets_sighdlr, env); + signal_add(&sigpipe, NULL); + signal_set(&sigusr1, SIGCHLD, sockets_sighdlr, env); + signal_add(&sigusr1, NULL); + signal_set(&sigchld, SIGCHLD, sockets_sighdlr, env); + signal_add(&sigchld, NULL); + #ifndef PROFILE if (pledge("stdio rpath inet recvfd proc exec sendfd unveil", NULL) == -1) fatal("pledge"); #endif + + event_dispatch(); + sockets_shutdown(); } void @@ -301,48 +307,68 @@ sockets_purge(struct gotwebd *env) } } -static int -sockets_dispatch_gotwebd(int fd, struct privsep_proc *p, struct imsg *imsg) +static void +sockets_dispatch_main(int fd, short event, void *arg) { - struct privsep *ps = p->p_ps; - int res = 0, cmd = 0, verbose; + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + struct gotwebd *env = gotwebd_env; + ssize_t n; + int shut = 0; - switch (imsg->hdr.type) { - case IMSG_CFG_SRV: - config_getserver(gotwebd_env, imsg); - break; - case IMSG_CFG_SOCK: - config_getsock(gotwebd_env, imsg); - break; - case IMSG_CFG_FD: - config_getfd(gotwebd_env, imsg); - break; - case IMSG_CFG_DONE: - config_getcfg(gotwebd_env, imsg); - break; - case IMSG_CTL_START: - sockets_launch(); - break; - case IMSG_CTL_VERBOSE: - IMSG_SIZE_CHECK(imsg, &verbose); - memcpy(&verbose, imsg->data, sizeof(verbose)); - log_setverbose(verbose); - break; - default: - return -1; + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed */ + shut = 1; } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed */ + shut = 1; + } - switch (cmd) { - case 0: - break; - default: - if (proc_compose_imsg(ps, PROC_GOTWEBD, -1, cmd, - imsg->hdr.peerid, -1, &res, sizeof(res)) == -1) - return -1; - break; + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_CFG_SRV: + config_getserver(env, &imsg); + break; + case IMSG_CFG_SOCK: + config_getsock(env, &imsg); + break; + case IMSG_CFG_FD: + config_getfd(env, &imsg); + break; + case IMSG_CFG_DONE: + config_getcfg(env, &imsg); + break; + case IMSG_CTL_START: + sockets_launch(); + break; + default: + fatalx("%s: unknown imsg type %d", __func__, + imsg.hdr.type); + } + + imsg_free(&imsg); } - return 0; + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } } static void @@ -366,7 +392,7 @@ sockets_sighdlr(int sig, short event, void *arg) } } -void +static void sockets_shutdown(void) { struct server *srv, *tsrv; @@ -395,12 +421,10 @@ sockets_shutdown(void) int sockets_privinit(struct gotwebd *env, struct socket *sock) { - struct privsep *ps = env->gotwebd_ps; - if (sock->conf.af_type == AF_UNIX) { log_debug("%s: initializing unix socket %s", __func__, sock->conf.unix_socket_name); - sock->fd = sockets_unix_socket_listen(ps, sock); + sock->fd = sockets_unix_socket_listen(env, sock); if (sock->fd == -1) { log_warnx("%s: create unix socket failed", __func__); return -1; @@ -422,9 +446,8 @@ sockets_privinit(struct gotwebd *env, struct socket *s } static int -sockets_unix_socket_listen(struct privsep *ps, struct socket *sock) +sockets_unix_socket_listen(struct gotwebd *env, struct socket *sock) { - struct gotwebd *env = ps->ps_env; struct sockaddr_un sun; struct socket *tsock; int u_fd = -1; @@ -480,8 +503,8 @@ sockets_unix_socket_listen(struct privsep *ps, struct return -1; } - if (chown(sock->conf.unix_socket_name, ps->ps_pw->pw_uid, - ps->ps_pw->pw_gid) == -1) { + if (chown(sock->conf.unix_socket_name, env->pw->pw_uid, + env->pw->pw_gid) == -1) { log_warn("%s: chown", __func__); close(u_fd); (void)unlink(sock->conf.unix_socket_name);