Commit Diff


commit - 1fbac5ba7c6c04d59d1c1199cd9f57638967a504
commit + bc99d868bc3745dcc65add06cd3f9b9ec3575cb5
blob - 47eed0b7d5fb40ad6b58a88c5cfded79bfea8444
blob + 68170246e73c706ee72ec56fee500b46c8a67f44
--- ex.c
+++ ex.c
@@ -27,200 +27,15 @@
 #include <stdarg.h>
 #include <string.h>
 
-int
-send_string(int fd, const char *str)
-{
-	ssize_t len;
-
-	if (str == NULL)
-		len = 0;
-	else
-		len = strlen(str);
-
-	if (write(fd, &len, sizeof(len)) != sizeof(len))
-		return 0;
-
-	if (len != 0)
-		if (write(fd, str, len) != len)
-			return 0;
-
-	return 1;
-}
-
-int
-recv_string(int fd, char **ret)
-{
-	ssize_t len;
-
-	if (read(fd, &len, sizeof(len)) != sizeof(len))
-		return 0;
-
-	if (len == 0) {
-		*ret = NULL;
-		return 1;
-	}
-
-	if ((*ret = calloc(1, len+1)) == NULL)
-		return 0;
-
-	if (read(fd, *ret, len) != len)
-		return 0;
-	return 1;
-}
-
-int
-send_iri(int fd, struct iri *i)
-{
-	return send_string(fd, i->schema)
-		&& send_string(fd, i->host)
-		&& send_string(fd, i->port)
-		&& send_string(fd, i->path)
-		&& send_string(fd, i->query);
-}
-
-int
-recv_iri(int fd, struct iri *i)
-{
-	memset(i, 0, sizeof(*i));
-
-	if (!recv_string(fd, &i->schema)
-	    || !recv_string(fd, &i->host)
-	    || !recv_string(fd, &i->port)
-	    || !recv_string(fd, &i->path)
-	    || !recv_string(fd, &i->query))
-		return 0;
-
-	return 1;
-}
-
-void
-free_recvd_iri(struct iri *i)
-{
-	free(i->schema);
-	free(i->host);
-	free(i->port);
-	free(i->path);
-	free(i->query);
-}
-
-int
-send_vhost(int fd, struct vhost *vhost)
-{
-	ssize_t n;
-
-	if (vhost < hosts || vhost > hosts + HOSTSLEN)
-		return 0;
-
-	n = vhost - hosts;
-	return write(fd, &n, sizeof(n)) == sizeof(n);
-}
-
-int
-recv_vhost(int fd, struct vhost **vhost)
-{
-	ssize_t n;
-
-	if (read(fd, &n, sizeof(n)) != sizeof(n))
-		return 0;
-
-	if (n < 0 || n > HOSTSLEN)
-		return 0;
-
-	*vhost = &hosts[n];
-	if ((*vhost)->domain == NULL)
-		return 0;
-	return 1;
-}
-
-int
-send_time(int fd, time_t t)
-{
-	return write(fd, &t, sizeof(t)) == sizeof(t);
-}
-
-int
-recv_time(int fd, time_t *t)
-{
-	return read(fd, t, sizeof(*t)) == sizeof(*t);
-}
-
-/* send d though fd. see /usr/src/usr.sbin/syslogd/privsep_fdpass.c
- * for an example */
-int
-send_fd(int fd, int d)
-{
-	struct msghdr msg;
-	union {
-		struct cmsghdr hdr;
-		unsigned char buf[CMSG_SPACE(sizeof(int))];
-	} cmsgbuf;
-	struct cmsghdr *cmsg;
-	struct iovec vec;
-	int result = 1;
-	ssize_t n;
-
-	memset(&msg, 0, sizeof(msg));
-
-	if (d >= 0) {
-		msg.msg_control = &cmsgbuf.buf;
-		msg.msg_controllen = sizeof(cmsgbuf.buf);
-		cmsg = CMSG_FIRSTHDR(&msg);
-		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-		cmsg->cmsg_level = SOL_SOCKET;
-		cmsg->cmsg_type = SCM_RIGHTS;
-		*(int*)CMSG_DATA(cmsg) = d;
-	} else
-		result = 0;
-
-	vec.iov_base = &result;
-	vec.iov_len = sizeof(int);
-	msg.msg_iov = &vec;
-	msg.msg_iovlen = 1;
-
-	if ((n = sendmsg(fd, &msg, 0)) == -1 || n != sizeof(int)) {
-                log_err(NULL, "sendmsg: got %zu but wanted %zu: (errno) %s",
-		    n, sizeof(int), strerror(errno));
-		return 0;
-	}
-	return 1;
-}
-
-/* receive a descriptor via fd */
-int
-recv_fd(int fd)
-{
-	struct msghdr msg;
-	union {
-		struct cmsghdr hdr;
-		char buf[CMSG_SPACE(sizeof(int))];
-	} cmsgbuf;
-	struct cmsghdr *cmsg;
-	struct iovec vec;
-	ssize_t n;
-	int result;
+static void	handle_imsg_cgi_req(struct imsgbuf*, struct imsg*, size_t);
+static void	handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
+static void	handle_dispatch_imsg(int, short, void*);
 
-	memset(&msg, 0, sizeof(msg));
-	vec.iov_base = &result;
-	vec.iov_len = sizeof(int);
-	msg.msg_iov = &vec;
-	msg.msg_iovlen = 1;
-	msg.msg_control = &cmsgbuf.buf;
-	msg.msg_controllen = sizeof(cmsgbuf.buf);
+static imsg_handlerfn *handlers[] = {
+	[IMSG_CGI_REQ] = handle_imsg_cgi_req,
+	[IMSG_QUIT] = handle_imsg_quit,
+};
 
-	if ((n = recvmsg(fd, &msg, 0)) != sizeof(int)) {
-		log_err(NULL, "read %zu bytes bu wanted %zu\n", n, sizeof(int));
-		return -1;
-	}
-
-	if (result) {
-		cmsg = CMSG_FIRSTHDR(&msg);
-		if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS)
-			return -1;
-		return (*(int *)CMSG_DATA(cmsg));
-	} else
-		return -1;
-}
-
 static inline void
 safe_setenv(const char *name, const char *val)
 {
@@ -299,10 +114,7 @@ setenv_time(const char *var, time_t t)
 
 /* fd or -1 on error */
 static int
-launch_cgi(struct iri *iri, const char *spath, char *relpath,
-    const char *addr, const char *ruser, const char *cissuer,
-    const char *chash, time_t notbefore, time_t notafter,
-    struct vhost *vhost)
+launch_cgi(struct iri *iri, struct cgireq *req, struct vhost *vhost)
 {
 	int p[2];		/* read end, write end */
 
@@ -322,38 +134,38 @@ launch_cgi(struct iri *iri, const char *spath, char *r
 		if (dup2(p[1], 1) == -1)
 			goto childerr;
 
-		ex = xasprintf("%s/%s", vhost->dir, spath);
+		ex = xasprintf("%s/%s", vhost->dir, req->spath);
 
 		serialize_iri(iri, iribuf, sizeof(iribuf));
 
 		safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
 		safe_setenv("GEMINI_DOCUMENT_ROOT", vhost->dir);
 		safe_setenv("GEMINI_SCRIPT_FILENAME",
-		    xasprintf("%s/%s", vhost->dir, spath));
+		    xasprintf("%s/%s", vhost->dir, req->spath));
 		safe_setenv("GEMINI_URL", iribuf);
 
 		strlcpy(path, "/", sizeof(path));
-		strlcat(path, spath, sizeof(path));
+		strlcat(path, req->spath, sizeof(path));
 		safe_setenv("GEMINI_URL_PATH", path);
 
-		if (relpath != NULL) {
+		if (*req->relpath != '\0') {
 			strlcpy(path, "/", sizeof(path));
-			strlcat(path, relpath, sizeof(path));
+			strlcat(path, req->relpath, sizeof(path));
 			safe_setenv("PATH_INFO", path);
 
 			strlcpy(path, vhost->dir, sizeof(path));
 			strlcat(path, "/", sizeof(path));
-			strlcat(path, relpath, sizeof(path));
+			strlcat(path, req->relpath, sizeof(path));
 			safe_setenv("PATH_TRANSLATED", path);
 		}
 
 		safe_setenv("QUERY_STRING", iri->query);
-		safe_setenv("REMOTE_ADDR", addr);
-		safe_setenv("REMOTE_HOST", addr);
+		safe_setenv("REMOTE_ADDR", req->addr);
+		safe_setenv("REMOTE_HOST", req->addr);
 		safe_setenv("REQUEST_METHOD", "");
 
 		strlcpy(path, "/", sizeof(path));
-		strlcat(path, spath, sizeof(path));
+		strlcat(path, req->spath, sizeof(path));
 		safe_setenv("SCRIPT_NAME", path);
 
 		safe_setenv("SERVER_NAME", iri->host);
@@ -364,16 +176,16 @@ launch_cgi(struct iri *iri, const char *spath, char *r
 		safe_setenv("SERVER_PROTOCOL", "GEMINI");
 		safe_setenv("SERVER_SOFTWARE", "gmid/1.5");
 
-		if (ruser != NULL)
+		if (*req->subject != '\0')
 			safe_setenv("AUTH_TYPE", "Certificate");
 		else
 			safe_setenv("AUTH_TYPE", "");
 
-		safe_setenv("REMOTE_USER", ruser);
-		safe_setenv("TLS_CLIENT_ISSUER", cissuer);
-		safe_setenv("TLS_CLIENT_HASH", chash);
-		setenv_time("TLS_CLIENT_NOT_AFTER", notafter);
-		setenv_time("TLS_CLIENT_NOT_BEFORE", notbefore);
+		safe_setenv("REMOTE_USER", req->subject);
+		safe_setenv("TLS_CLIENT_ISSUER", req->issuer);
+		safe_setenv("TLS_CLIENT_HASH", req->hash);
+		setenv_time("TLS_CLIENT_NOT_AFTER", req->notafter);
+		setenv_time("TLS_CLIENT_NOT_BEFORE", req->notbefore);
 
 		strlcpy(path, ex, sizeof(path));
 
@@ -383,7 +195,7 @@ launch_cgi(struct iri *iri, const char *spath, char *r
 			goto childerr;
 		}
 
-		do_exec(ex, spath, iri->query);
+		do_exec(ex, req->spath, iri->query);
 		goto childerr;
 	}
 
@@ -399,52 +211,67 @@ childerr:
 }
 
 static void
-handle_fork_req(int fd, short ev, void *data)
+handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
 {
-	char *spath, *relpath, *addr, *ruser, *cissuer, *chash;
-	struct vhost *vhost;
-	struct iri iri;
-	time_t notbefore, notafter;
-	int d;
+	struct cgireq	req;
+	struct iri	iri;
+	int		fd;
 
-	if (!recv_iri(fd, &iri)
-	    || !recv_string(fd, &spath)
-	    || !recv_string(fd, &relpath)
-	    || !recv_string(fd, &addr)
-	    || !recv_string(fd, &ruser)
-	    || !recv_string(fd, &cissuer)
-	    || !recv_string(fd, &chash)
-	    || !recv_time(fd, &notbefore)
-	    || !recv_time(fd, &notafter)
-	    || !recv_vhost(fd, &vhost)) {
-		if (errno == EINTR) {
-			event_loopbreak();
-			return;
-		}
-		fatal("failure in handling fork request: %s", strerror(errno));
+	if (datalen != sizeof(req))
+		abort();
+
+	memcpy(&req, imsg->data, datalen);
+
+	iri.schema = req.iri_schema_off + req.buf;
+	iri.host = req.iri_host_off + req.buf;
+	iri.port = req.iri_port_off + req.buf;
+	iri.path = req.iri_path_off + req.buf;
+	iri.query = req.iri_query_off + req.buf;
+	iri.fragment = req.iri_fragment_off + req.buf;
+
+	/* patch the query, otherwise do_exec will always pass "" as
+	 * first argument to the script. */
+	if (*iri.query == '\0')
+		iri.query = NULL;
+
+	if (req.host_off > HOSTSLEN || hosts[req.host_off].domain == NULL)
+		abort();
+
+	fd = launch_cgi(&iri, &req, &hosts[req.host_off]);
+	imsg_compose(ibuf, IMSG_CGI_RES, imsg->hdr.peerid, 0, fd, NULL, 0);
+	imsg_flush(ibuf);
+}
+
+static void
+handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
+{
+	int i;
+
+	(void)ibuf;
+	(void)imsg;
+	(void)datalen;
+
+	for (i = 0; i < conf.prefork; ++i) {
+		imsg_compose(&servibuf[i], IMSG_QUIT, 0, 0, -1, NULL, 0);
+		imsg_flush(&exibuf);
+		close(servibuf[i].fd);
 	}
 
-	d = launch_cgi(&iri, spath, relpath, addr, ruser, cissuer, chash,
-	    notbefore, notafter, vhost);
-	if (!send_fd(fd, d))
-		fatal("failure in sending the fd to the server: %s",
-		    strerror(errno));
-	close(d);
+	event_loopbreak();
+}
 
-	free_recvd_iri(&iri);
-	free(spath);
-	free(relpath);
-	free(addr);
-	free(ruser);
-	free(cissuer);
-	free(chash);
+static void
+handle_dispatch_imsg(int fd, short ev, void *d)
+{
+	struct imsgbuf *ibuf = d;
+	dispatch_imsg(ibuf, handlers, sizeof(handlers));
 }
 
 int
-executor_main(void)
+executor_main(struct imsgbuf *ibuf)
 {
 	struct vhost	*vhost;
-	struct event	 evs[PROC_MAX];
+	struct event	 evs[PROC_MAX], imsgev;
 	int		 i;
 
 #ifdef __OpenBSD__
@@ -462,9 +289,15 @@ executor_main(void)
 
 	event_init();
 
+	if (ibuf != NULL) {
+		event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST,
+		    handle_dispatch_imsg, ibuf);
+		event_add(&imsgev, NULL);
+	}
+
 	for (i = 0; i < conf.prefork; ++i) {
-		event_set(&evs[i], servpipes[i], EV_READ | EV_PERSIST,
-		    handle_fork_req, NULL);
+		event_set(&evs[i], servibuf[i].fd, EV_READ | EV_PERSIST,
+		    handle_dispatch_imsg, &servibuf[i]);
 		event_add(&evs[i], NULL);
 	}
 
blob - 823299b09ff3f59370e72b7cbc5c050e7238c5e3
blob + 0714df4fea4e5001d15aa169e1e48cb871da36fb
--- gmid.c
+++ gmid.c
@@ -25,13 +25,11 @@
 #include <signal.h>
 #include <string.h>
 
-volatile sig_atomic_t hupped;
-
 struct vhost hosts[HOSTSLEN];
 
-int exfd, logfd, sock4, sock6, servpipes[PROC_MAX];
+int sock4, sock6;
 
-struct imsgbuf logpibuf, logcibuf;
+struct imsgbuf logibuf, exibuf, servibuf[PROC_MAX];
 
 const char *config_path, *certs_dir, *hostname;
 
@@ -40,14 +38,6 @@ struct conf conf;
 struct tls_config *tlsconf;
 struct tls *ctx;
 
-void
-sig_handler(int sig)
-{
-	(void)sig;
-
-	hupped = sig == SIGHUP;
-}
-
 /* XXX: create recursively */
 void
 mkdirs(const char *path)
@@ -196,13 +186,12 @@ setup_tls(void)
 }
 
 static int
-listener_main(void)
+listener_main(struct imsgbuf *ibuf)
 {
 	drop_priv();
-	unblock_signals();
 	load_default_mime(&conf.mime);
 	load_vhosts();
-	loop(ctx, sock4, sock6);
+	loop(ctx, sock4, sock6, ibuf);
 	return 0;
 }
 
@@ -320,22 +309,21 @@ logger_init(void)
 	case -1:
 		err(1, "fork");
 	case 0:
-		logfd = p[1];
+		signal(SIGHUP, SIG_IGN);
 		close(p[0]);
 		setproctitle("logger");
-		imsg_init(&logcibuf, p[1]);
+		imsg_init(&logibuf, p[1]);
 		drop_priv();
-		_exit(logger_main(p[1], &logcibuf));
+		_exit(logger_main(p[1], &logibuf));
 	default:
-		logfd = p[0];
 		close(p[1]);
-		imsg_init(&logpibuf, p[0]);
+		imsg_init(&logibuf, p[0]);
 		return;
 	}
 }
 
 static int
-serve(int argc, char **argv)
+serve(int argc, char **argv, struct imsgbuf *ibuf)
 {
 	char	path[PATH_MAX];
 	int	i, p[2];
@@ -380,19 +368,18 @@ serve(int argc, char **argv)
 			fatal("fork: %s", strerror(errno));
 		case 0:		/* child */
 			close(p[0]);
-			exfd = p[1];
+			imsg_init(&exibuf, p[1]);
 			setproctitle("server");
-			_exit(listener_main());
+			_exit(listener_main(&exibuf));
 		default:
-			servpipes[i] = p[0];
 			close(p[1]);
+			imsg_init(&servibuf[i], p[0]);
 		}
 	}
 
 	setproctitle("executor");
 	drop_priv();
-	unblock_signals();
-	_exit(executor_main());
+	_exit(executor_main(ibuf));
 }
 
 int
@@ -480,12 +467,6 @@ main(int argc, char **argv)
 	signal(SIGPIPE, SIG_IGN);
 	signal(SIGCHLD, SIG_IGN);
 
-#ifdef SIGINFO
-	signal(SIGINFO, sig_handler);
-#endif
-	signal(SIGUSR2, sig_handler);
-	signal(SIGHUP, sig_handler);
-
 	if (!conf.foreground && !configless) {
 		if (daemon(1, 1) == -1)
 			err(1, "daemon");
@@ -501,25 +482,43 @@ main(int argc, char **argv)
 	if (conf.ipv6)
 		sock6 = make_socket(conf.port, AF_INET6);
 
-	if (configless)
-		return serve(argc, argv);
+	if (configless) {
+		serve(argc, argv, NULL);
+		imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0);
+		imsg_flush(&logibuf);
+		return 0;
+	}
 
 	/* wait a sighup and reload the daemon */
 	for (;;) {
-		block_signals();
+		struct imsgbuf exibuf;
+		int p[2];
 
-		hupped = 0;
+		if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC,
+		    PF_UNSPEC, p) == -1)
+			fatal("socketpair: %s", strerror(errno));
+
 		switch (fork()) {
 		case -1:
 			fatal("fork: %s", strerror(errno));
 		case 0:
-			return serve(argc, argv);
+			signal(SIGHUP, SIG_IGN);
+			close(p[0]);
+			imsg_init(&exibuf, p[1]);
+			_exit(serve(argc, argv, &exibuf));
 		}
 
+		close(p[1]);
+		imsg_init(&exibuf, p[0]);
+
 		wait_sighup();
-		unblock_signals();
 		log_info(NULL, "reloading configuration %s", config_path);
 
+		/* close the executor (it'll close the servers too) */
+		imsg_compose(&exibuf, IMSG_QUIT, 0, 0, -1, NULL, 0);
+		imsg_flush(&exibuf);
+		close(p[0]);
+
 		old_ipv6 = conf.ipv6;
 		old_port = conf.port;
 
blob - b858ebc97cbf83139224a9c5d91c53ade5b33194
blob + dad7b4c1ca4fb918b5d8b9aa1e1dd13ab9ce2166
--- gmid.h
+++ gmid.h
@@ -26,6 +26,8 @@
 #include <netinet/in.h>
 
 #include <dirent.h>
+#include <limits.h>
+#include <netdb.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -116,12 +118,9 @@ struct conf {
 
 extern const char *config_path;
 extern struct conf conf;
-extern int exfd, logfd;
 
-extern struct imsgbuf logpibuf, logcibuf;
+extern struct imsgbuf logibuf, exibuf, servibuf[PROC_MAX];
 
-extern volatile sig_atomic_t hupped;
-
 extern int servpipes[PROC_MAX];
 
 struct iri {
@@ -142,6 +141,8 @@ struct parser {
 
 struct client;
 
+typedef void (imsg_handlerfn)(struct imsgbuf*, struct imsg*, size_t);
+
 typedef void (*statefn)(int, short, void*);
 
 /*
@@ -169,6 +170,7 @@ typedef void (*statefn)(int, short, void*);
  * send_file -> close_conn
  */
 struct client {
+	int		 id;
 	struct tls	*ctx;
 	char		 req[GEMINI_URL_LEN];
 	struct iri	 iri;
@@ -181,9 +183,35 @@ struct client {
 	char		 sbuf[1024];
 	ssize_t		 len, off;
 	struct sockaddr_storage	 addr;
-	struct vhost	*host;	/* host she's talking to */
+	struct vhost	*host;	/* host they're talking to */
 };
 
+struct cgireq {
+	char		buf[GEMINI_URL_LEN];
+
+	size_t		iri_schema_off;
+	size_t		iri_host_off;
+	size_t		iri_port_off;
+	size_t		iri_path_off;
+	size_t		iri_query_off;
+	size_t		iri_fragment_off;
+	int		iri_portno;
+
+	char		spath[PATH_MAX+1];
+	char		relpath[PATH_MAX+1];
+	char		addr[NI_MAXHOST+1];
+
+	/* AFAIK there isn't an upper limit for these two fields. */
+	char		subject[64+1];
+	char		issuer[64+1];
+
+	char		hash[128+1];
+	time_t		notbefore;
+	time_t		notafter;
+
+	size_t		host_off;
+};
+
 enum {
 	FILE_EXISTS,
 	FILE_EXECUTABLE,
@@ -191,8 +219,14 @@ enum {
 	FILE_MISSING,
 };
 
+enum imsg_type {
+	IMSG_CGI_REQ,
+	IMSG_CGI_RES,
+	IMSG_LOG,
+	IMSG_QUIT,
+};
+
 /* gmid.c */
-void		 sig_handler(int);
 void		 mkdirs(const char*);
 char		*data_dir(void);
 void		 load_local_cert(const char*, const char*);
@@ -243,7 +277,7 @@ int		 vhost_strip(struct vhost*, const char*);
 X509_STORE	*vhost_require_ca(struct vhost*, const char*);
 int		 vhost_disable_log(struct vhost*, const char*);
 void		 mark_nonblock(int);
-void		 loop(struct tls*, int, int);
+void		 loop(struct tls*, int, int, struct imsgbuf*);
 
 /* ex.c */
 int		 send_string(int, const char*);
@@ -257,7 +291,7 @@ int		 send_time(int, time_t);
 int		 recv_time(int, time_t*);
 int		 send_fd(int, int);
 int		 recv_fd(int);
-int		 executor_main(void);
+int		 executor_main(struct imsgbuf*);
 
 /* sandbox.c */
 void		 sandbox(void);
@@ -286,5 +320,6 @@ char		*xstrdup(const char*);
 void		 gen_certificate(const char*, const char*, const char*);
 X509_STORE	*load_ca(const char*);
 int		 validate_against_ca(X509_STORE*, const uint8_t*, size_t);
+void		 dispatch_imsg(struct imsgbuf*, imsg_handlerfn**, size_t);
 
 #endif
blob - 767d8239ce82b7a0ae18c80fe6b562d7896afb5d
blob + b66aa19f73bdd79ae9869351aa60a9afc403b807
--- log.c
+++ log.c
@@ -28,10 +28,17 @@
 #include <string.h>
 #include <syslog.h>
 
-static struct event inlog;
+static struct event imsgev;
 
-static void handle_log(int, short, void*);
+static void	handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
+static void	handle_imsg_log(struct imsgbuf*, struct imsg*, size_t);
+static void	handle_dispatch_imsg(int, short, void*);
 
+static imsg_handlerfn *handlers[] = {
+	[IMSG_QUIT] = handle_imsg_quit,
+	[IMSG_LOG] = handle_imsg_log,
+};
+
 void
 fatal(const char *fmt, ...)
 {
@@ -71,9 +78,9 @@ should_log(int priority)
 static inline void
 send_log(const char *msg, size_t len)
 {
-	imsg_compose(&logpibuf, 0, 0, 0, -1, msg, len);
+	imsg_compose(&logibuf, IMSG_LOG, 0, 0, -1, msg, len);
 	/* XXX: use event_once() */
-	imsg_flush(&logpibuf);
+	imsg_flush(&logibuf);
 }
 
 static inline void
@@ -229,39 +236,30 @@ log_request(struct client *c, char *meta, size_t l)
 
 
 static void
-handle_log(int fd, short ev, void *d)
+handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
 {
-	struct imsgbuf	*ibuf = d;
-	struct imsg	 imsg;
-	ssize_t		 n, datalen;
-	char		*msg;
+	event_loopbreak();
+}
 
-	if ((n = imsg_read(ibuf)) == -1) {
-		if (errno == EAGAIN || errno == EWOULDBLOCK)
-			return;
-                err(1, "imsg_read");
-	}
-	if (n == 0)
-		errx(1, "connection lost?");
+static void
+handle_imsg_log(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
+{
+	char *msg;
 
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			err(1, "read error");
-		if (n == 0)
-			return;
+	msg = imsg->data;
+	msg[datalen-1] = '\0';
 
-		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
-		msg = imsg.data;
-		msg[datalen-1] = '\0';
+	if (conf.foreground)
+		fprintf(stderr, "%s\n", msg);
+	else
+		syslog(LOG_DAEMON, "%s", msg);
+}
 
-		/* ignore imsg.hdr.type for now */
-		if (conf.foreground)
-			fprintf(stderr, "%s\n", msg);
-		else
-			syslog(LOG_DAEMON, "%s", msg);
-
-		imsg_free(&imsg);
-	}
+static void
+handle_dispatch_imsg(int fd, short ev, void *d)
+{
+	struct imsgbuf *ibuf = d;
+	dispatch_imsg(ibuf, handlers, sizeof(handlers));
 }
 
 int
@@ -269,8 +267,8 @@ logger_main(int fd, struct imsgbuf *ibuf)
 {
 	event_init();
 
-	event_set(&inlog, fd, EV_READ | EV_PERSIST, &handle_log, ibuf);
-	event_add(&inlog, NULL);
+	event_set(&imsgev, fd, EV_READ | EV_PERSIST, &handle_dispatch_imsg, ibuf);
+	event_add(&imsgev, NULL);
 
 #ifdef __OpenBSD__
 	if (pledge("stdio", NULL) == -1)
blob - 1d6926171d8453157ba58864e3731f0e1898ed55
blob + 2397e9afb105a1803d6c94fc46c2083187258a48
--- regress/puny-test.c
+++ regress/puny-test.c
@@ -21,7 +21,7 @@
 
 /* to make the linker happy */
 struct conf conf;
-struct imsgbuf logpibuf, logcibuf;
+struct imsgbuf logibuf, servibuf[PROC_MAX];
 
 struct suite {
 	const char	*src;
blob - bfb9dce996ce435f18a9201132997a8d6915317b
blob + b059412a1bd3b72a09d57b87388b68d9a319bb3d
--- server.c
+++ server.c
@@ -18,8 +18,6 @@
 
 #include <sys/stat.h>
 
-#include <netdb.h>
-
 #include <assert.h>
 #include <errno.h>
 #include <event.h>
@@ -28,12 +26,10 @@
 #include <limits.h>
 #include <string.h>
 
-struct server {
-	struct client	 clients[MAX_USERS];
-	struct tls	*ctx;
-};
+static struct client	 clients[MAX_USERS];
+static struct tls	*ctx;
 
-static struct event e4, e6, sighup, siginfo, sigusr2;
+static struct event e4, e6, imsgev, siginfo, sigusr2;
 static int has_ipv6, has_siginfo;
 
 int connected_clients;
@@ -65,7 +61,15 @@ static void	 handle_cgi_reply(int, short, void*);
 static void	 handle_copy(int, short, void*);
 static void	 close_conn(int, short, void*);
 static void	 do_accept(int, short, void*);
-static void	 handle_sighup(int, short, void*);
+struct client	*client_by_id(int);
+static void	 handle_imsg_cgi_res(struct imsgbuf*, struct imsg*, size_t);
+static void	 handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
+static void	 handle_siginfo(int, short, void*);
+
+static imsg_handlerfn *handlers[] = {
+	[IMSG_QUIT] = handle_imsg_quit,
+	[IMSG_CGI_RES] = handle_imsg_cgi_res,
+};
 
 static inline int
 matches(const char *pattern, const char *path)
@@ -633,6 +637,8 @@ static void
 start_cgi(const char *spath, const char *relpath, struct client *c)
 {
 	char addr[NI_MAXHOST];
+	const char *t;
+	struct cgireq req;
 	int e;
 
 	e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr),
@@ -640,32 +646,41 @@ start_cgi(const char *spath, const char *relpath, stru
 	    NULL, 0,
 	    NI_NUMERICHOST);
 	if (e != 0)
-		goto err;
+		fatal("getnameinfo failed");
 
-	if (!send_iri(exfd, &c->iri)
-	    || !send_string(exfd, spath)
-	    || !send_string(exfd, relpath)
-	    || !send_string(exfd, addr)
-	    || !send_string(exfd, tls_peer_cert_subject(c->ctx))
-	    || !send_string(exfd, tls_peer_cert_issuer(c->ctx))
-	    || !send_string(exfd, tls_peer_cert_hash(c->ctx))
-	    || !send_time(exfd, tls_peer_cert_notbefore(c->ctx))
-	    || !send_time(exfd, tls_peer_cert_notafter(c->ctx))
-	    || !send_vhost(exfd, c->host))
-		goto err;
+	memset(&req, 0, sizeof(req));
 
-	close(c->pfd);
-	if ((c->pfd = recv_fd(exfd)) == -1) {
-		start_reply(c, TEMP_FAILURE, "internal server error");
-		return;
-	}
+	memcpy(req.buf, c->req, sizeof(req.buf));
+
+	req.iri_schema_off = c->iri.schema - c->req;
+	req.iri_host_off = c->iri.host - c->req;
+	req.iri_port_off = c->iri.port - c->req;
+	req.iri_path_off = c->iri.path - c->req;
+	req.iri_query_off = c->iri.query - c->req;
+	req.iri_fragment_off = c->iri.fragment - c->req;
 
-	reschedule_read(c->pfd, c, &handle_cgi_reply);
-	return;
+	req.iri_portno = c->iri.port_no;
 
-err:
-	/* fatal("cannot talk to the executor process: %s", strerror(errno)); */
-	fatal("cannot talk to the executor process");
+	strlcpy(req.spath, spath, sizeof(req.spath));
+	strlcpy(req.relpath, relpath, sizeof(req.relpath));
+	strlcpy(req.addr, addr, sizeof(req.addr));
+
+	if ((t = tls_peer_cert_subject(c->ctx)) != NULL)
+		strlcpy(req.subject, t, sizeof(req.subject));
+	if ((t = tls_peer_cert_issuer(c->ctx)) != NULL)
+		strlcpy(req.issuer, t, sizeof(req.issuer));
+	if ((t = tls_peer_cert_hash(c->ctx)) != NULL)
+		strlcpy(req.hash, t, sizeof(req.hash));
+
+	req.notbefore = tls_peer_cert_notbefore(c->ctx);
+	req.notafter = tls_peer_cert_notafter(c->ctx);
+
+	req.host_off = c->host - hosts;
+
+	imsg_compose(&exibuf, IMSG_CGI_REQ, c->id, 0, -1, &req, sizeof(req));
+	imsg_flush(&exibuf);
+
+	close(c->pfd);
 }
 
 static void
@@ -986,7 +1001,6 @@ static void
 do_accept(int sock, short et, void *d)
 {
 	struct client *c;
-	struct server *s = d;
 	struct sockaddr_storage addr;
 	struct sockaddr *saddr;
 	socklen_t len;
@@ -994,7 +1008,6 @@ do_accept(int sock, short et, void *d)
 
 	(void)et;
 
-
 	saddr = (struct sockaddr*)&addr;
 	len = sizeof(addr);
 	if ((fd = accept(sock, saddr, &len)) == -1) {
@@ -1006,10 +1019,11 @@ do_accept(int sock, short et, void *d)
 	mark_nonblock(fd);
 
 	for (i = 0; i < MAX_USERS; ++i) {
-		c = &s->clients[i];
+		c = &clients[i];
 		if (c->fd == -1) {
 			memset(c, 0, sizeof(*c));
-			if (tls_accept_socket(s->ctx, &c->ctx, fd) == -1)
+			c->id = i;
+			if (tls_accept_socket(ctx, &c->ctx, fd) == -1)
 				break; /* goodbye fd! */
 
 			c->fd = fd;
@@ -1026,22 +1040,53 @@ do_accept(int sock, short et, void *d)
 	close(fd);
 }
 
+struct client *
+client_by_id(int id)
+{
+	if ((size_t)id > sizeof(clients)/sizeof(clients[0]))
+		fatal("in client_by_id: invalid id %d", id);
+	return &clients[id];
+}
+
 static void
-handle_sighup(int fd, short ev, void *d)
+handle_imsg_cgi_res(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
 {
-	(void)fd;
-	(void)ev;
+	struct client *c;
 
+	c = client_by_id(imsg->hdr.peerid);
+
+	if ((c->pfd = imsg->fd) == -1)
+		start_reply(c, TEMP_FAILURE, "internal server error");
+	else
+		reschedule_read(c->pfd, c, &handle_cgi_reply);
+}
+
+static void
+handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
+{
+	(void)imsg;
+	(void)len;
+
+	/* don't call event_loopbreak since we want to finish to
+	 * handle the ongoing connections. */
+
 	event_del(&e4);
 	if (has_ipv6)
 		event_del(&e6);
 	if (has_siginfo)
 		signal_del(&siginfo);
+	event_del(&imsgev);
 	signal_del(&sigusr2);
-	signal_del(&sighup);
 }
 
 static void
+handle_dispatch_imsg(int fd, short ev, void *d)
+{
+	struct imsgbuf *ibuf = d;
+	dispatch_imsg(ibuf, handlers, sizeof(handlers));
+}
+
+static void
 handle_siginfo(int fd, short ev, void *d)
 {
 	(void)fd;
@@ -1052,28 +1097,29 @@ handle_siginfo(int fd, short ev, void *d)
 }
 
 void
-loop(struct tls *ctx, int sock4, int sock6)
+loop(struct tls *ctx_, int sock4, int sock6, struct imsgbuf *ibuf)
 {
-	struct server server;
 	size_t i;
 
+	ctx = ctx_;
+
 	event_init();
 
-	memset(&server, 0, sizeof(server));
+	memset(&clients, 0, sizeof(clients));
 	for (i = 0; i < MAX_USERS; ++i)
-		server.clients[i].fd = -1;
+		clients[i].fd = -1;
 
-	event_set(&e4, sock4, EV_READ | EV_PERSIST, &do_accept, &server);
+	event_set(&e4, sock4, EV_READ | EV_PERSIST, &do_accept, NULL);
 	event_add(&e4, NULL);
 
 	if (sock6 != -1) {
 		has_ipv6 = 1;
-		event_set(&e6, sock6, EV_READ | EV_PERSIST, &do_accept, &server);
+		event_set(&e6, sock6, EV_READ | EV_PERSIST, &do_accept, NULL);
 		event_add(&e6, NULL);
 	}
 
-	signal_set(&sighup, SIGHUP, &handle_sighup, NULL);
-	signal_add(&sighup, NULL);
+	event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST, handle_dispatch_imsg, ibuf);
+	event_add(&imsgev, NULL);
 
 #ifdef SIGINFO
 	has_siginfo = 1;
@@ -1083,8 +1129,6 @@ loop(struct tls *ctx, int sock4, int sock6)
 	signal_set(&sigusr2, SIGUSR2, &handle_siginfo, NULL);
 	signal_add(&sigusr2, NULL);
 
-	server.ctx = ctx;
-
 	sandbox();
 	event_dispatch();
 	_exit(0);
blob - 50f57a1aafb4e0fa06c331df93823b846a457bf6
blob + 5d831f18895f38d4cdd94cd7c66d36aeb8bb8a02
--- utils.c
+++ utils.c
@@ -24,24 +24,6 @@
 #include <openssl/x509_vfy.h>
 #include <openssl/x509v3.h>
 
-static sigset_t set;
-
-void
-block_signals(void)
-{
-	sigset_t new;
-
-	sigemptyset(&new);
-	sigaddset(&new, SIGHUP);
-	sigprocmask(SIG_BLOCK, &new, &set);
-}
-
-void
-unblock_signals(void)
-{
-	sigprocmask(SIG_SETMASK, &set, NULL);
-}
-
 int
 starts_with(const char *str, const char *prefix)
 {
@@ -247,4 +229,34 @@ end:
 	if (ctx != NULL)
 		X509_STORE_CTX_free(ctx);
 	return ret;
+}
+
+void
+dispatch_imsg(struct imsgbuf *ibuf, imsg_handlerfn **handlers, size_t size)
+{
+	struct imsg	imsg;
+	size_t		datalen, i;
+	ssize_t		n;
+
+	if ((n = imsg_read(ibuf)) == -1) {
+		if (errno == EAGAIN || errno == EWOULDBLOCK)
+			return;
+		_exit(1);
+	}
+
+	if (n == 0)
+		_exit(1);
+
+	for (;;) {
+		if ((n = imsg_get(ibuf, &imsg)) == -1)
+			_exit(1);
+		if (n == 0)
+			return;
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+		i = imsg.hdr.type;
+		if (i > (size / sizeof(imsg_handlerfn*)) || handlers[i] == NULL)
+			abort();
+		handlers[i](ibuf, &imsg, datalen);
+		imsg_free(&imsg);
+	}
 }