From: Stefan Sperling Subject: gotwebd fcgi/gotweb process split To: gameoftrees@openbsd.org Date: Wed, 9 Apr 2025 18:31:42 +0200 I would like gotwebd to be able to browse repositories outside the web server's chroot directory in order to save disk space on servers and avoid the synchronization delay between the gotd repository and the gotwebd copy in /var/www. And Omar pointed out that the gotwebd-specific libexec helper binaries are causing problems in -portable. This patch addresses both problems by splitting the FCGI server part and the part which reads Git repositories into separate processes. The FCGI part still runs in chroot as before, but the gotweb part runs outside chroot. gotwebd will need a run-time dependency on the main got package since libexec helpers are now shared between got/tog and gotwebd. Since gotwebd doesn't really need to read anything from var/www, default to a new _gotwebd user instead of www. www can still be used if desired. This patch is still work-in-progress. It lacks documentation updates, and one TODO is mentioned in the code. However, since this is working for me with a simple configuration file I want to share it for testing and review feedback. I am testing with the following gotwebd.conf file: [[[ chroot "/var/www" prefork 1 user www server "localhost" { repos_path "/git" } ]]] The diff: M gotwebd/Makefile | 0+ 2- M gotwebd/Makefile.inc | 1+ 1- M gotwebd/config.c | 9+ 15- M gotwebd/fcgi.c | 102+ 5- M gotwebd/gotweb.c | 384+ 14- M gotwebd/gotwebd.c | 236+ 82- M gotwebd/gotwebd.h | 29+ 10- D gotwebd/libexec/Makefile | 0+ 4- D gotwebd/libexec/Makefile.inc | 0+ 11- D gotwebd/libexec/got-read-blob/Makefile | 0+ 15- D gotwebd/libexec/got-read-commit/Makefile | 0+ 15- D gotwebd/libexec/got-read-gitconfig/Makefile | 0+ 16- D gotwebd/libexec/got-read-gotconfig/Makefile | 0+ 19- D gotwebd/libexec/got-read-object/Makefile | 0+ 15- D gotwebd/libexec/got-read-pack/Makefile | 0+ 16- D gotwebd/libexec/got-read-tag/Makefile | 0+ 15- D gotwebd/libexec/got-read-tree/Makefile | 0+ 15- M gotwebd/sockets.c | 165+ 38- 18 files changed, 926 insertions(+), 308 deletions(-) commit - cbe39f11829176889cb350bc903e0f339cff366e commit + 4230a2ade42b54c9e07602ba86c6532f698b15d8 blob - 6b6e9e1815bb7b6d738165292b522056eee96e22 blob + 545a13d2e5aba6dc9df486f7300af0acf23c4df7 --- gotwebd/Makefile +++ gotwebd/Makefile @@ -1,8 +1,6 @@ .PATH:${.CURDIR}/../lib .PATH:${.CURDIR}/../template -SUBDIR = libexec - .include "../got-version.mk" .include "Makefile.inc" blob - 8140d7d3e8b25972b9b347173294bd3b671a0da1 blob + 22466be67cc8ab44489af620bed8558a24ed17c9 --- gotwebd/Makefile.inc +++ gotwebd/Makefile.inc @@ -1,9 +1,9 @@ LDADD += -lz -lutil PREFIX ?= /usr/local BINDIR ?= ${PREFIX}/sbin +LIBEXECDIR ?= ${PREFIX}/libexec CHROOT_DIR ?= /var/www GOTWEB_DIR = /bin/gotwebd -LIBEXECDIR = ${GOTWEB_DIR}/libexec HTTPD_DIR = ${CHROOT_DIR}/htdocs PROG_DIR = ${HTTPD_DIR}/${PROG} WWWUSR ?= www blob - 980ecf25520da4497a36f585459ebecbf7b1d3f5 blob + 90eb143afce8e50c7ba53d141294ca8616d7a074 --- gotwebd/config.c +++ gotwebd/config.c @@ -74,15 +74,6 @@ config_getcfg(struct gotwebd *env, struct imsg *imsg) } int -config_setserver(struct gotwebd *env, struct server *srv) -{ - if (main_compose_sockets(env, GOTWEBD_IMSG_CFG_SRV, - -1, srv, sizeof(*srv)) == -1) - fatal("main_compose_sockets GOTWEBD_IMSG_CFG_SRV"); - return 0; -} - -int config_getserver(struct gotwebd *env, struct imsg *imsg) { struct server *srv; @@ -106,17 +97,20 @@ config_getserver(struct gotwebd *env, struct imsg *ims } int -config_setsock(struct gotwebd *env, struct socket *sock) +config_setsock(struct gotwebd *env, struct socket *sock, uid_t uid, gid_t gid) { /* open listening sockets */ - if (sockets_privinit(env, sock) == -1) + if (sockets_privinit(env, sock, uid, gid) == -1) return -1; if (main_compose_sockets(env, GOTWEBD_IMSG_CFG_SOCK, sock->fd, &sock->conf, sizeof(sock->conf)) == -1) fatal("main_compose_sockets GOTWEBD_IMSG_CFG_SOCK"); - sock->fd = -1; + if (main_compose_gotweb(env, GOTWEBD_IMSG_CFG_SOCK, sock->fd, + &sock->conf, sizeof(sock->conf)) == -1) + fatal("main_compose_gotweb GOTWEBD_IMSG_CFG_SOCK"); + return 0; } @@ -172,13 +166,13 @@ config_setfd(struct gotwebd *env) fd = got_opentempfd(); if (fd == -1) fatal("got_opentemp"); - if (imsg_compose_event(&env->iev_server[j], + if (imsg_compose_event(&env->iev_gotweb[j], GOTWEBD_IMSG_CFG_FD, 0, -1, fd, NULL, 0) == -1) fatal("imsg_compose_event GOTWEBD_IMSG_CFG_FD"); - if (imsgbuf_flush(&env->iev_server[j].ibuf) == -1) + if (imsgbuf_flush(&env->iev_gotweb[j].ibuf) == -1) fatal("imsgbuf_flush"); - imsg_event_add(&env->iev_server[j]); + imsg_event_add(&env->iev_gotweb[j]); } } blob - fe58b1f12ab25d401bc5d3c10aaecf29d138f7bd blob + 02d64b76e9201d76470c48fc07e0b6cd3a8ba35a --- gotwebd/fcgi.c +++ gotwebd/fcgi.c @@ -54,6 +54,7 @@ void dump_fcgi_end_request_body(const char *, extern int cgi_inflight; extern volatile int client_cnt; +extern struct requestlist requests; void fcgi_request(int fd, short events, void *arg) @@ -142,6 +143,7 @@ fcgi_parse_record(uint8_t *buf, size_t n, struct reque ntohs(h->content_len), c, ntohs(h->id)); break; case FCGI_STDIN: + return 0; case FCGI_ABORT_REQUEST: fcgi_create_end_record(c); fcgi_cleanup_request(c); @@ -175,6 +177,87 @@ fcgi_parse_begin_request(uint8_t *buf, uint16_t n, c->id = id; } +static void +fcgi_forward_response(int fd, short event, void *arg) +{ + struct request *c = arg; + uint8_t outbuf[GOTWEBD_CACHESIZE]; + ssize_t r, w; + + if ((event & EV_READ) == 0) + return; + + r = read(fd, outbuf, sizeof(outbuf)); + if (r == 0) + return; + + if (r == -1) { + log_warn("read response"); + } else { + log_info("%s: %zd bytes", __func__, r); + w = write(c->fd, outbuf, r); + if (w != r) + log_warn("%s: wrote %zd of %zd bytes", + __func__, w, r); + } + + event_add(c->resp_event, NULL); +} + +static void +process_request(struct request *c) +{ + struct gotwebd *env = gotwebd_env; + int ret, i, pipe[2]; + struct request ic; + struct event *resp_event = NULL; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1) { + log_warn("socketpair"); + return; + } + + memcpy(&ic, c, sizeof(ic)); + + /* Don't leak pointers from our address space to another process. */ + ic.sock = NULL; + ic.srv = NULL; + ic.t = NULL; + ic.tp = NULL; + ic.buf = NULL; + ic.outbuf = NULL; + + /* Other process will use its own set of temp files. */ + for (i = 0; i < nitems(c->priv_fd); i++) + ic.priv_fd[i] = -1; + ic.fd = -1; + ic.resp_fd = -1; + + resp_event = calloc(1, sizeof(*resp_event)); + if (resp_event == NULL) { + log_warn("calloc"); + close(pipe[0]); + close(pipe[1]); + return; + } + + ret = imsg_compose_event(env->iev_gotweb, GOTWEBD_IMSG_REQ_PROCESS, + GOTWEBD_PROC_SERVER, getpid(), pipe[0], &ic, sizeof(ic)); + if (ret == -1) { + log_warn("imsg_compose_event"); + close(pipe[0]); + close(pipe[1]); + free(resp_event); + return; + } + + event_set(resp_event, pipe[1], EV_READ, fcgi_forward_response, c); + event_add(resp_event, NULL); + + c->resp_fd = pipe[1]; + c->resp_event = resp_event; +} + void fcgi_parse_params(uint8_t *buf, uint16_t n, struct request *c, uint16_t id) { @@ -192,8 +275,7 @@ fcgi_parse_params(uint8_t *buf, uint16_t n, struct req } if (n == 0) { - gotweb_process_request(c); - template_flush(c->tp); + process_request(c); return; } @@ -398,17 +480,32 @@ fcgi_create_end_record(struct request *c) void fcgi_cleanup_request(struct request *c) { + log_info("%s: %d", __func__, c->request_id); + cgi_inflight--; client_cnt--; - evtimer_del(&c->tmo); + TAILQ_REMOVE(&requests, c, entry); + + if (evtimer_initialized(&c->tmo)) + evtimer_del(&c->tmo); if (event_initialized(&c->ev)) event_del(&c->ev); - close(c->fd); - template_free(c->tp); + if (c->fd != -1) + close(c->fd); + if (c->resp_fd != -1) + close(c->resp_fd); + if (c->tp != NULL) + template_free(c->tp); if (c->t != NULL) gotweb_free_transport(c->t); + if (c->resp_event) { + event_del(c->resp_event); + free(c->resp_event); + } + free(c->buf); + free(c->outbuf); free(c); } blob - 1a1a8770b26108d09399eb43a781fa0e4d63c1b9 blob + 91799176b5f93d034ae4014bd3a97128c31aeee4 --- gotwebd/gotweb.c +++ gotwebd/gotweb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -142,11 +143,124 @@ gotweb_reply_file(struct request *c, const char *ctype return gotweb_reply(c, 200, ctype, NULL); } +static void +free_request(struct request *c) +{ + if (c->fd != -1) + close(c->fd); + if (c->tp != NULL) + template_free(c->tp); + if (c->t != NULL) + gotweb_free_transport(c->t); + free(c->buf); + free(c->outbuf); + free(c); +} + +static struct socket * +gotweb_get_socket(int sock_id) +{ + struct socket *sock; + + TAILQ_FOREACH(sock, &gotwebd_env->sockets, entry) { + if (sock->conf.id == sock_id) + return sock; + } + + return NULL; +} + +static struct request * +recv_request(struct imsg *imsg) +{ + const struct got_error *error; + struct request *c; + struct server *srv; + size_t datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + int fd = -1; + uint8_t *outbuf = NULL; + + if (datalen != sizeof(*c)) { + log_warnx("bad request size received over imsg"); + return NULL; + } + + fd = imsg_get_fd(imsg); + if (fd == -1) { + log_warnx("no client file descriptor"); + return NULL; + } + + c = calloc(1, sizeof(*c)); + if (c == NULL) { + log_warn("calloc"); + return NULL; + } + + outbuf = calloc(1, GOTWEBD_CACHESIZE); + if (outbuf == NULL) { + log_warn("calloc"); + free(c); + return NULL; + } + + memcpy(c, imsg->data, sizeof(*c)); + + /* Non-NULL pointers, if any, are not from our address space. */ + c->sock = NULL; + c->srv = NULL; + c->t = NULL; + c->tp = NULL; + c->buf = NULL; + c->outbuf = outbuf; + + memset(&c->ev, 0, sizeof(c->ev)); + memset(&c->tmo, 0, sizeof(c->tmo)); + + /* Use our own temporary file descriptors. */ + memcpy(c->priv_fd, gotwebd_env->priv_fd, sizeof(c->priv_fd)); + + c->fd = fd; + + c->tp = template(c, fcgi_write, c->outbuf, GOTWEBD_CACHESIZE); + if (c->tp == NULL) { + log_warn("gotweb init template"); + free_request(c); + return NULL; + } + + c->sock = gotweb_get_socket(c->sock_id); + if (c->sock == NULL) { + log_warn("socket id '%d' not found", c->sock_id); + free_request(c); + return NULL; + } + + /* init the transport */ + error = gotweb_init_transport(&c->t); + if (error) { + log_warnx("gotweb init transport: %s", error->msg); + free_request(c); + return NULL; + } + + /* get the gotwebd server */ + srv = gotweb_get_server(c->server_name); + if (srv == NULL) { + log_warnx("server '%s' not found", c->server_name); + free_request(c); + return NULL; + } + c->srv = srv; + + return c; +} + void gotweb_process_request(struct request *c) { const struct got_error *error = NULL; - struct server *srv = NULL; + struct server *srv = c->srv;; struct querystring *qs = NULL; struct repo_dir *repo_dir = NULL; struct repo_commit *commit; @@ -155,19 +269,6 @@ gotweb_process_request(struct request *c) size_t len; int r, binary = 0; - /* init the transport */ - error = gotweb_init_transport(&c->t); - if (error) { - log_warnx("%s: %s", __func__, error->msg); - return; - } - /* get the gotwebd server */ - srv = gotweb_get_server(c->server_name); - if (srv == NULL) { - log_warnx("%s: error server is NULL", __func__); - goto err; - } - c->srv = srv; /* parse our querystring */ error = gotweb_init_querystring(&qs); if (error) { @@ -1297,3 +1398,272 @@ gotweb_render_age(struct template *tp, time_t committe } return 0; } + +static void +gotweb_shutdown(void) +{ + imsgbuf_clear(&gotwebd_env->iev_parent->ibuf); + free(gotwebd_env->iev_parent); + free(gotwebd_env); + + exit(0); +} + +static void +gotweb_sighdlr(int sig, short event, void *arg) +{ + switch (sig) { + case SIGHUP: + log_info("%s: ignoring SIGHUP", __func__); + break; + case SIGPIPE: + log_info("%s: ignoring SIGPIPE", __func__); + break; + case SIGUSR1: + log_info("%s: ignoring SIGUSR1", __func__); + break; + case SIGCHLD: + break; + case SIGINT: + case SIGTERM: + gotweb_shutdown(); + break; + default: + log_warn("unhandled signal %d", sig); + } +} + +static void +gotweb_launch(struct gotwebd *env) +{ + struct server *srv; + const struct got_error *error; + + if (env->iev_server == NULL) + fatal("server process not connected"); + +#ifndef PROFILE + if (pledge("stdio rpath recvfd sendfd proc exec unveil", NULL) == -1) + fatal("pledge"); +#endif + + TAILQ_FOREACH(srv, &gotwebd_env->servers, entry) { + if (unveil(srv->repos_path, "r") == -1) + fatal("unveil %s", srv->repos_path); + } + + error = got_privsep_unveil_exec_helpers(); + if (error) + fatalx("%s", error->msg); + + if (unveil(NULL, NULL) == -1) + fatal("unveil"); + + event_add(&env->iev_server->ev, NULL); +} + +static void +send_request_done(struct imsgev *iev, int request_id) +{ + struct gotwebd *env = gotwebd_env; + + if (imsg_compose_event(env->iev_server, GOTWEBD_IMSG_REQ_DONE, + GOTWEBD_PROC_GOTWEB, getpid(), -1, + &request_id, sizeof(request_id)) == -1) + log_warn("imsg_compose_event"); +} + +static void +gotweb_dispatch_server(int fd, short event, void *arg) +{ + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + struct request *c; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_IMSG_REQ_PROCESS: + c = recv_request(&imsg); + if (c) { + int request_id = c->request_id; + gotweb_process_request(c); + template_flush(c->tp); + free_request(c); + send_request_done(iev, request_id); + } + 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); + } +} + +static void +recv_server_pipe(struct gotwebd *env, struct imsg *imsg) +{ + struct imsgev *iev; + int fd; + + if (env->iev_server != NULL) { + log_warn("server pipe already received"); + return; + } + + fd = imsg_get_fd(imsg); + if (fd == -1) + fatalx("invalid server pipe fd"); + + iev = calloc(1, sizeof(*iev)); + if (iev == NULL) + fatal("calloc"); + + if (imsgbuf_init(&iev->ibuf, fd) == -1) + fatal("imsgbuf_init"); + imsgbuf_allow_fdpass(&iev->ibuf); + + iev->handler = gotweb_dispatch_server; + iev->data = iev; + event_set(&iev->ev, fd, EV_READ, gotweb_dispatch_server, iev); + + env->iev_server = iev; +} + +static void +gotweb_dispatch_main(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 = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_IMSG_CFG_SRV: + config_getserver(env, &imsg); + break; + case GOTWEBD_IMSG_CFG_FD: + config_getfd(env, &imsg); + break; + case GOTWEBD_IMSG_CFG_SOCK: + config_getsock(env, &imsg); + break; + case GOTWEBD_IMSG_CFG_DONE: + config_getcfg(env, &imsg); + break; + case GOTWEBD_IMSG_CTL_PIPE: + recv_server_pipe(env, &imsg); + break; + case GOTWEBD_IMSG_CTL_START: + gotweb_launch(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 +gotweb(struct gotwebd *env, int fd) +{ + struct event sighup, sigint, sigusr1, sigchld, sigterm; + struct event_base *evb; + + evb = event_init(); + + sockets_rlimit(-1); + + if ((env->iev_parent = malloc(sizeof(*env->iev_parent))) == NULL) + fatal("malloc"); + if (imsgbuf_init(&env->iev_parent->ibuf, fd) == -1) + fatal("imsgbuf_init"); + imsgbuf_allow_fdpass(&env->iev_parent->ibuf); + env->iev_parent->handler = gotweb_dispatch_main; + env->iev_parent->data = env->iev_parent; + event_set(&env->iev_parent->ev, fd, EV_READ, gotweb_dispatch_main, + env->iev_parent); + event_add(&env->iev_parent->ev, NULL); + + signal(SIGPIPE, SIG_IGN); + + signal_set(&sighup, SIGHUP, gotweb_sighdlr, env); + signal_add(&sighup, NULL); + signal_set(&sigint, SIGINT, gotweb_sighdlr, env); + signal_add(&sigint, NULL); + signal_set(&sigusr1, SIGUSR1, gotweb_sighdlr, env); + signal_add(&sigusr1, NULL); + signal_set(&sigchld, SIGCHLD, gotweb_sighdlr, env); + signal_add(&sigchld, NULL); + signal_set(&sigterm, SIGTERM, gotweb_sighdlr, env); + signal_add(&sigterm, NULL); + +#ifndef PROFILE + if (pledge("stdio rpath recvfd sendfd proc exec unveil", NULL) == -1) + fatal("pledge"); +#endif + event_dispatch(); + event_base_free(evb); + gotweb_shutdown(); +} blob - 61a08a5b8aa69b071a02be23f9794bab66c345fc blob + 00aabaed5092fea490a2c5a9b0dd40dc01ffab52 --- gotwebd/gotwebd.c +++ gotwebd/gotwebd.c @@ -48,11 +48,12 @@ __dead void usage(void); int main(int, char **); -int gotwebd_configure(struct gotwebd *); +int gotwebd_configure(struct gotwebd *, uid_t, gid_t); void gotwebd_configure_done(struct gotwebd *); void gotwebd_sighdlr(int sig, short event, void *arg); void gotwebd_shutdown(void); -void gotwebd_dispatch_sockets(int, short, void *); +void gotwebd_dispatch_server(int, short, void *); +void gotwebd_dispatch_gotweb(int, short, void *); struct gotwebd *gotwebd_env; @@ -75,7 +76,7 @@ imsg_event_add(struct imsgev *iev) int imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, - pid_t pid, int fd, const void *data, uint16_t datalen) + pid_t pid, int fd, const void *data, size_t datalen) { int ret; @@ -86,42 +87,67 @@ imsg_compose_event(struct imsgev *iev, uint16_t type, return (ret); } -int -main_compose_sockets(struct gotwebd *env, uint32_t type, int fd, - const void *data, uint16_t len) +static int +send_imsg(struct imsgev *iev, uint32_t type, int fd, const void *data, + uint16_t len) { - size_t i; - int ret, d; + int ret, d = -1; - for (i = 0; i < env->nserver; ++i) { + if (fd != -1 && (d = dup(fd)) == -1) + goto err; + + ret = imsg_compose_event(iev, type, 0, -1, d, data, len); + if (ret == -1) + goto err; + + if (d != -1) { d = -1; - if (fd != -1 && (d = dup(fd)) == -1) + /* Flush imsg to prevent fd exhaustion. 'd' will be closed. */ + if (imsgbuf_flush(&iev->ibuf) == -1) goto err; - - ret = imsg_compose_event(&env->iev_server[i], type, 0, -1, - d, data, len); - if (ret == -1) - goto err; - - /* prevent fd exhaustion */ - if (d != -1) { - if (imsgbuf_flush(&env->iev_server[i].ibuf) == -1) - goto err; - imsg_event_add(&env->iev_server[i]); - } + imsg_event_add(iev); } - if (fd != -1) - close(fd); return 0; - err: - if (fd != -1) - close(fd); + if (d != -1) + close(d); return -1; } int +main_compose_sockets(struct gotwebd *env, uint32_t type, int fd, + const void *data, uint16_t len) +{ + size_t i; + int ret = 0; + + for (i = 0; i < env->nserver; ++i) { + ret = send_imsg(&env->iev_server[i], type, fd, data, len); + if (ret) + break; + } + + return ret; +} + +int +main_compose_gotweb(struct gotwebd *env, uint32_t type, int fd, + const void *data, uint16_t len) +{ + size_t i; + int ret = 0; + + for (i = 0; i < env->nserver; ++i) { + ret = send_imsg(&env->iev_gotweb[i], type, fd, data, len); + if (ret) + break; + } + + return ret; +} + +int sockets_compose_main(struct gotwebd *env, uint32_t type, const void *d, uint16_t len) { @@ -129,7 +155,7 @@ sockets_compose_main(struct gotwebd *env, uint32_t typ } void -gotwebd_dispatch_sockets(int fd, short event, void *arg) +gotwebd_dispatch_server(int fd, short event, void *arg) { struct imsgev *iev = arg; struct imsgbuf *ibuf; @@ -179,6 +205,56 @@ gotwebd_dispatch_sockets(int fd, short event, void *ar } void +gotwebd_dispatch_gotweb(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 = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_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; */ @@ -203,10 +279,11 @@ gotwebd_sighdlr(int sig, short event, void *arg) } } -static int -spawn_socket_process(struct gotwebd *env, const char *argv0, int n) +static void +spawn_process(struct gotwebd *env, const char *argv0, struct imsgev *iev, + enum gotwebd_proc_type proc_type, void (*handler)(int, short, void *)) { - const char *argv[8]; + const char *argv[9]; int argc = 0; int p[2]; pid_t pid; @@ -221,21 +298,26 @@ spawn_socket_process(struct gotwebd *env, const char * break; default: /* parent */ close(p[0]); - if (imsgbuf_init(&env->iev_server[n].ibuf, p[1]) == -1) + if (imsgbuf_init(&iev->ibuf, p[1]) == -1) fatal("imsgbuf_init"); - imsgbuf_allow_fdpass(&env->iev_server[n].ibuf); - 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; + imsgbuf_allow_fdpass(&iev->ibuf); + iev->handler = handler; + iev->data = iev; + event_set(&iev->ev, p[1], EV_READ, handler, iev); + event_add(&iev->ev, NULL); + return; } close(p[1]); argv[argc++] = argv0; - argv[argc++] = "-S"; + if (proc_type == GOTWEBD_PROC_SERVER) { + argv[argc++] = "-S"; + argv[argc++] = env->user; + } else if (proc_type == GOTWEBD_PROC_GOTWEB) { + argv[argc++] = "-G"; + argv[argc++] = env->user; + } if (strcmp(env->gotwebd_conffile, GOTWEBD_CONF) != 0) { argv[argc++] = "-f"; argv[argc++] = env->gotwebd_conffile; @@ -273,12 +355,12 @@ main(int argc, char **argv) struct event sigint, sigterm, sighup, sigpipe, sigusr1; struct event_base *evb; struct gotwebd *env; - struct passwd *pw; + struct passwd *pw_gotwebd; int ch, i; int no_action = 0; - int server_proc = 0; + int proc_type = GOTWEBD_PROC_PARENT; const char *conffile = GOTWEBD_CONF; - const char *username = GOTWEBD_DEFAULT_USER; + const char *gotwebd_username = GOTWEBD_DEFAULT_USER; const char *argv0; if ((argv0 = argv[0]) == NULL) @@ -292,7 +374,7 @@ main(int argc, char **argv) fatal("%s: calloc", __func__); config_init(env); - while ((ch = getopt(argc, argv, "D:df:nSv")) != -1) { + while ((ch = getopt(argc, argv, "D:dG:f:nS:vW:")) != -1) { switch (ch) { case 'D': if (cmdline_symset(optarg) < 0) @@ -302,6 +384,10 @@ main(int argc, char **argv) case 'd': env->gotwebd_debug = 1; break; + case 'G': + proc_type = GOTWEBD_PROC_GOTWEB; + gotwebd_username = optarg; + break; case 'f': conffile = optarg; break; @@ -309,7 +395,8 @@ main(int argc, char **argv) no_action = 1; break; case 'S': - server_proc = 1; + proc_type = GOTWEBD_PROC_SERVER; + gotwebd_username = optarg; break; case 'v': if (env->gotwebd_verbose < 3) @@ -327,43 +414,64 @@ main(int argc, char **argv) gotwebd_env = env; env->gotwebd_conffile = conffile; - if (parse_config(env->gotwebd_conffile, env) == -1) - exit(1); + if (proc_type == GOTWEBD_PROC_PARENT) { + if (parse_config(env->gotwebd_conffile, env) == -1) + exit(1); - if (no_action) { - fprintf(stderr, "configuration OK\n"); - exit(0); + if (no_action) { + fprintf(stderr, "configuration OK\n"); + exit(0); + } + + if (env->user) + gotwebd_username = env->user; } + pw_gotwebd = getpwnam(gotwebd_username); + if (pw_gotwebd == NULL) + fatalx("unknown user %s", gotwebd_username); + /* check for root privileges */ if (geteuid()) fatalx("need root privileges"); - if (env->user) - username = env->user; - pw = getpwnam(username); - if (pw == NULL) - fatalx("unknown user %s", username); - env->pw = pw; - log_init(env->gotwebd_debug, LOG_DAEMON); log_setverbose(env->gotwebd_verbose); - if (server_proc) { - setproctitle("server"); - log_procinit("server"); + switch (proc_type) { + case GOTWEBD_PROC_SERVER: + setproctitle("fcgiserver"); + log_procinit("fcgiserver"); if (chroot(env->httpd_chroot) == -1) fatal("chroot %s", env->httpd_chroot); 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) + + if (setgroups(1, &pw_gotwebd->pw_gid) == -1 || + setresgid(pw_gotwebd->pw_gid, pw_gotwebd->pw_gid, + pw_gotwebd->pw_gid) == -1 || + setresuid(pw_gotwebd->pw_uid, pw_gotwebd->pw_uid, + pw_gotwebd->pw_uid) == -1) fatal("failed to drop privileges"); sockets(env, GOTWEBD_SOCK_FILENO); return 1; + case GOTWEBD_PROC_GOTWEB: + setproctitle("gotweb"); + log_procinit("gotweb"); + + if (setgroups(1, &pw_gotwebd->pw_gid) == -1 || + setresgid(pw_gotwebd->pw_gid, pw_gotwebd->pw_gid, + pw_gotwebd->pw_gid) == -1 || + setresuid(pw_gotwebd->pw_uid, pw_gotwebd->pw_uid, + pw_gotwebd->pw_uid) == -1) + fatal("failed to drop privileges"); + + gotweb(env, GOTWEBD_SOCK_FILENO); + return 1; + default: + break; } if (!env->gotwebd_debug && daemon(1, 0) == -1) @@ -375,12 +483,16 @@ main(int argc, char **argv) env->iev_server = calloc(env->nserver, sizeof(*env->iev_server)); if (env->iev_server == NULL) fatal("calloc"); + env->iev_gotweb = calloc(env->nserver, sizeof(*env->iev_gotweb)); + if (env->iev_gotweb == NULL) + fatal("calloc"); for (i = 0; i < env->nserver; ++i) { - if (spawn_socket_process(env, argv0, i) == -1) - fatal("spawn_socket_process"); + spawn_process(env, argv0, &env->iev_server[i], + GOTWEBD_PROC_SERVER, gotwebd_dispatch_server); + spawn_process(env, argv0, &env->iev_gotweb[i], + GOTWEBD_PROC_GOTWEB, gotwebd_dispatch_gotweb); } - if (chdir("/") == -1) fatal("chdir /"); @@ -400,12 +512,15 @@ main(int argc, char **argv) signal_add(&sigpipe, NULL); signal_add(&sigusr1, NULL); - if (gotwebd_configure(env) == -1) + if (gotwebd_configure(env, + pw_gotwebd->pw_uid, pw_gotwebd->pw_gid) == -1) fatalx("configuration failed"); - 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) + if (setgroups(1, &pw_gotwebd->pw_gid) == -1 || + setresgid(pw_gotwebd->pw_gid, pw_gotwebd->pw_gid, + pw_gotwebd->pw_gid) == -1 || + setresuid(pw_gotwebd->pw_uid, pw_gotwebd->pw_uid, + pw_gotwebd->pw_uid) == -1) fatal("failed to drop privileges"); #ifdef PROFILE @@ -432,24 +547,54 @@ main(int argc, char **argv) return (0); } +static void +connect_children(struct gotwebd *env) +{ + struct imsgev *iev1, *iev2; + int pipe[2]; + int i; + + for (i = 0; i < env->nserver; i++) { + iev1 = &env->iev_server[i]; + iev2 = &env->iev_gotweb[i]; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1) + fatal("socketpair"); + + if (send_imsg(iev1, GOTWEBD_IMSG_CTL_PIPE, pipe[0], NULL, 0)) + fatal("send_imsg"); + + if (send_imsg(iev2, GOTWEBD_IMSG_CTL_PIPE, pipe[1], NULL, 0)) + fatal("send_imsg"); + + close(pipe[0]); + close(pipe[1]); + } +} + int -gotwebd_configure(struct gotwebd *env) +gotwebd_configure(struct gotwebd *env, uid_t uid, gid_t gid) { struct server *srv; struct socket *sock; /* gotweb need to reload its config. */ - env->gotwebd_reload = env->prefork_gotwebd; + env->servers_pending = env->prefork_gotwebd; + env->gotweb_pending = env->prefork_gotwebd; /* send our gotweb servers */ TAILQ_FOREACH(srv, &env->servers, entry) { - if (config_setserver(env, srv) == -1) - fatalx("%s: send server error", __func__); + if (main_compose_sockets(env, GOTWEBD_IMSG_CFG_SRV, + -1, srv, sizeof(*srv)) == -1) + fatal("main_compose_sockets GOTWEBD_IMSG_CFG_SRV"); + if (main_compose_gotweb(env, GOTWEBD_IMSG_CFG_SRV, + -1, srv, sizeof(*srv)) == -1) + fatal("main_compose_gotweb GOTWEBD_IMSG_CFG_SRV"); } /* send our sockets */ TAILQ_FOREACH(sock, &env->sockets, entry) { - if (config_setsock(env, sock) == -1) + if (config_setsock(env, sock, uid, gid) == -1) fatalx("%s: send socket error", __func__); } @@ -457,6 +602,9 @@ gotwebd_configure(struct gotwebd *env) if (config_setfd(env) == -1) fatalx("%s: send priv_fd error", __func__); + /* Connect servers and gotwebs. */ + connect_children(env); + if (main_compose_sockets(env, GOTWEBD_IMSG_CFG_DONE, -1, NULL, 0) == -1) fatal("main_compose_sockets GOTWEBD_IMSG_CFG_DONE"); @@ -466,15 +614,21 @@ gotwebd_configure(struct gotwebd *env) void gotwebd_configure_done(struct gotwebd *env) { - if (env->gotwebd_reload == 0) { - log_warnx("%s: configuration already finished", __func__); - return; + if (env->servers_pending > 0) { + env->servers_pending--; + if (env->servers_pending == 0 && + main_compose_sockets(env, GOTWEBD_IMSG_CTL_START, + -1, NULL, 0) == -1) + fatal("main_compose_sockets GOTWEBD_IMSG_CTL_START"); } - env->gotwebd_reload--; - if (env->gotwebd_reload == 0 && - main_compose_sockets(env, GOTWEBD_CTL_START, -1, NULL, 0) == -1) - fatal("main_compose_sockets GOTWEBD_CTL_START"); + if (env->gotweb_pending > 0) { + env->gotweb_pending--; + if (env->gotweb_pending == 0 && + main_compose_gotweb(env, GOTWEBD_IMSG_CTL_START, + -1, NULL, 0) == -1) + fatal("main_compose_sockets GOTWEBD_IMSG_CTL_START"); + } } void blob - 7dd491f1a07f53e526bf9e5df13d9e0767649384 blob + 297a5497cc363872f0e505bdc4e40713efd212f8 --- gotwebd/gotwebd.h +++ gotwebd/gotwebd.h @@ -32,7 +32,7 @@ #define GOTWEBD_CONF "/etc/gotwebd.conf" #ifndef GOTWEBD_DEFAULT_USER -#define GOTWEBD_DEFAULT_USER "www" +#define GOTWEBD_DEFAULT_USER "_gotwebd" #endif #define GOTWEBD_MAXDESCRSZ 1024 @@ -117,12 +117,21 @@ struct got_blob_object; struct got_tree_entry; struct got_reflist_head; +enum gotwebd_proc_type { + GOTWEBD_PROC_PARENT, + GOTWEBD_PROC_SERVER, + GOTWEBD_PROC_GOTWEB, +}; + enum imsg_type { GOTWEBD_IMSG_CFG_SRV, GOTWEBD_IMSG_CFG_SOCK, GOTWEBD_IMSG_CFG_FD, GOTWEBD_IMSG_CFG_DONE, - GOTWEBD_CTL_START, + GOTWEBD_IMSG_CTL_PIPE, + GOTWEBD_IMSG_CTL_START, + GOTWEBD_IMSG_REQ_PROCESS, + GOTWEBD_IMSG_REQ_DONE, }; struct imsgev { @@ -229,6 +238,7 @@ enum socket_priv_fds { struct template; struct request { + TAILQ_ENTRY(request) entry; struct socket *sock; struct server *srv; struct transport *t; @@ -239,12 +249,16 @@ struct request { uint16_t id; int fd; int priv_fd[PRIV_FDS__MAX]; + int resp_fd; + struct event *resp_event; + int sock_id; + uint32_t request_id; - uint8_t buf[FCGI_RECORD_SIZE]; + uint8_t *buf; size_t buf_pos; size_t buf_len; - uint8_t outbuf[GOTWEBD_CACHESIZE]; + uint8_t *outbuf; char querystring[MAX_QUERYSTRING]; char document_uri[MAX_DOCUMENT_URI]; @@ -253,6 +267,7 @@ struct request { uint8_t request_started; }; +TAILQ_HEAD(requestlist, request); struct fcgi_begin_request_body { uint16_t role; @@ -349,12 +364,12 @@ struct gotwebd { struct imsgev *iev_parent; struct imsgev *iev_server; + struct imsgev *iev_gotweb; size_t nserver; - struct passwd *pw; - uint16_t prefork_gotwebd; - int gotwebd_reload; + int servers_pending; + int gotweb_pending; int server_cnt; @@ -430,18 +445,21 @@ typedef int (*got_render_blame_line_cb)(struct templat /* 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); + pid_t, int, const void *, size_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); +int main_compose_gotweb(struct gotwebd *, uint32_t, int, + const void *, uint16_t); /* sockets.c */ 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 *); +int sockets_privinit(struct gotwebd *, struct socket *, uid_t, gid_t); void sockets_purge(struct gotwebd *); +void sockets_rlimit(int); /* gotweb.c */ void gotweb_index_navs(struct request *, struct gotweb_url *, int *, @@ -455,6 +473,7 @@ void gotweb_free_repo_commit(struct repo_commit *); void gotweb_free_repo_tag(struct repo_tag *); void gotweb_process_request(struct request *); void gotweb_free_transport(struct transport *); +void gotweb(struct gotwebd *, int); /* pages.tmpl */ int gotweb_render_page(struct template *, int (*)(struct template *)); @@ -507,7 +526,7 @@ const struct got_error *got_output_file_blame(struct r /* config.c */ int config_setserver(struct gotwebd *, struct server *); int config_getserver(struct gotwebd *, struct imsg *); -int config_setsock(struct gotwebd *, struct socket *); +int config_setsock(struct gotwebd *, struct socket *, uid_t, gid_t); int config_getsock(struct gotwebd *, struct imsg *); int config_setfd(struct gotwebd *); int config_getfd(struct gotwebd *, struct imsg *); blob - 5fd34708bd3654bc05060446ff5d55557747cfd3 (mode 644) blob + /dev/null --- gotwebd/libexec/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIR = got-read-blob got-read-commit got-read-object got-read-tree \ - got-read-tag got-read-pack got-read-gitconfig got-read-gotconfig - -.include blob - 85bee26728643214f5d4570f003572ac1fc36d05 (mode 644) blob + /dev/null --- gotwebd/libexec/Makefile.inc +++ /dev/null @@ -1,11 +0,0 @@ -.include "../Makefile.inc" - -realinstall: - if [ ! -d ${DESTDIR}${CHROOT_DIR}${LIBEXECDIR}/. ]; then \ - ${INSTALL} -d -o root -g daemon -m 755 \ - ${DESTDIR}${CHROOT_DIR}${LIBEXECDIR}; \ - fi - ${INSTALL} ${INSTALL_COPY} -o root -g daemon -m 755 ${PROG} \ - ${DESTDIR}${CHROOT_DIR}${LIBEXECDIR}/${PROG} - -NOMAN = Yes blob - 9daacd33712bbd6a9ded56d7a03ed143ce6642d3 (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-blob/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-blob -SRCS= got-read-blob.c error.c inflate.c object_parse.c \ - path.c privsep.c hash.c pollfd.c object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-blob - -.include blob - f45137c3ce56dbaadeaf0a5f0ffffd3b4204668c (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-commit/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-commit -SRCS= got-read-commit.c error.c inflate.c object_parse.c \ - path.c privsep.c hash.c pollfd.c object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-commit - -.include blob - 44760613f030f1f1f62c831825d7bb85b07c9a46 (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-gitconfig/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-gitconfig -SRCS= got-read-gitconfig.c error.c inflate.c object_parse.c \ - path.c privsep.c hash.c gitconfig.c pollfd.c \ - object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-gitconfig - -.include blob - 26dec69ed1be0648baa16cabea96c54dc3731c54 (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-gotconfig/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-gotconfig -SRCS= got-read-gotconfig.c error.c inflate.c object_parse.c \ - path.c privsep.c hash.c parse.y pollfd.c object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib \ - -I${.CURDIR}/../../../libexec/got-read-gotconfig -YFLAGS = -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -CLEANFILES = parse.h - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-gotconfig - -.include blob - 85841212056443f99c873f1089e3fef315ab7d2a (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-object/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-object -SRCS= got-read-object.c error.c inflate.c object_parse.c \ - path.c privsep.c hash.c pollfd.c object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-object - -.include blob - 6ae7cd0c813fcc65f4256be6d06040df2d2880ba (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-pack/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-pack -SRCS= got-read-pack.c delta.c error.c inflate.c object_cache.c \ - object_idset.c object_parse.c opentemp.c pack.c path.c \ - privsep.c hash.c delta_cache.c pollfd.c object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-pack - -.include blob - eb7b682a069470aa6b445adfc142e1d7bdf7c4a4 (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-tag/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-tag -SRCS= got-read-tag.c error.c inflate.c object_parse.c \ - path.c privsep.c hash.c pollfd.c object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-tag - -.include blob - 497d2ff1cd06d53bcc607653bb658219cd8ea982 (mode 644) blob + /dev/null --- gotwebd/libexec/got-read-tree/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -.include "../../../got-version.mk" -.include "../Makefile.inc" - -PROG= got-read-tree -SRCS= got-read-tree.c error.c inflate.c object_parse.c \ - path.c privsep.c hash.c pollfd.c object_qid.c - -CPPFLAGS = -I${.CURDIR}/../../../include -I${.CURDIR}/../../../lib -LDADD = -lutil -lz -DPADD = ${LIBZ} ${LIBUTIL} -LDSTATIC = ${STATIC} - -.PATH: ${.CURDIR}/../../../lib ${.CURDIR}/../../../libexec/got-read-tree - -.include blob - 4ca6de208a73860f721177a094834ffc846d1aaf blob + 313a8625005f26b7351b119e50c6bad61b49a5b0 --- gotwebd/sockets.c +++ gotwebd/sockets.c @@ -49,11 +49,7 @@ #include #include -#include "got_error.h" -#include "got_opentemp.h" #include "got_reference.h" -#include "got_repository.h" -#include "got_privsep.h" #include "gotwebd.h" #include "log.h" @@ -63,17 +59,17 @@ #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) volatile int client_cnt; +volatile uint32_t request_id; static struct timeval timeout = { TIMEOUT_DEFAULT, 0 }; static void sockets_sighdlr(int, short, void *); static void sockets_shutdown(void); -static void sockets_launch(void); +static void sockets_launch(struct gotwebd *); static void sockets_accept_paused(int, short, void *); -static void sockets_rlimit(int); static void sockets_dispatch_main(int, short, void *); -static int sockets_unix_socket_listen(struct gotwebd *, struct socket *); +static int sockets_unix_socket_listen(struct gotwebd *, struct socket *, uid_t, gid_t); static int sockets_create_socket(struct address *); static int sockets_accept_reserve(int, struct sockaddr *, socklen_t *, int, volatile int *); @@ -83,6 +79,8 @@ static struct socket *sockets_conf_new_socket(struct g int cgi_inflight = 0; +struct requestlist requests = TAILQ_HEAD_INITIALIZER(requests); + void sockets(struct gotwebd *env, int fd) { @@ -118,8 +116,7 @@ sockets(struct gotwebd *env, int fd) signal_add(&sigterm, NULL); #ifndef PROFILE - if (pledge("stdio rpath inet recvfd proc exec sendfd unveil", - NULL) == -1) + if (pledge("stdio inet recvfd sendfd", NULL) == -1) fatal("pledge"); #endif @@ -190,12 +187,13 @@ sockets_conf_new_socket(struct gotwebd *env, int id, s } static void -sockets_launch(void) +sockets_launch(struct gotwebd *env) { struct socket *sock; - struct server *srv; - const struct got_error *error; + if (env->iev_gotweb == NULL) + fatal("gotweb process not connected"); + TAILQ_FOREACH(sock, &gotwebd_env->sockets, entry) { log_info("%s: configuring socket %d (%d)", __func__, sock->conf.id, sock->fd); @@ -212,17 +210,12 @@ sockets_launch(void) sock->conf.id); } - TAILQ_FOREACH(srv, &gotwebd_env->servers, entry) { - if (unveil(srv->repos_path, "r") == -1) - fatal("unveil %s", srv->repos_path); - } +#ifndef PROFILE + if (pledge("stdio inet sendfd", NULL) == -1) + fatal("pledge"); +#endif + event_add(&env->iev_gotweb->ev, NULL); - error = got_privsep_unveil_exec_helpers(); - if (error) - fatal("%s", error->msg); - - if (unveil(NULL, NULL) == -1) - fatal("unveil"); } void @@ -245,7 +238,126 @@ sockets_purge(struct gotwebd *env) } } +static struct request * +find_request(uint32_t request_id) +{ + struct request *c; + + TAILQ_FOREACH(c, &requests, entry) { + if (c->request_id == request_id) + return c; + } + + return NULL; +} + static void +request_done(struct imsg *imsg) +{ + struct request *c; + uint32_t request_id; + size_t datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + if (datalen != sizeof(request_id)) { + log_warn("IMSG_REQ_DONE with bad data length"); + return; + } + + memcpy(&request_id, imsg->data, sizeof(request_id)); + + log_info("%s: request_id: %d", __func__, request_id); + + c = find_request(request_id); + if (c == NULL) { + log_warnx("no request to clean up found for ID '%d'", + request_id); + return; + } + + fcgi_create_end_record(c); + fcgi_cleanup_request(c); +} + +static void +server_dispatch_gotweb(int fd, short event, void *arg) +{ + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_IMSG_REQ_DONE: + request_done(&imsg); + 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); + } +} + +static void +recv_gotweb_pipe(struct gotwebd *env, struct imsg *imsg) +{ + struct imsgev *iev; + int fd; + + if (env->iev_gotweb != NULL) { + log_warn("gotweb pipe already received"); + return; + } + + fd = imsg_get_fd(imsg); + if (fd == -1) + fatalx("invalid gotweb pipe fd"); + + iev = calloc(1, sizeof(*iev)); + if (iev == NULL) + fatal("calloc"); + + if (imsgbuf_init(&iev->ibuf, fd) == -1) + fatal("imsgbuf_init"); + imsgbuf_allow_fdpass(&iev->ibuf); + + iev->handler = server_dispatch_gotweb; + iev->data = iev; + event_set(&iev->ev, fd, EV_READ, server_dispatch_gotweb, iev); + + env->iev_gotweb = iev; +} + +static void sockets_dispatch_main(int fd, short event, void *arg) { struct imsgev *iev = arg; @@ -281,15 +393,15 @@ sockets_dispatch_main(int fd, short event, void *arg) case GOTWEBD_IMSG_CFG_SOCK: config_getsock(env, &imsg); break; - case GOTWEBD_IMSG_CFG_FD: - config_getfd(env, &imsg); - break; case GOTWEBD_IMSG_CFG_DONE: config_getcfg(env, &imsg); break; - case GOTWEBD_CTL_START: - sockets_launch(); + case GOTWEBD_IMSG_CTL_PIPE: + recv_gotweb_pipe(env, &imsg); break; + case GOTWEBD_IMSG_CTL_START: + sockets_launch(env); + break; default: fatalx("%s: unknown imsg type %d", __func__, imsg.hdr.type); @@ -354,6 +466,13 @@ sockets_shutdown(void) free(h); } + while (!TAILQ_EMPTY(&requests)) { + struct request *c; + + c = TAILQ_FIRST(&requests); + fcgi_cleanup_request(c); + } + imsgbuf_clear(&gotwebd_env->iev_parent->ibuf); free(gotwebd_env->iev_parent); free(gotwebd_env); @@ -362,12 +481,12 @@ sockets_shutdown(void) } int -sockets_privinit(struct gotwebd *env, struct socket *sock) +sockets_privinit(struct gotwebd *env, struct socket *sock, uid_t uid, gid_t gid) { if (sock->conf.af_type == AF_UNIX) { log_info("%s: initializing unix socket %s", __func__, sock->conf.unix_socket_name); - sock->fd = sockets_unix_socket_listen(env, sock); + sock->fd = sockets_unix_socket_listen(env, sock, uid, gid); if (sock->fd == -1) return -1; } @@ -385,7 +504,8 @@ sockets_privinit(struct gotwebd *env, struct socket *s } static int -sockets_unix_socket_listen(struct gotwebd *env, struct socket *sock) +sockets_unix_socket_listen(struct gotwebd *env, struct socket *sock, + uid_t uid, gid_t gid) { int u_fd = -1; mode_t old_umask, mode; @@ -425,8 +545,7 @@ sockets_unix_socket_listen(struct gotwebd *env, struct return -1; } - if (chown(sock->conf.unix_socket_name, env->pw->pw_uid, - env->pw->pw_gid) == -1) { + if (chown(sock->conf.unix_socket_name, uid, gid) == -1) { log_warn("%s: chown", __func__); close(u_fd); (void)unlink(sock->conf.unix_socket_name); @@ -521,6 +640,7 @@ sockets_socket_accept(int fd, short event, void *arg) struct sockaddr_storage ss; struct timeval backoff; struct request *c = NULL; + uint8_t *buf = NULL; socklen_t len; int s; @@ -557,28 +677,33 @@ sockets_socket_accept(int fd, short event, void *arg) c = calloc(1, sizeof(struct request)); if (c == NULL) { - log_warn("%s", __func__); + log_warn("%s: calloc", __func__); close(s); cgi_inflight--; return; } - c->tp = template(c, &fcgi_write, c->outbuf, sizeof(c->outbuf)); - if (c->tp == NULL) { - log_warn("%s", __func__); + buf = calloc(1, FCGI_RECORD_SIZE); + if (buf == NULL) { + log_warn("%s: calloc", __func__); close(s); - cgi_inflight--; free(c); + cgi_inflight--; return; } + c->buf = buf; c->fd = s; + c->resp_fd = -1; c->sock = sock; memcpy(c->priv_fd, gotwebd_env->priv_fd, sizeof(c->priv_fd)); + c->sock_id = sock->conf.id; c->buf_pos = 0; c->buf_len = 0; c->request_started = 0; c->sock->client_status = CLIENT_CONNECT; + c->request_id = ++request_id; /* TODO: use a client hash table like gotd does */ + log_info("%s: new request_id: %d", __func__, request_id); event_set(&c->ev, s, EV_READ|EV_PERSIST, fcgi_request, c); event_add(&c->ev, NULL); @@ -586,6 +711,8 @@ sockets_socket_accept(int fd, short event, void *arg) evtimer_set(&c->tmo, fcgi_timeout, c); evtimer_add(&c->tmo, &timeout); + TAILQ_INSERT_TAIL(&requests, c, entry); + client_cnt++; return; @@ -596,7 +723,7 @@ err: free(c); } -static void +void sockets_rlimit(int maxfd) { struct rlimit rl;