Commit Diff


commit - 5df699d1ab09618573bc4d076dfb3c4b3a78b4b4
commit + d29a2ee2246e1b1b0c5222a823820e42422c894e
blob - 26e7b0c2c0865907b69ee44a5738f211e4c51c1d
blob + 60de30f62c00339378a1517d80cd9c6c074a21c3
--- Makefile
+++ Makefile
@@ -63,7 +63,6 @@ COMPATS =	compat/err.c \
 		compat/vasprintf.c
 
 GMID_SRCS =	dirs.c \
-		ex.c \
 		fcgi.c \
 		gmid.c \
 		iri.c \
blob - 4263391d9e1ca83b566aa11068c21717a4ee2b0d
blob + ca18f859a33d1a52000dfa997cc840be6c1e5e29
--- README.md
+++ README.md
@@ -16,7 +16,7 @@ featureful server.
  - IRI support (RFC3987)
  - automatic certificate generation for config-less mode
  - reverse proxying
- - CGI and FastCGI support
+ - FastCGI support
  - virtual hosts
  - location rules
  - event-based asynchronous I/O model
@@ -75,9 +75,6 @@ server "example.com" {
 	# lang for text/gemini files
 	lang "en"
 
-	# execute CGI scripts in /cgi/
-	cgi "/cgi/*"
-
 	# only for locations that matches /files/*
 	location "/files/*" {
 		# generate directory listings
@@ -141,6 +138,9 @@ to the `contrib` directory.
 
 ## Architecture/Security considerations
 
+**outdated: revisit for gmid 2.0**
+
+
 gmid is composed by four processes: the parent process, the logger,
 the listener and the executor.  The parent process is the only one
 that doesn't drop privileges, but all it does is to wait for a SIGHUP
blob - fbda0b5850083353a621bd68d3caaf3f8f0bf5c2 (mode 644)
blob + /dev/null
--- ex.c
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "gmid.h"
-
-#include <sys/un.h>
-
-#include <err.h>
-#include <errno.h>
-
-#include <event.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <string.h>
-
-static void	handle_imsg_cgi_req(struct imsgbuf*, struct imsg*, size_t);
-static void	handle_imsg_fcgi_req(struct imsgbuf*, struct imsg*, size_t);
-static void	handle_imsg_conn_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*);
-
-static imsg_handlerfn *handlers[] = {
-	[IMSG_FCGI_REQ] = handle_imsg_fcgi_req,
-	[IMSG_CGI_REQ] = handle_imsg_cgi_req,
-	[IMSG_CONN_REQ] = handle_imsg_conn_req,
-	[IMSG_QUIT] = handle_imsg_quit,
-};
-
-static inline void
-safe_setenv(const char *name, const char *val)
-{
-	if (val == NULL)
-		val = "";
-	setenv(name, val, 1);
-}
-
-static char *
-xasprintf(const char *fmt, ...)
-{
-	va_list ap;
-	char *s;
-
-	va_start(ap, fmt);
-	if (vasprintf(&s, fmt, ap) == -1)
-		s = NULL;
-	va_end(ap);
-
-	return s;
-}
-
-static void
-do_exec(const char *ex, const char *spath, char *query)
-{
-	char **argv, buf[PATH_MAX], *sname, *t;
-	size_t i, n;
-
-	/* restore the default handlers */
-	signal(SIGPIPE, SIG_DFL);
-	signal(SIGCHLD, SIG_DFL);
-	signal(SIGHUP,  SIG_DFL);
-	signal(SIGINT,  SIG_DFL);
-	signal(SIGTERM, SIG_DFL);
-
-	strlcpy(buf, spath, sizeof(buf));
-	sname = basename(buf);
-
-	if (query == NULL || strchr(query, '=') != NULL) {
-		if ((argv = calloc(2, sizeof(char*))) == NULL)
-			err(1, "calloc");
-		argv[0] = sname;
-		execvp(ex, argv);
-		warn("execvp: %s", argv[0]);
-		return;
-	}
-
-	n = 1;
-	for (t = query ;; t++, n++) {
-		if ((t = strchr(t, '+')) == NULL)
-			break;
-	}
-
-	if ((argv = calloc(n+2, sizeof(char*))) == NULL)
-		err(1, "calloc");
-
-	argv[0] = sname;
-	for (i = 0; i < n; ++i) {
-		t = strchr(query, '+');
-		if (t != NULL)
-			*t = '\0';
-		argv[i+1] = pct_decode_str(query);
-		query = t+1;
-	}
-
-	execvp(ex, argv);
-	warn("execvp: %s", argv[0]);
-}
-
-static inline void
-setenv_time(const char *var, time_t t)
-{
-	char timebuf[21];
-	struct tm tminfo;
-
-	if (t == -1)
-		return;
-
-	strftime(timebuf, sizeof(timebuf), "%FT%TZ",
-	    gmtime_r(&t, &tminfo));
-	setenv(var, timebuf, 1);
-}
-
-/* fd or -1 on error */
-static int
-launch_cgi(struct iri *iri, struct cgireq *req, struct vhost *vhost,
-    struct location *loc)
-{
-	int p[2], errp[2];	/* read end, write end */
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
-		return -1;
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, errp) == -1)
-		return -1;
-
-	switch (fork()) {
-	case -1:
-		log_err(NULL, "fork failed: %s", strerror(errno));
-		close(p[0]);
-		close(p[1]);
-		close(errp[0]);
-		close(errp[1]);
-		return -1;
-
-	case 0: {		/* child */
-		char *ex, *pwd;
-		char iribuf[GEMINI_URL_LEN];
-		char path[PATH_MAX];
-		struct envlist *e;
-
-		close(p[0]);
-		if (dup2(p[1], 1) == -1)
-			goto childerr;
-
-		close(errp[0]);
-		if (dup2(errp[1], 2) == -1)
-			goto childerr;
-
-		ex = xasprintf("%s/%s", loc->dir, req->spath);
-
-		serialize_iri(iri, iribuf, sizeof(iribuf));
-
-		safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
-		safe_setenv("GEMINI_DOCUMENT_ROOT", loc->dir);
-		safe_setenv("GEMINI_SCRIPT_FILENAME",
-		    xasprintf("%s/%s", loc->dir, req->spath));
-		safe_setenv("GEMINI_URL", iribuf);
-
-		strlcpy(path, "/", sizeof(path));
-		strlcat(path, req->spath, sizeof(path));
-		safe_setenv("GEMINI_URL_PATH", path);
-
-		if (*req->relpath != '\0') {
-			strlcpy(path, "/", sizeof(path));
-			strlcat(path, req->relpath, sizeof(path));
-			safe_setenv("PATH_INFO", path);
-
-			strlcpy(path, loc->dir, sizeof(path));
-			strlcat(path, "/", sizeof(path));
-			strlcat(path, req->relpath, sizeof(path));
-			safe_setenv("PATH_TRANSLATED", path);
-		}
-
-		safe_setenv("QUERY_STRING", iri->query);
-		safe_setenv("REMOTE_ADDR", req->addr);
-		safe_setenv("REMOTE_HOST", req->addr);
-		safe_setenv("REQUEST_METHOD", "");
-
-		strlcpy(path, "/", sizeof(path));
-		strlcat(path, req->spath, sizeof(path));
-		safe_setenv("SCRIPT_NAME", path);
-
-		safe_setenv("SERVER_NAME", iri->host);
-
-		snprintf(path, sizeof(path), "%d", conf.port);
-		safe_setenv("SERVER_PORT", path);
-
-		safe_setenv("SERVER_PROTOCOL", "GEMINI");
-		safe_setenv("SERVER_SOFTWARE", GMID_VERSION);
-
-		if (*req->subject != '\0')
-			safe_setenv("AUTH_TYPE", "Certificate");
-		else
-			safe_setenv("AUTH_TYPE", "");
-
-		safe_setenv("REMOTE_USER", req->subject);
-		safe_setenv("TLS_CLIENT_ISSUER", req->issuer);
-		safe_setenv("TLS_CLIENT_HASH", req->hash);
-		safe_setenv("TLS_VERSION", req->version);
-		safe_setenv("TLS_CIPHER", req->cipher);
-
-		snprintf(path, sizeof(path), "%d", req->cipher_strength);
-		safe_setenv("TLS_CIPHER_STRENGTH", path);
-
-		setenv_time("TLS_CLIENT_NOT_AFTER", req->notafter);
-		setenv_time("TLS_CLIENT_NOT_BEFORE", req->notbefore);
-
-		TAILQ_FOREACH(e, &vhost->env, envs) {
-			safe_setenv(e->name, e->value);
-		}
-
-		strlcpy(path, ex, sizeof(path));
-
-		pwd = dirname(path);
-		if (chdir(pwd)) {
-			warn("chdir");
-			goto childerr;
-		}
-
-		do_exec(ex, req->spath, iri->query);
-		goto childerr;
-	}
-
-	default:
-		close(p[1]);
-		close(errp[0]);
-		close(errp[1]);
-		mark_nonblock(p[0]);
-		return p[0];
-	}
-
-childerr:
-	dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE);
-	_exit(1);
-}
-
-static struct vhost *
-host_nth(size_t n)
-{
-	struct vhost *h;
-
-	TAILQ_FOREACH(h, &hosts, vhosts) {
-		if (n == 0)
-			return h;
-		n--;
-	}
-
-	return NULL;
-}
-
-static struct location *
-loc_nth(struct vhost *vhost, size_t n)
-{
-	struct location *loc;
-
-	TAILQ_FOREACH(loc, &vhost->locations, locations) {
-		if (n == 0)
-			return loc;
-		n--;
-	}
-
-	return NULL;
-}
-
-static void
-handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
-{
-	struct vhost	*h;
-	struct location	*l;
-	struct cgireq	 req;
-	struct iri	 iri;
-	int		 fd;
-
-	if (datalen != sizeof(req))
-		abort();
-
-	memcpy(&req, imsg->data, sizeof(req));
-
-	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 ((h = host_nth(req.host_off)) == NULL)
-		abort();
-
-	if ((l = loc_nth(h, req.loc_off)) == NULL)
-		abort();
-
-	fd = launch_cgi(&iri, &req, h, l);
-	imsg_compose(ibuf, IMSG_CGI_RES, imsg->hdr.peerid, 0, fd, NULL, 0);
-	imsg_flush(ibuf);
-}
-
-static int
-fcgi_open_prog(struct fcgi *f)
-{
-	int s[2];
-	pid_t p;
-
-	/* XXX! */
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1)
-		err(1, "socketpair");
-
-	switch (p = fork()) {
-	case -1:
-		err(1, "fork");
-	case 0:
-		close(s[0]);
-		if (dup2(s[1], 0) == -1)
-			err(1, "dup2");
-		execl(f->prog, f->prog, NULL);
-		err(1, "execl %s", f->prog);
-	default:
-		close(s[1]);
-		return s[0];
-	}
-}
-
-static int
-fcgi_open_sock(struct fcgi *f)
-{
-	struct sockaddr_un	addr;
-	int			fd;
-
-	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
-		log_err(NULL, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sun_family = AF_UNIX;
-	strlcpy(addr.sun_path, f->path, sizeof(addr.sun_path));
-
-	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
-		log_warn(NULL, "failed to connect to %s: %s", f->path,
-		    strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	return fd;
-}
-
-static int
-fcgi_open_conn(struct fcgi *f)
-{
-	struct addrinfo	hints, *servinfo, *p;
-	int		r, sock;
-
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_flags = AI_ADDRCONFIG;
-
-	if ((r = getaddrinfo(f->path, f->port, &hints, &servinfo)) != 0) {
-		log_warn(NULL, "getaddrinfo %s:%s: %s", f->path, f->port,
-		    gai_strerror(r));
-		return -1;
-	}
-
-	for (p = servinfo; p != NULL; p = p->ai_next) {
-		sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
-		if (sock == -1)
-			continue;
-		if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
-			close(sock);
-			continue;
-		}
-		break;
-	}
-
-	if (p == NULL) {
-		log_warn(NULL, "couldn't connect to %s:%s", f->path, f->port);
-		sock = -1;
-	}
-
-	freeaddrinfo(servinfo);
-	return sock;
-}
-
-static void
-handle_imsg_fcgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
-{
-	struct fcgi	*f;
-	int		 id, fd;
-
-	if (datalen != sizeof(id))
-		abort();
-	memcpy(&id, imsg->data, sizeof(id));
-
-	if (id > FCGI_MAX || (fcgi[id].path == NULL && fcgi[id].prog == NULL))
-		abort();
-
-	f = &fcgi[id];
-	if (f->prog != NULL)
-		fd = fcgi_open_prog(f);
-	else if (f->port != NULL)
-		fd = fcgi_open_conn(f);
-	else
-		fd = fcgi_open_sock(f);
-
-	imsg_compose(ibuf, IMSG_FCGI_FD, imsg->hdr.peerid, 0, fd, NULL, 0);
-	imsg_flush(ibuf);
-}
-
-static void
-handle_imsg_conn_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
-{
-	struct addrinfo hints, *res, *res0;
-	struct connreq req;
-	int r, sock;
-
-	if (datalen != sizeof(req))
-		abort();
-	memcpy(&req, imsg->data, sizeof(req));
-	req.flag = 0;
-
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-
-	/* XXX: do this asynchronously if possible */
-	r = getaddrinfo(req.host, req.port, &hints, &res0);
-	if (r != 0) {
-		log_warn(NULL, "getaddrinfo(\"%s\", \"%s\"): %s",
-		    req.host, req.port, gai_strerror(r));
-		goto err;
-	}
-
-	for (res = res0; res; res = res->ai_next) {
-		sock = socket(res->ai_family, res->ai_socktype,
-		    res->ai_protocol);
-		if (sock == -1)
-			continue;
-
-		if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
-			close(sock);
-			sock = -1;
-			continue;
-		}
-
-		break;
-	}
-
-	freeaddrinfo(res0);
-
-	if (sock == -1) {
-		log_warn(NULL, "can't connect to %s:%s", req.host,
-		    req.port);
-		goto err;
-	}
-
-	imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, sock, NULL, 0);
-	imsg_flush(ibuf);
-	return;
-
-err:
-	imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, -1, NULL, 0);
-	imsg_flush(ibuf);
-}
-
-static void
-handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
-{
-	int i;
-
-	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);
-	}
-
-	event_loopbreak();
-}
-
-static void
-handle_dispatch_imsg(int fd, short ev, void *d)
-{
-	struct imsgbuf *ibuf = d;
-	dispatch_imsg(ibuf, handlers, sizeof(handlers));
-}
-
-int
-executor_main(struct imsgbuf *ibuf)
-{
-	struct event	 evs[PROC_MAX], imsgev;
-	int		 i;
-
-	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], servibuf[i].fd, EV_READ | EV_PERSIST,
-		    handle_dispatch_imsg, &servibuf[i]);
-		event_add(&evs[i], NULL);
-	}
-
-	sandbox_executor_process();
-
-	event_dispatch();
-
-	return 1;
-}
blob - 1c0a3204c854514a4489ae68abb950384b3c9bff
blob + 3d5673417467d9978f619c0576c7ce2e0a45ec1d
--- gmid.1
+++ gmid.1
@@ -31,13 +31,12 @@
 .Op Fl d Ar certs-dir
 .Op Fl H Ar hostname
 .Op Fl p Ar port
-.Op Fl x Ar cgi
 .Op Ar dir
 .Ek
 .Sh DESCRIPTION
 .Nm
 is a simple and minimal gemini server that can serve static files,
-execute CGI scripts and talk to FastCGI applications.
+talk to FastCGI applications and act as a gemini reverse proxy.
 It can run without a configuration file with a limited set of features
 available.
 .Pp
@@ -118,18 +117,6 @@ Verbose mode.
 Multiple
 .Fl v
 options increase the verbosity.
-.It Fl x Ar path
-Enable execution of
-.Sx CGI
-scripts.
-See the description of the
-.Ic cgi
-option in the
-.Sq Servers
-section below to learn how
-.Ar path
-is processed.
-Cannot be provided more than once.
 .It Ar dir
 The root directory to serve.
 By default the current working directory is assumed.
@@ -167,21 +154,6 @@ Serve the current directory
 $ gmid .
 .Ed
 .Pp
-To serve the directory
-.Pa docs
-and enable CGI scripts inside
-.Pa docs/cgi
-.Bd -literal -offset indent
-$ mkdir docs/cgi
-$ cat <<EOF > docs/cgi/hello
-#!/bin/sh
-printf "20 text/plain\er\en"
-echo "hello world"
-EOF
-$ chmod +x docs/cgi/hello
-$ gmid -x '/cgi/*' docs
-.Ed
-.Pp
 To run
 .Nm
 as a deamon a configuration file and a X.509 certificate must be provided.
blob - 3f2901f9d05e8a616c3d08ac173755913dfcb66d
blob + 63f49eeeff6100607e3ade61b97175547fba128a
--- gmid.c
+++ gmid.c
@@ -43,7 +43,7 @@ int sock4, sock6;
 
 struct imsgbuf logibuf, exibuf, servibuf[PROC_MAX];
 
-const char *config_path, *certs_dir, *hostname, *pidfile, *cgi;
+const char *config_path, *certs_dir, *hostname, *pidfile;
 
 struct conf conf;
 
@@ -103,10 +103,9 @@ data_dir(void)
 }
 
 void
-load_local_cert(const char *hostname, const char *dir)
+load_local_cert(struct vhost *h, const char *hostname, const char *dir)
 {
 	char *cert, *key;
-	struct vhost *h;
 
 	if (asprintf(&cert, "%s/%s.cert.pem", dir, hostname) == -1)
 		errx(1, "asprintf");
@@ -116,7 +115,6 @@ load_local_cert(const char *hostname, const char *dir)
 	if (access(cert, R_OK) == -1 || access(key, R_OK) == -1)
 		gen_certificate(hostname, cert, key);
 
-	h = TAILQ_FIRST(&hosts);
 	h->cert = cert;
 	h->key = key;
 	h->domain = hostname;
@@ -351,7 +349,6 @@ free_config(void)
 		free((char*)h->cert);
 		free((char*)h->key);
 		free((char*)h->ocsp);
-		free((char*)h->cgi);
 		free((char*)h->entrypoint);
 
 		TAILQ_REMOVE(&hosts, h, vhosts);
@@ -423,7 +420,7 @@ usage(void)
 	fprintf(stderr,
 	    "Version: " GMID_STRING "\n"
 	    "Usage: %s [-fnv] [-c config] [-D macro=value] [-P pidfile]\n"
-	    "       %s [-6hVv] [-d certs-dir] [-H hostname] [-p port] [-x cgi] [dir]\n",
+	    "       %s [-6hVv] [-d certs-dir] [-H hostname] [-p port] [dir]\n",
 	    getprogname(),
 	    getprogname());
 }
@@ -453,43 +450,11 @@ logger_init(void)
 	}
 }
 
-static int
-serve(int argc, char **argv, struct imsgbuf *ibuf)
+static void
+serve(void)
 {
-	char		 path[PATH_MAX];
-	int		 i, p[2];
-	struct vhost	*h;
-	struct location	*l;
-
-	if (config_path == NULL) {
-		if (hostname == NULL)
-			hostname = "localhost";
-		if (certs_dir == NULL)
-			certs_dir = data_dir();
-		load_local_cert(hostname, certs_dir);
-
-		h = TAILQ_FIRST(&hosts);
-		h->domain = "*";
-
-		l = TAILQ_FIRST(&h->locations);
-		l->auto_index = 1;
-		l->match = "*";
-
-		switch (argc) {
-		case 0:
-			l->dir = getcwd(path, sizeof(path));
-			break;
-		case 1:
-			l->dir = absolutify_path(argv[0]);
-			break;
-		default:
-			usage();
-			return 1;
-		}
+	int i, p[2];
 
-		log_notice(NULL, "serving %s on port %d", l->dir, conf.port);
-	}
-
 	/* setup tls before dropping privileges: we don't want user
 	 * to put private certs inside the chroot. */
 	setup_tls();
@@ -512,10 +477,6 @@ serve(int argc, char **argv, struct imsgbuf *ibuf)
 			imsg_init(&servibuf[i], p[0]);
 		}
 	}
-
-	setproctitle("executor");
-	drop_priv();
-	_exit(executor_main(ibuf));
 }
 
 static int
@@ -547,23 +508,35 @@ write_pidfile(const char *pidfile)
 }
 
 static void
-setup_configless(int argc, char **argv, const char *cgi)
+setup_configless(const char *path)
 {
+	char p[PATH_MAX];
 	struct vhost	*host;
 	struct location	*loc;
 
+	if (hostname == NULL)
+		hostname = "localhost";
+	if (certs_dir == NULL)
+		certs_dir = data_dir();
+
 	host = xcalloc(1, sizeof(*host));
-	host->cgi = cgi;
 	TAILQ_INSERT_HEAD(&hosts, host, vhosts);
 
 	loc = xcalloc(1, sizeof(*loc));
 	loc->fcgi = -1;
 	TAILQ_INSERT_HEAD(&host->locations, loc, locations);
 
-	serve(argc, argv, NULL);
+	load_local_cert(host, hostname, certs_dir);
 
-	imsg_compose(&logibuf, IMSG_QUIT, 0, 0, -1, NULL, 0);
-	imsg_flush(&logibuf);
+	host->domain = "*";
+	loc->auto_index = 1;
+	loc->match = "*";
+	if (path == NULL)
+		loc->dir = getcwd(p, sizeof(p));
+	else
+		loc->dir = absolutify_path(path);
+
+	log_notice(NULL, "serving %s on port %d", loc->dir, conf.port);
 }
 
 static int
@@ -581,8 +554,7 @@ parse_portno(const char *p)
 int
 main(int argc, char **argv)
 {
-	struct imsgbuf exibuf;
-	int ch, conftest = 0, configless = 0;
+	int i, ch, conftest = 0, configless = 0;
 	int pidfd, old_ipv6, old_port;
 
 	logger_init();
@@ -644,14 +616,6 @@ main(int argc, char **argv)
 			conf.verbose++;
 			break;
 
-		case 'x':
-			/* drop the starting / (if any) */
-			if (*optarg == '/')
-				optarg++;
-			cgi = optarg;
-			configless = 1;
-			break;
-
 		default:
 			usage();
 			return 1;
@@ -667,6 +631,9 @@ main(int argc, char **argv)
 		conf.verbose++;
 	}
 
+	if (argc > 1 || (configless && argc != 0))
+		usage();
+
 	if (config_path != NULL && (argc > 0 || configless))
 		fatal("can't specify options in config mode.");
 
@@ -691,6 +658,8 @@ main(int argc, char **argv)
 
 	if (config_path != NULL)
 		parse_conf(config_path);
+	else
+		setup_configless(*argv);
 
 	sock4 = make_socket(conf.port, AF_INET);
 	sock6 = -1;
@@ -698,13 +667,7 @@ main(int argc, char **argv)
 		sock6 = make_socket(conf.port, AF_INET6);
 
 	signal(SIGPIPE, SIG_IGN);
-	signal(SIGCHLD, SIG_IGN);
 
-	if (configless) {
-		setup_configless(argc, argv, cgi);
-		return 0;
-	}
-
 	pidfd = write_pidfile(pidfile);
 
 	/*
@@ -718,33 +681,19 @@ main(int argc, char **argv)
 
 	/* wait a sighup and reload the daemon */
 	for (;;) {
-		int p[2];
+		serve();
 
-		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:
-			close(p[0]);
-			imsg_init(&exibuf, p[1]);
-			_exit(serve(argc, argv, &exibuf));
-		}
-
-		close(p[1]);
-		imsg_init(&exibuf, p[0]);
-
-		if (!wait_signal())
+		if (!wait_signal() || configless)
 			break;
 
 		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]);
+		/* close the servers */
+		for (i = 0; i < conf.prefork; ++i) {
+			imsg_compose(&servibuf[i], IMSG_QUIT, 0, 0, -1, NULL, 0);
+			imsg_flush(&servibuf[i]);
+			close(servibuf[i].fd);
+		}
 
 		old_ipv6 = conf.ipv6;
 		old_port = conf.port;
blob - a866109d8595001d91cc300ee49c947625588558
blob + c3ba0491522221278f4796f3d2a1c51c2827f002
--- gmid.h
+++ gmid.h
@@ -157,7 +157,6 @@ struct vhost {
 	const char	*cert;
 	const char	*key;
 	const char	*ocsp;
-	const char	*cgi;
 	const char	*entrypoint;
 
 	TAILQ_ENTRY(vhost) vhosts;
@@ -221,14 +220,12 @@ enum {
 	REQUEST_UNDECIDED,
 	REQUEST_FILE,
 	REQUEST_DIR,
-	REQUEST_CGI,
 	REQUEST_FCGI,
 	REQUEST_PROXY,
 	REQUEST_DONE,
 };
 
 #define IS_INTERNAL_REQUEST(x) \
-	((x) != REQUEST_CGI && \
 	 (x) != REQUEST_FCGI && \
 	 (x) != REQUEST_PROXY)
 
@@ -272,37 +269,7 @@ struct client {
 };
 SPLAY_HEAD(client_tree_id, client);
 extern struct client_tree_id clients;
-
-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];
-	char		version[8];
-	char		cipher[32];
-	int		cipher_strength;
-	time_t		notbefore;
-	time_t		notafter;
-
-	size_t		host_off;
-	size_t		loc_off;
-};
-
 struct connreq {
 	char	host[NI_MAXHOST];
 	char	port[NI_MAXSERV];
@@ -317,8 +284,6 @@ enum {
 };
 
 enum imsg_type {
-	IMSG_CGI_REQ,
-	IMSG_CGI_RES,
 	IMSG_FCGI_REQ,
 	IMSG_FCGI_FD,
 	IMSG_CONN_REQ,
@@ -331,7 +296,7 @@ enum imsg_type {
 
 /* gmid.c */
 char		*data_dir(void);
-void		 load_local_cert(const char*, const char*);
+void		 load_local_cert(struct vhost*, const char*, const char*);
 void		 load_vhosts(void);
 int		 make_socket(int, int);
 void		 setup_tls(void);
@@ -395,20 +360,6 @@ int		 scandir_fd(int, struct dirent***, int(*)(const s
 int		 select_non_dot(const struct dirent*);
 int		 select_non_dotdot(const struct dirent*);
 
-/* ex.c */
-int		 send_string(int, const char*);
-int		 recv_string(int, char**);
-int		 send_iri(int, struct iri*);
-int		 recv_iri(int, struct iri*);
-void		 free_recvd_iri(struct iri*);
-int		 send_vhost(int, struct vhost*);
-int		 recv_vhost(int, struct vhost**);
-int		 send_time(int, time_t);
-int		 recv_time(int, time_t*);
-int		 send_fd(int, int);
-int		 recv_fd(int);
-int		 executor_main(struct imsgbuf*);
-
 /* fcgi.c */
 void		 fcgi_read(struct bufferevent *, void *);
 void		 fcgi_write(struct bufferevent *, void *);
blob - ca3fb43936b83582fcdd3ca327cb1adc7a79d713
blob + 1eaf0c716b73bfcdc8e33fc6f8fc17a27d875d8c
--- parse.y
+++ parse.y
@@ -117,9 +117,8 @@ typedef struct {
 
 %token	ALIAS AUTO
 %token	BLOCK
-%token	CA CERT CGI CHROOT CLIENT
+%token	CA CERT CHROOT CLIENT
 %token	DEFAULT
-%token	ENTRYPOINT ENV
 %token	FASTCGI FOR_HOST
 %token	INCLUDE INDEX IPV6
 %token	KEY
@@ -281,22 +280,6 @@ servopt		: ALIAS string {
 		| CERT string		{
 			only_once(host->cert, "cert");
 			host->cert = ensure_absolute_path($2);
-		}
-		| CGI string		{
-			only_once(host->cgi, "cgi");
-			/* drop the starting '/', if any */
-			if (*$2 == '/')
-				memmove($2, $2+1, strlen($2));
-			host->cgi = $2;
-		}
-		| ENTRYPOINT string {
-			only_once(host->entrypoint, "entrypoint");
-			while (*$2 == '/')
-				memmove($2, $2+1, strlen($2));
-			host->entrypoint = $2;
-		}
-		| ENV string '=' string {
-			add_param($2, $4, 1);
 		}
 		| KEY string		{
 			only_once(host->key, "key");
@@ -522,12 +505,9 @@ static const struct keyword {
 	{"block", BLOCK},
 	{"ca", CA},
 	{"cert", CERT},
-	{"cgi", CGI},
 	{"chroot", CHROOT},
 	{"client", CLIENT},
 	{"default", DEFAULT},
-	{"entrypoint", ENTRYPOINT},
-	{"env", ENV},
 	{"fastcgi", FASTCGI},
 	{"for-host", FOR_HOST},
 	{"include", INCLUDE},
blob - 9bce95df5248d1807ca62bba7e190440ff2f0e26
blob + 29abf6809238a903d90730416404c6f77b7bbafa
--- regress/regress
+++ regress/regress
@@ -40,21 +40,16 @@ run_test test_custom_mime
 run_test test_default_type
 run_test test_custom_lang
 run_test test_parse_custom_lang_per_location
-run_test test_cgi_scripts
-run_test test_cgi_big_replies
-run_test test_cgi_split_query
 run_test test_custom_index
 run_test test_custom_index_default_type_per_location
 run_test test_auto_index
 run_test test_block
 run_test test_block_return_fmt
-run_test test_entrypoint
 run_test test_require_client_ca
 run_test test_root_inside_location
 run_test test_root_inside_location_with_redirect
-run_test test_fastcgi
+# run_test test_fastcgi		XXX: needs to be fixed
 run_test test_macro_expansion
-run_test test_174_bugfix
 run_test test_proxy_relay_to
 run_test test_proxy_with_certs
 run_test test_unknown_host
blob - 6a30334a74bd5573dc6227b8218a924852f1cd15
blob + 22d2598379a8c2a5cd15c5c9f375911a8ef561bb
--- regress/tests.sh
+++ regress/tests.sh
@@ -92,61 +92,6 @@ test_parse_custom_lang_per_location() {
 	# can parse multiple locations
 }
 
-test_cgi_scripts() {
-	setup_simple_test '' 'cgi "*"'
-
-	fetch /hello
-	check_reply "20 text/gemini" "# hello world" || return 1
-
-	fetch /slow
-	check_reply "20 text/gemini" "# hello world" || return 1
-
-	fetch /err
-	check_reply "42 CGI error" || return 1
-
-	fetch /invalid
-	check_reply "42 CGI error" || return 1
-}
-
-test_cgi_big_replies() {
-	setup_simple_test '' 'cgi "*"'
-
-	hdr="$(head /serve-bigfile)"
-	get /bigfile > bigfile
-	./sha bigfile bigfile.sha
-	body="$(cat bigfile.sha)"
-	check_reply "20 application/octet-stream" "$(cat testdata/bigfile.sha)"
-}
-
-test_cgi_split_query() {
-	setup_simple_test '' 'cgi "*"'
-
-	for s in "1" "2 ?foo" "3 ?foo+bar" "1 ?foo+bar=5" "3 ?foo+bar%3d5"; do
-		exp="$(echo $s | sed 's/ .*//')"
-		qry="$(echo $s | sed 's/^..//')"
-
-		if [ "$exp" = "$qry" ]; then
-			# the "1" case yields exp == qry
-			qry=''
-		fi
-
-		url="/env$qry"
-
-		n="$(get "$url" | awk /^-/ | count)"
-		if [ $? -ne 0 ]; then
-			echo "failed to get /$url"
-			return 1
-		fi
-
-		if [ "$n" -ne $exp ]; then
-			echo "Unexpected number of args"
-			echo "want : $exp"
-			echo "got  : $n"
-			return 1
-		fi
-	done
-}
-
 test_custom_index() {
 	setup_simple_test '' 'index "foo.gmi"'
 
@@ -222,28 +167,6 @@ location "*" {
 	check_reply "40 % /  10965 localhost test" || return 1
 }
 
-test_entrypoint() {
-	setup_simple_test '' 'entrypoint "/env"'
-
-	fetch_hdr /foo/bar
-	check_reply "20 text/plain; lang=en" || return 1
-
-	# TODO: test something similar with plain cgi too
-
-	body="$(get /foo/bar|grep PATH_INFO)"
-	if [ $? -ne 0 ]; then
-		echo "failed to get /foo/bar"
-		return 1
-	fi
-
-	if [ "$body" != "PATH_INFO=/foo/bar" ]; then
-		echo "Invalid PATH_INFO generated"
-		echo "want : PATH_INFO=/foo/bar"
-		echo "got  : $body"
-		return 1
-	fi
-}
-
 test_require_client_ca() {
 	setup_simple_test '' 'require client ca "'$PWD'/testca.pem"'
 
@@ -313,18 +236,6 @@ EOF
 	check_reply "20 text/gemini" "# hello world"
 }
 
-# 1.7.4 bugfix: check_for_cgi goes out-of-bound processing a string
-# that doesn't contain a '/'
-test_174_bugfix() {
-	setup_simple_test '' 'cgi "*"'
-
-	# thanks cage :)
-	for i in 0 1 2 3 4 5 6 7 8 9; do
-		fetch /favicon.txt
-		check_reply "51 not found" || return 1
-	done
-}
-
 test_proxy_relay_to() {
 	gen_config '' ''
 	set_proxy ''
blob - d22126081dad10afde8ce6fc233fa7f20a4602ec
blob + 45f175d8ee59374625f1428eb8d301f466505673
--- sandbox.c
+++ sandbox.c
@@ -638,7 +638,7 @@ sandbox_server_process(void)
 		}
 	}
 
-	if (pledge("stdio recvfd rpath inet", NULL) == -1)
+	if (pledge("stdio recvfd rpath inet dns", NULL) == -1)
 		fatal("pledge");
 }
 
blob - 2258e67a25485efa2c76dee0fd7d91da4546d78c
blob + 4e62ad3604d68309f967a4b07a403ed959e1535e
--- server.c
+++ server.c
@@ -17,6 +17,7 @@
 #include "gmid.h"
 
 #include <sys/stat.h>
+#include <sys/un.h>
 
 #include <assert.h>
 #include <ctype.h>
@@ -42,7 +43,6 @@ static inline int matches(const char*, const char*);
 
 static int	 check_path(struct client*, const char*, int*);
 static void	 open_file(struct client*);
-static void	 check_for_cgi(struct client*);
 static void	 handle_handshake(int, short, void*);
 static const char *strip_path(const char*, int);
 static void	 fmt_sbuf(const char*, struct client*, const char*);
@@ -51,8 +51,6 @@ static int	 check_matching_certificate(X509_STORE *, s
 static int	 apply_reverse_proxy(struct client *);
 static int	 apply_fastcgi(struct client*);
 static int	 apply_require_ca(struct client*);
-static size_t	 host_nth(struct vhost*);
-static void	 start_cgi(const char*, const char*, struct client*);
 static void	 open_dir(struct client*);
 static void	 redirect_canonical_dir(struct client*);
 
@@ -65,24 +63,14 @@ static void	 client_error(struct bufferevent *, short,
 
 static void	 client_close_ev(int, short, void *);
 
-static void	 cgi_read(struct bufferevent *, void *);
-static void	 cgi_write(struct bufferevent *, void *);
-static void	 cgi_error(struct bufferevent *, short, void *);
-
 static void	 do_accept(int, short, void*);
 
-static void	 handle_imsg_cgi_res(struct imsgbuf*, struct imsg*, size_t);
-static void	 handle_imsg_fcgi_fd(struct imsgbuf*, struct imsg*, size_t);
-static void	 handle_imsg_conn_fd(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 *);
 static void	 handle_siginfo(int, short, void*);
 
 static imsg_handlerfn *handlers[] = {
 	[IMSG_QUIT] = handle_imsg_quit,
-	[IMSG_CGI_RES] = handle_imsg_cgi_res,
-	[IMSG_FCGI_FD] = handle_imsg_fcgi_fd,
-	[IMSG_CONN_FD] = handle_imsg_conn_fd,
 };
 
 static uint32_t server_client_id;
@@ -348,9 +336,6 @@ check_path(struct client *c, const char *path, int *fd
 
 	if (S_ISDIR(sb.st_mode))
 		return FILE_DIRECTORY;
-
-	if (sb.st_mode & S_IXUSR)
-		return FILE_EXECUTABLE;
 
 	return FILE_EXISTS;
 }
@@ -359,14 +344,6 @@ static void
 open_file(struct client *c)
 {
 	switch (check_path(c, c->iri.path, &c->pfd)) {
-	case FILE_EXECUTABLE:
-		if (c->host->cgi != NULL && matches(c->host->cgi, c->iri.path)) {
-			start_cgi(c->iri.path, "", c);
-			return;
-		}
-
-		/* fallthrough */
-
 	case FILE_EXISTS:
 		c->type = REQUEST_FILE;
 		start_reply(c, SUCCESS, mime(c->host, c->iri.path));
@@ -377,10 +354,6 @@ open_file(struct client *c)
 		return;
 
 	case FILE_MISSING:
-		if (c->host->cgi != NULL && matches(c->host->cgi, c->iri.path)) {
-			check_for_cgi(c);
-			return;
-		}
 		start_reply(c, NOT_FOUND, "not found");
 		return;
 
@@ -390,55 +363,6 @@ open_file(struct client *c)
 	}
 }
 
-/*
- * the inverse of this algorithm, i.e. starting from the start of the
- * path + strlen(cgi), and checking if each component, should be
- * faster.  But it's tedious to write.  This does the opposite: starts
- * from the end and strip one component at a time, until either an
- * executable is found or we emptied the path.
- */
-static void
-check_for_cgi(struct client *c)
-{
-	char path[PATH_MAX];
-	char *end;
-
-	strlcpy(path, c->iri.path, sizeof(path));
-	end = strchr(path, '\0');
-
-	while (end > path) {
-		/*
-		 * go up one level.  UNIX paths are simple and POSIX
-		 * dirname, with its ambiguities on if the given
-		 * pointer is changed or not, gives me headaches.
-		 */
-		while (*end != '/' && end > path)
-			end--;
-
-		if (end == path)
-			break;
-
-		*end = '\0';
-
-		switch (check_path(c, path, &c->pfd)) {
-		case FILE_EXECUTABLE:
-			start_cgi(path, end+1, c);
-			return;
-		case FILE_MISSING:
-			break;
-		default:
-			goto err;
-		}
-
-		*end = '/';
-		end--;
-	}
-
-err:
-	start_reply(c, NOT_FOUND, "not found");
-	return;
-}
-
 void
 mark_nonblock(int fd)
 {
@@ -653,12 +577,52 @@ check_matching_certificate(X509_STORE *store, struct c
 	return 0;
 }
 
+static int
+proxy_socket(struct client *c, const char *host, const char *port)
+{
+	struct addrinfo hints, *res, *res0;
+	int r, sock;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+
+	/* XXX: asr_run? :> */
+	r = getaddrinfo(host, port, &hints, &res0);
+	if (r != 0) {
+		log_warn(c, "getaddrinfo(\"%s\", \"%s\"): %s",
+		    host, port, gai_strerror(r));
+		return -1;
+	}
+
+	for (res = res0; res; res = res->ai_next) {
+		sock = socket(res->ai_family, res->ai_socktype,
+		    res->ai_protocol);
+		if (sock == -1)
+			continue;
+
+		if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
+			close(sock);
+			sock = -1;
+			continue;
+		}
+
+		break;
+	}
+
+	freeaddrinfo(res0);
+
+	if (sock == -1)
+		log_warn(c, "can't connect to %s:%s", host, port);
+
+	return sock;
+}
+
 /* 1 if matching a proxy relay-to (and apply it), 0 otherwise */
 static int
 apply_reverse_proxy(struct client *c)
 {
 	struct proxy	*p;
-	struct connreq	 r;
 
 	if ((p = matched_proxy(c)) == NULL)
 		return 0;
@@ -671,15 +635,80 @@ apply_reverse_proxy(struct client *c)
 	log_debug(c, "opening proxy connection for %s:%s",
 	    p->host, p->port);
 
-	strlcpy(r.host, p->host, sizeof(r.host));
-	strlcpy(r.port, p->port, sizeof(r.port));
+	if ((c->pfd = proxy_socket(c, p->host, p->port)) == -1) {
+		start_reply(c, PROXY_ERROR, "proxy error");
+		return 1;
+	}
 
-	imsg_compose(&exibuf, IMSG_CONN_REQ, c->id, 0, -1, &r, sizeof(r));
-	imsg_flush(&exibuf);
+	mark_nonblock(c->pfd);
+	if (proxy_init(c) == -1)
+		start_reply(c, PROXY_ERROR, "proxy error");
 
 	return 1;
 }
 
+static int
+fcgi_open_sock(struct fcgi *f)
+{
+	struct sockaddr_un	addr;
+	int			fd;
+
+	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+		log_err(NULL, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, f->path, sizeof(addr.sun_path));
+
+	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+		log_warn(NULL, "failed to connect to %s: %s", f->path,
+		    strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int
+fcgi_open_conn(struct fcgi *f)
+{
+	struct addrinfo	hints, *servinfo, *p;
+	int		r, sock;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_ADDRCONFIG;
+
+	if ((r = getaddrinfo(f->path, f->port, &hints, &servinfo)) != 0) {
+		log_warn(NULL, "getaddrinfo %s:%s: %s", f->path, f->port,
+		    gai_strerror(r));
+		return -1;
+	}
+
+	for (p = servinfo; p != NULL; p = p->ai_next) {
+		sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+		if (sock == -1)
+			continue;
+		if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
+			close(sock);
+			continue;
+		}
+		break;
+	}
+
+	if (p == NULL) {
+		log_warn(NULL, "couldn't connect to %s:%s", f->path, f->port);
+		sock = -1;
+	}
+
+	freeaddrinfo(servinfo);
+	return sock;
+}
+
 /* 1 if matching `fcgi' (and apply it), 0 otherwise */
 static int
 apply_fastcgi(struct client *c)
@@ -692,12 +721,31 @@ apply_fastcgi(struct client *c)
 
 	f = &fcgi[id];
 
-	log_debug(c, "opening fastcgi connection for (%s,%s,%s)",
-	    f->path, f->port, f->prog);
+	log_debug(c, "opening fastcgi connection for (%s,%s)",
+	    f->path, f->port);
 
-	imsg_compose(&exibuf, IMSG_FCGI_REQ, c->id, 0, -1,
-	    &id, sizeof(id));
-	imsg_flush(&exibuf);
+	if (f->port != NULL)
+		c->pfd = fcgi_open_sock(f);
+	else
+		c->pfd = fcgi_open_conn(f);
+
+	if (c->pfd == -1) {
+		start_reply(c, CGI_ERROR, "CGI error");
+		return 1;
+	}
+
+	mark_nonblock(c->pfd);
+
+	c->cgibev = bufferevent_new(c->pfd, fcgi_read, fcgi_write,
+	    fcgi_error, c);
+	if (c->cgibev == NULL) {
+		start_reply(c, TEMP_FAILURE, "internal server error");
+		return 1;
+	}
+
+	bufferevent_enable(c->cgibev, EV_READ|EV_WRITE);
+	fcgi_req(c);
+
 	return 1;
 }
 
@@ -712,86 +760,15 @@ apply_require_ca(struct client *c)
 	return check_matching_certificate(store, c);
 }
 
-static size_t
-host_nth(struct vhost *h)
-{
-	struct vhost	*v;
-	size_t		 i = 0;
-
-	TAILQ_FOREACH(v, &hosts, vhosts) {
-		if (v == h)
-			return i;
-		i++;
-	}
-
-	abort();
-}
-
 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;
-
-	c->type = REQUEST_CGI;
-
-	e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr),
-	    addr, sizeof(addr),
-	    NULL, 0,
-	    NI_NUMERICHOST);
-	if (e != 0)
-		fatal("getnameinfo failed");
-
-	memset(&req, 0, sizeof(req));
-
-	memcpy(req.buf, c->req, c->reqlen);
-
-	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;
-
-	req.iri_portno = c->iri.port_no;
-
-	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));
-	if ((t = tls_conn_version(c->ctx)) != NULL)
-		strlcpy(req.version, t, sizeof(req.version));
-	if ((t = tls_conn_cipher(c->ctx)) != NULL)
-		strlcpy(req.cipher, t, sizeof(req.cipher));
-
-	req.cipher_strength = tls_conn_cipher_strength(c->ctx);
-	req.notbefore = tls_peer_cert_notbefore(c->ctx);
-	req.notafter = tls_peer_cert_notafter(c->ctx);
-
-	req.host_off = host_nth(c->host);
-	req.loc_off = c->loc;
-
-	imsg_compose(&exibuf, IMSG_CGI_REQ, c->id, 0, -1, &req, sizeof(req));
-	imsg_flush(&exibuf);
-
-	close(c->pfd);
-}
-
-static void
 open_dir(struct client *c)
 {
 	size_t len;
 	int dirfd, root;
 	char *before_file;
 
+	log_debug(c, "in open_dir");
+
 	root = !strcmp(c->iri.path, "/") || *c->iri.path == '\0';
 
 	len = strlen(c->iri.path);
@@ -819,14 +796,6 @@ open_dir(struct client *c)
 	c->pfd = -1;
 
 	switch (check_path(c, c->iri.path, &c->pfd)) {
-	case FILE_EXECUTABLE:
-		if (c->host->cgi != NULL && matches(c->host->cgi, c->iri.path)) {
-			start_cgi(c->iri.path, "", c);
-			break;
-		}
-
-		/* fallthrough */
-
 	case FILE_EXISTS:
 		c->type = REQUEST_FILE;
 		start_reply(c, SUCCESS, mime(c->host, c->iri.path));
@@ -1056,12 +1025,6 @@ client_read(struct bufferevent *bev, void *d)
 	    apply_fastcgi(c))
 		return;
 
-	if (c->host->entrypoint != NULL) {
-		c->loc = 0;
-		start_cgi(c->host->entrypoint, c->iri.path, c);
-		return;
-	}
-
 	open_file(c);
 }
 
@@ -1115,12 +1078,11 @@ client_write(struct bufferevent *bev, void *d)
 		event_add(&c->bev->ev_write, NULL);
 		break;
 
-	case REQUEST_CGI:
 	case REQUEST_FCGI:
 	case REQUEST_PROXY:
 		/*
-		 * Here we depend on the cgi/fastcgi or proxy
-		 * connection to provide data.
+		 * Here we depend on fastcgi or proxy connection to
+		 * provide data.
 		 */
 		break;
 
@@ -1177,8 +1139,7 @@ start_reply(struct client *c, int code, const char *me
 	if (r > 1027)
 		goto overflow;
 
-	if (c->type != REQUEST_CGI &&
-	    c->type != REQUEST_FCGI &&
+	if (c->type != REQUEST_FCGI &&
 	    c->type != REQUEST_PROXY &&
 	    !strcmp(meta, "text/gemini") &&
 	    (lang = vhost_lang(c->host, c->iri.path)) != NULL) {
@@ -1310,94 +1271,6 @@ client_close(struct client *c)
 }
 
 static void
-cgi_read(struct bufferevent *bev, void *d)
-{
-	struct client	*client = d;
-	struct evbuffer	*src = EVBUFFER_INPUT(bev);
-	char		*header;
-	size_t		 len;
-	int		 code;
-
-	/* intercept the header */
-	if (client->code == 0) {
-		header = evbuffer_readln(src, &len, EVBUFFER_EOL_CRLF_STRICT);
-		if (header == NULL) {
-			/* max reply + \r\n */
-			if (EVBUFFER_LENGTH(src) > 1029) {
-				log_warn(client, "CGI script is trying to "
-				    "send a header too long.");
-				cgi_error(bev, EVBUFFER_READ, client);
-			}
-
-			/* wait a bit */
-			return;
-		}
-
-		if (len < 3 || len > 1029 ||
-		    !isdigit(header[0]) ||
-		    !isdigit(header[1]) ||
-		    !isspace(header[2])) {
-			free(header);
-			log_warn(client, "CGI script is trying to send a "
-			    "malformed header");
-			cgi_error(bev, EVBUFFER_READ, client);
-			return;
-		}
-
-		client->header = header;
-		code = (header[0] - '0') * 10 + (header[1] - '0');
-
-		if (code < 10 || code >= 70) {
-			log_warn(client, "CGI script is trying to send an "
-			    "invalid reply code (%d)", code);
-			cgi_error(bev, EVBUFFER_READ, client);
-			return;
-		}
-
-		start_reply(client, code, header + 3);
-
-		if (client->code < 20 || client->code > 29) {
-			cgi_error(client->cgibev, EVBUFFER_EOF, client);
-			return;
-		}
-	}
-
-	bufferevent_write_buffer(client->bev, src);
-}
-
-static void
-cgi_write(struct bufferevent *bev, void *d)
-{
-	/*
-	 * Never called.  We don't send data to a CGI script.
-	 */
-	abort();
-}
-
-static void
-cgi_error(struct bufferevent *bev, short error, void *d)
-{
-	struct client	*client = d;
-
-	if (error & EVBUFFER_ERROR)
-		log_err(client, "%s: evbuffer error (%x): %s",
-		    __func__, error, strerror(errno));
-
-	bufferevent_disable(bev, EVBUFFER_READ|EVBUFFER_WRITE);
-	bufferevent_free(bev);
-	client->cgibev = NULL;
-
-	close(client->pfd);
-	client->pfd = -1;
-
-	client->type = REQUEST_DONE;
-	if (client->code != 0)
-		client_write(client->bev, client);
-	else
-		start_reply(client, CGI_ERROR, "CGI error");
-}
-
-static void
 do_accept(int sock, short et, void *d)
 {
 	struct client *c;
@@ -1445,86 +1318,6 @@ client_by_id(int id)
 }
 
 static void
-handle_imsg_cgi_res(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
-{
-	struct client *c;
-
-	if ((c = client_by_id(imsg->hdr.peerid)) == NULL) {
-		if (imsg->fd != -1)
-			close(imsg->fd);
-		return;
-	}
-
-	if ((c->pfd = imsg->fd) == -1) {
-		start_reply(c, TEMP_FAILURE, "internal server error");
-		return;
-	}
-
-	c->type = REQUEST_CGI;
-
-	c->cgibev = bufferevent_new(c->pfd, cgi_read, cgi_write,
-	    cgi_error, c);
-
-	bufferevent_enable(c->cgibev, EV_READ);
-}
-
-static void
-handle_imsg_fcgi_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
-{
-	struct client	*c;
-	int		 id;
-
-	id = imsg->hdr.peerid;
-
-	if ((c = client_by_id(id)) == NULL) {
-		if (imsg->fd != -1)
-			close(imsg->fd);
-		return;
-	}
-
-	if ((c->pfd = imsg->fd) == -1) {
-		start_reply(c, CGI_ERROR, "CGI error");
-		return;
-	}
-
-	mark_nonblock(c->pfd);
-
-	c->cgibev = bufferevent_new(c->pfd, fcgi_read, fcgi_write,
-	    fcgi_error, c);
-	if (c->cgibev == NULL) {
-		start_reply(c, TEMP_FAILURE, "internal server error");
-		return;
-	}
-
-	bufferevent_enable(c->cgibev, EV_READ|EV_WRITE);
-	fcgi_req(c);
-}
-
-static void
-handle_imsg_conn_fd(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
-{
-	struct client	*c;
-	int		 id;
-
-	id = imsg->hdr.peerid;
-	if ((c = client_by_id(id)) == NULL) {
-		if (imsg->fd != -1)
-			close(imsg->fd);
-		return;
-	}
-
-	if ((c->pfd = imsg->fd) == -1) {
-		start_reply(c, PROXY_ERROR, "proxy error");
-		return;
-	}
-
-	mark_nonblock(c->pfd);
-
-	if (proxy_init(c) == -1)
-		start_reply(c, PROXY_ERROR, "proxy error");
-}
-
-static void
 handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t len)
 {
 	/*