commit - 371384deec49fde240400ddd0867d8488ba0eed4
commit + b27be572b3b9afbcc52cb800bfacbd20218d4177
blob - 980ecf25520da4497a36f585459ebecbf7b1d3f5
blob + 90eb143afce8e50c7ba53d141294ca8616d7a074
--- gotwebd/config.c
+++ gotwebd/config.c
}
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;
}
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;
}
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 + 3ec4aee067d2084604a4b3ca1fa315b83811ccc3
--- gotwebd/fcgi.c
+++ gotwebd/fcgi.c
#include "got_error.h"
#include "got_reference.h"
+#include "got_lib_poll.h"
+
#include "gotwebd.h"
#include "log.h"
#include "tmpl.h"
struct fcgi_end_request_body *);
extern int cgi_inflight;
-extern volatile int client_cnt;
+extern struct requestlist requests;
void
fcgi_request(int fd, short events, void *arg)
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);
c->id = id;
}
+static void
+fcgi_forward_response(int fd, short event, void *arg)
+{
+ const struct got_error *err = NULL;
+ struct request *c = arg;
+ uint8_t outbuf[GOTWEBD_CACHESIZE];
+ ssize_t r;
+
+ if ((event & EV_READ) == 0)
+ return;
+
+ r = read(fd, outbuf, sizeof(outbuf));
+ if (r == 0)
+ return;
+
+ if (r == -1) {
+ log_warn("read response");
+ } else {
+ err = got_poll_write_full(c->fd, outbuf, r);
+ if (err) {
+ log_warnx("forward response: %s", err->msg);
+ fcgi_cleanup_request(c);
+ return;
+ }
+ }
+
+ 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)
{
}
if (n == 0) {
- gotweb_process_request(c);
- template_flush(c->tp);
+ process_request(c);
return;
}
fcgi_cleanup_request(struct request *c)
{
cgi_inflight--;
- client_cnt--;
- evtimer_del(&c->tmo);
+ sockets_del_request(c);
+
+ 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
#include <imsg.h>
#include <sha1.h>
#include <sha2.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
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) {
return -1;
}
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 - 8413e78f48a48cfc92e794fff34a97a8765976e7
blob + f6353254aaee1f21a9dd7e56f1aff9a4d95f47af
--- gotwebd/gotwebd.8
+++ gotwebd/gotwebd.8
.Xr gotwebd.conf 5
configuration file.
.It
-Git repositories must be created at a suitable location inside the
-web server's
-.Xr chroot 2
-environment.
-These repositories should
+Git repositories must be created.
+These repositories may reside anywhere in the filesystem and must
+be readable, but should
.Em not
-be writable by the user ID shared between
+be writable, by the user
.Nm
-and
-.Xr httpd 8 .
+runs as.
The default location for repositories published by
.Nm
is
.Pa /var/www/got/public .
.It
-Git repositories served by
+If the Git repositories served by
.Nm
-should be kept up-to-date with a mechanism such as
+do not receive changes from committers directly, they need to be kept
+up-to-date with a mechanism such as
.Cm got fetch ,
.Xr git-fetch 1 ,
or
blob - 61a08a5b8aa69b071a02be23f9794bab66c345fc
blob + f66b9f38c71db3ea73f08f317e7446e2d3929e59
--- gotwebd/gotwebd.c
+++ gotwebd/gotwebd.c
__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;
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;
return (ret);
}
+static int
+send_imsg(struct imsgev *iev, uint32_t type, int fd, const void *data,
+ uint16_t len)
+{
+ int ret, d = -1;
+
+ 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;
+ /* Flush imsg to prevent fd exhaustion. 'd' will be closed. */
+ if (imsgbuf_flush(&iev->ibuf) == -1)
+ goto err;
+ imsg_event_add(iev);
+ }
+
+ return 0;
+err:
+ 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, d;
+ size_t i;
+ int ret = 0;
for (i = 0; i < env->nserver; ++i) {
- d = -1;
- if (fd != -1 && (d = dup(fd)) == -1)
- goto err;
+ ret = send_imsg(&env->iev_server[i], type, fd, data, len);
+ if (ret)
+ break;
+ }
- ret = imsg_compose_event(&env->iev_server[i], type, 0, -1,
- d, data, len);
- if (ret == -1)
- goto err;
+ return ret;
+}
- /* prevent fd exhaustion */
- if (d != -1) {
- if (imsgbuf_flush(&env->iev_server[i].ibuf) == -1)
- goto err;
- imsg_event_add(&env->iev_server[i]);
- }
+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;
}
- if (fd != -1)
- close(fd);
- return 0;
-
-err:
- if (fd != -1)
- close(fd);
- return -1;
+ return ret;
}
int
}
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;
}
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; */
}
}
-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, const char *username,
+ void (*handler)(int, short, void *))
{
- const char *argv[8];
+ const char *argv[9];
int argc = 0;
int p[2];
pid_t pid;
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++] = username;
+ } else if (proc_type == GOTWEBD_PROC_GOTWEB) {
+ argv[argc++] = "-G";
+ argv[argc++] = username;
+ }
if (strcmp(env->gotwebd_conffile, GOTWEBD_CONF) != 0) {
argv[argc++] = "-f";
argv[argc++] = env->gotwebd_conffile;
struct passwd *pw;
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 *www_username = GOTWEBD_WWW_USER;
+ gid_t www_gid;
const char *argv0;
if ((argv0 = argv[0]) == NULL)
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)
case 'd':
env->gotwebd_debug = 1;
break;
+ case 'G':
+ proc_type = GOTWEBD_PROC_GOTWEB;
+ gotwebd_username = optarg;
+ break;
case 'f':
conffile = optarg;
break;
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)
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;
+ if (env->www_user)
+ www_username = env->www_user;
}
+ pw = getpwnam(www_username);
+ if (pw == NULL)
+ fatalx("unknown user %s", www_username);
+ www_gid = pw->pw_gid;
+
+ pw = getpwnam(gotwebd_username);
+ if (pw == 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) {
+ switch (proc_type) {
+ case GOTWEBD_PROC_SERVER:
setproctitle("server");
log_procinit("server");
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)
sockets(env, GOTWEBD_SOCK_FILENO);
return 1;
+ case GOTWEBD_PROC_GOTWEB:
+ setproctitle("gotweb");
+ log_procinit("gotweb");
+
+ 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");
+
+ gotweb(env, GOTWEBD_SOCK_FILENO);
+ return 1;
+ default:
+ break;
}
if (!env->gotwebd_debug && daemon(1, 0) == -1)
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_username,
+ gotwebd_dispatch_server);
+ spawn_process(env, argv0, &env->iev_gotweb[i],
+ GOTWEBD_PROC_GOTWEB, gotwebd_username,
+ gotwebd_dispatch_gotweb);
}
-
if (chdir("/") == -1)
fatal("chdir /");
signal_add(&sigpipe, NULL);
signal_add(&sigusr1, NULL);
- if (gotwebd_configure(env) == -1)
+ if (gotwebd_configure(env, pw->pw_uid, www_gid) == -1)
fatalx("configuration failed");
if (setgroups(1, &pw->pw_gid) == -1 ||
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__);
}
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");
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
}
free(env->iev_server);
+ for (i = 0; i < env->nserver; ++i) {
+ event_del(&env->iev_gotweb[i].ev);
+ imsgbuf_clear(&env->iev_gotweb[i].ibuf);
+ close(env->iev_gotweb[i].ibuf.fd);
+ env->iev_gotweb[i].ibuf.fd = -1;
+ }
+ free(env->iev_gotweb);
+
do {
pid = waitpid(WAIT_ANY, &status, 0);
if (pid <= 0)
blob - 2bbc2da98a42f54eda543a5d44e452364ab5402f
blob + 562d13bf30399506f8f4f2c9eca0d103cf6e5cdd
--- gotwebd/gotwebd.conf.5
+++ gotwebd/gotwebd.conf.5
lan_addr = "192.168.0.1"
listen on $lan_addr port 9090
.Ed
-.Pp
-Paths mentioned in
-.Nm
-must be relative to
-.Pa /var/www ,
-the
-.Xr chroot 2
-environment of
-.Xr httpd 8 .
.Sh GLOBAL CONFIGURATION
The available global configuration directives are as follows:
.Bl -tag -width Ds
If not specified, it defaults to
.Pa /var/www ,
the home directory of the www user.
+Setting the
+.Ar path
+to
+.Pa /
+effectively disables chroot.
.It Ic listen on Ar address Ic port Ar number
Configure an address and port for incoming FastCGI connections.
Valid
.Ux Ns -domain
socket for incoming FastCGI connections.
May be specified multiple times to build up a list of listening sockets.
+.Pp
+While the specified
+.Ar path
+must be absolute, it should usually point inside the web server's chroot
+directory such that the web server can access the socket.
.It Ic prefork Ar number
Run the specified number of server processes.
.Xr gotwebd 8
Set the
.Ar user
which will run
+.Xr gotwebd 8 .
+If not specified, the user _gotwebd will be used.
+.It Ic www user Ar user
+Set the
+.Ar user
+which runs
+.Xr httpd 8 .
+Needed to ensure that the web server can access UNIX-domain sockets created by
.Xr gotwebd 8 .
If not specified, the user www will be used.
.El
If this option is not specified then the default style sheet
.Sq gotweb.css
will be used.
+.Pp
+This path must be valid in the web server's URL space since browsers
+will attempt to fetch it.
.It Ic logo Ar path
Set the path to an image file containing a logo to be displayed.
Defaults to
.Sq got.png .
+.Pp
+This path must be valid in the web server's URL space since browsers
+will attempt to fetch it.
.It Ic logo_url Ar url
Set a hyperlink for the logo.
Defaults to
.It Ic repos_path Ar path
Set the path to the directory which contains Git repositories that
the server should publish.
+This path is absolute.
+Repositories can be served even if they reside outside the web server's
+chroot directory.
+.Pp
Defaults to
.Pa /got/public
-under the chroot.
+inside the web server's chroot directory.
+The
+.Cm chroot
+directive must be used before the server declaration in order to
+take effect.
.It Ic respect_exportok Ar on | off
Set whether to display the repository only if it contains the magic
.Pa git-daemon-export-ok
.Sh EXAMPLES
A sample configuration:
.Bd -literal -offset indent
+www user "www" # www username needs quotes since www is a keyword
+
server "localhost" {
site_name "my public repos"
site_owner "Flan Hacker"
Another example, this time listening on a local port instead of the
implicit
.Ux
-socket.
+socket, and serving repositories located outside the web server's chroot:
.Bd -literal -offset indent
listen on 127.0.0.1 port 9000
listen on ::1 port 9000
server "localhost" {
- site_name "my public repos"
+ site_name "my public repos"
+ repos_path "/var/git"
}
.Ed
.Sh SEE ALSO
blob - 7dd491f1a07f53e526bf9e5df13d9e0767649384
blob + 27a53cfbf3baff7f391f1445c503ba9712923fc6
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
#define GOTWEBD_CONF "/etc/gotwebd.conf"
#ifndef GOTWEBD_DEFAULT_USER
-#define GOTWEBD_DEFAULT_USER "www"
+#define GOTWEBD_DEFAULT_USER "_gotwebd"
#endif
+#ifndef GOTWEBD_WWW_USER
+#define GOTWEBD_WWW_USER "www"
+#endif
+
#define GOTWEBD_MAXDESCRSZ 1024
#define GOTWEBD_MAXCLONEURLSZ 1024
#define GOTWEBD_CACHESIZE 1024
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 {
struct template;
struct request {
+ TAILQ_ENTRY(request) entry;
struct socket *sock;
struct server *srv;
struct transport *t;
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];
uint8_t request_started;
};
+TAILQ_HEAD(requestlist, request);
struct fcgi_begin_request_body {
uint16_t role;
int priv_fd[PRIV_FDS__MAX];
char *user;
+ char *www_user;
const char *gotwebd_conffile;
int gotwebd_debug;
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;
/* 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);
+void sockets_del_request(struct request *);
/* gotweb.c */
void gotweb_index_navs(struct request *, struct gotweb_url *, int *,
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 *));
/* 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 - ce616554e436816c2be0cd52555746f30ab943f9
blob + 28cc307a5e9e04f189781f0f3c7c189a87700a17
--- gotwebd/parse.y
+++ gotwebd/parse.y
%}
-%token LISTEN WWW_PATH SITE_NAME SITE_OWNER SITE_LINK LOGO
+%token LISTEN WWW SITE_NAME SITE_OWNER SITE_LINK LOGO
%token LOGO_URL SHOW_REPO_OWNER SHOW_REPO_AGE SHOW_REPO_DESCRIPTION
%token MAX_REPOS_DISPLAY REPOS_PATH MAX_COMMITS_DISPLAY ON ERROR
%token SHOW_SITE_OWNER SHOW_REPO_CLONEURL PORT PREFORK RESPECT_EXPORTOK
n = strlcpy(gotwebd->httpd_chroot, $2,
sizeof(gotwebd->httpd_chroot));
if (n >= sizeof(gotwebd->httpd_chroot)) {
- yyerror("%s: httpd_chroot truncated", __func__);
+ yyerror("chroot path too long: %s", $2);
free($2);
YYERROR;
}
+ if (gotwebd->httpd_chroot[0] != '/') {
+ yyerror("chroot path must be an absolute path: "
+ "bad path %s", gotwebd->httpd_chroot);
+ free($2);
+ YYERROR;
+ }
free($2);
}
| LISTEN ON listen_addr PORT STRING {
free(gotwebd->user);
gotwebd->user = $2;
}
+ | WWW USER STRING {
+ if (gotwebd->www_user != NULL)
+ yyerror("www user already specified");
+ free(gotwebd->www_user);
+ gotwebd->www_user = $3;
+ }
;
server : SERVER STRING {
{ "summary_commits_display", SUMMARY_COMMITS_DISPLAY },
{ "summary_tags_display", SUMMARY_TAGS_DISPLAY },
{ "user", USER },
+ { "www", WWW },
};
const struct keywords *p;
/* add the implicit listen on socket */
if (TAILQ_EMPTY(&gotwebd->addresses)) {
- const char *path = D_HTTPD_CHROOT D_UNIX_SOCKET;
+ char path[_POSIX_PATH_MAX];
+
+ if (strlcpy(path, gotwebd->httpd_chroot, sizeof(path))
+ >= sizeof(path)) {
+ yyerror("chroot path too long: %s",
+ gotwebd->httpd_chroot);
+ }
+ if (strlcat(path, D_UNIX_SOCKET, sizeof(path))
+ >= sizeof(path)) {
+ yyerror("chroot path too long: %s",
+ gotwebd->httpd_chroot);
+ }
if (get_unix_addr(path) == -1)
yyerror("can't listen on %s", path);
}
n = strlcpy(srv->name, name, sizeof(srv->name));
if (n >= sizeof(srv->name))
fatalx("%s: strlcpy", __func__);
- n = strlcpy(srv->repos_path, D_GOTPATH,
+ n = strlcpy(srv->repos_path, gotwebd->httpd_chroot,
sizeof(srv->repos_path));
if (n >= sizeof(srv->repos_path))
fatalx("%s: strlcpy", __func__);
+ n = strlcat(srv->repos_path, D_GOTPATH,
+ sizeof(srv->repos_path));
+ if (n >= sizeof(srv->repos_path))
+ fatalx("%s: strlcat", __func__);
n = strlcpy(srv->site_name, D_SITENAME,
sizeof(srv->site_name));
if (n >= sizeof(srv->site_name))
blob - 4ca6de208a73860f721177a094834ffc846d1aaf
blob + fe41e8a1588f58c48515f05270570fa6735580c8
--- gotwebd/sockets.c
+++ gotwebd/sockets.c
#include <netdb.h>
#include <poll.h>
#include <pwd.h>
+#include <siphash.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <util.h>
-#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"
#define SOCKS_BACKLOG 5
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
-volatile int client_cnt;
+static volatile int client_cnt;
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 *);
int cgi_inflight = 0;
+/* Request hash table needs some spare room to avoid collisions. */
+struct requestlist requests[GOTWEBD_MAXCLIENTS * 4];
+static SIPHASH_KEY requests_hash_key;
+
+static void
+requests_init(void)
+{
+ int i;
+
+ arc4random_buf(&requests_hash_key, sizeof(requests_hash_key));
+
+ for (i = 0; i < nitems(requests); i++)
+ TAILQ_INIT(&requests[i]);
+}
+
+static uint64_t
+request_hash(uint32_t request_id)
+{
+ return SipHash24(&requests_hash_key, &request_id, sizeof(request_id));
+}
+
+static void
+add_request(struct request *c)
+{
+ uint64_t slot = request_hash(c->request_id) % nitems(requests);
+ TAILQ_INSERT_HEAD(&requests[slot], c, entry);
+ client_cnt++;
+}
+
void
+sockets_del_request(struct request *c)
+{
+ uint64_t slot = request_hash(c->request_id) % nitems(requests);
+ TAILQ_REMOVE(&requests[slot], c, entry);
+ client_cnt--;
+}
+
+static struct request *
+find_request(uint32_t request_id)
+{
+ uint64_t slot;
+ struct request *c;
+
+ slot = request_hash(request_id) % nitems(requests);
+ TAILQ_FOREACH(c, &requests[slot], entry) {
+ if (c->request_id == request_id)
+ return c;
+ }
+
+ return NULL;
+}
+
+static void
+requests_purge(void)
+{
+ uint64_t slot;
+ struct request *c;
+
+ for (slot = 0; slot < nitems(requests); slot++) {
+ while (!TAILQ_EMPTY(&requests[slot])) {
+ c = TAILQ_FIRST(&requests[slot]);
+ fcgi_cleanup_request(c);
+ }
+ }
+}
+
+static uint32_t
+get_request_id(void)
+{
+ int duplicate = 0;
+ uint32_t id;
+
+ do {
+ id = arc4random();
+ duplicate = (find_request(id) != NULL);
+ } while (duplicate || id == 0);
+
+ return id;
+}
+
+void
sockets(struct gotwebd *env, int fd)
{
struct event sighup, sigint, sigusr1, sigchld, sigterm;
struct event_base *evb;
+ requests_init();
+
evb = event_init();
sockets_rlimit(-1);
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
}
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);
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
}
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));
+
+ 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;
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);
free(h);
}
+ requests_purge();
+
imsgbuf_clear(&gotwebd_env->iev_parent->ibuf);
free(gotwebd_env->iev_parent);
free(gotwebd_env);
}
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;
}
}
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;
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);
struct sockaddr_storage ss;
struct timeval backoff;
struct request *c = NULL;
+ uint8_t *buf = NULL;
socklen_t len;
int s;
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);
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 = get_request_id();
event_set(&c->ev, s, EV_READ|EV_PERSIST, fcgi_request, c);
event_add(&c->ev, NULL);
evtimer_set(&c->tmo, fcgi_timeout, c);
evtimer_add(&c->tmo, &timeout);
- client_cnt++;
-
+ add_request(c);
return;
err:
cgi_inflight--;
free(c);
}
-static void
+void
sockets_rlimit(int maxfd)
{
struct rlimit rl;