commit 25f1ab9865dbcaffb6a8c303f41275d0dbf626e3
parent 98de763632989c89c25987c797bffd581600ffb0
Author: Joris Vink <joris@coders.se>
Date:   Mon, 17 Jun 2013 23:39:17 +0200
Add BSD kqueue(2) support. Compile with make bsd (or make linux for linux)
Diffstat:
9 files changed, 625 insertions(+), 325 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,20 +3,29 @@
 CC=gcc
 BIN=kore
 
-S_SRC=	src/kore.c src/buf.c src/config.c src/net.c src/spdy.c src/http.c \
+S_SRC+=	src/kore.c src/buf.c src/config.c src/net.c src/spdy.c src/http.c \
 	src/module.c src/utils.c src/zlib_dict.c
 S_OBJS=	$(S_SRC:.c=.o)
 
 CFLAGS+=-I/usr/local/ssl/include
 CFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+=-Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual
-CFLAGS+=-D_GNU_SOURCE=1 -Wsign-compare -Iincludes -g
-LDFLAGS=-rdynamic -Llibs -lssl -lcrypto -ldl -lz
+CFLAGS+=-Wsign-compare -Iincludes -g
+LDFLAGS+=-rdynamic -Llibs -lssl -lcrypto -lz
+
+default:
+	@echo "Please specify a build target [linux | bsd]"
+
+linux:
+	@LDFLAGS=-ldl CFLAGS=-D_GNU_SOURCE=1 S_SRC=src/linux.c make kore
+
+bsd:
+	@S_SRC=src/bsd.c make kore
 
 kore: $(S_OBJS)
 	$(CC) $(CFLAGS) $(S_OBJS) $(LDFLAGS) -o $(BIN)
 
-.c.o: $<
+.c.o:
 	$(CC) $(CFLAGS) -c $< -o $@
 
 clean:
diff --git a/example.conf b/example.conf
@@ -3,7 +3,7 @@
 # Server configuration.
 bind		10.211.55.3 443
 certfile	cert/server.crt
-certkey		certe/server.key
+certkey		cert/server.key
 
 # The path worker processes will chroot too after starting.
 chroot		/home/joris/src/kore
diff --git a/example/Makefile b/example/Makefile
@@ -37,7 +37,7 @@ html_inject: tools/html_inject.c
 .css.c:
 	tools/html_inject $< `basename $<` > $@
 
-.c.o: $<
+.c.o:
 	$(CC) -fPIC $(CFLAGS) -c $< -o $@
 
 clean:
diff --git a/example/src/example.c b/example/src/example.c
@@ -18,7 +18,6 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/queue.h>
-#include <sys/epoll.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
diff --git a/includes/kore.h b/includes/kore.h
@@ -111,6 +111,8 @@ struct kore_worker {
 	TAILQ_ENTRY(kore_worker)	list;
 };
 
+TAILQ_HEAD(kore_worker_h, kore_worker);
+
 #define KORE_BUF_INITIAL	128
 #define KORE_BUF_INCREMENT	KORE_BUF_INITIAL
 
@@ -125,6 +127,7 @@ struct buf_vec {
 	u_int32_t		length;
 };
 
+extern pid_t	mypid;
 extern int	kore_debug;
 extern int	server_port;
 extern char	*server_ip;
@@ -134,8 +137,25 @@ extern char	*kore_module_onload;
 extern char	*kore_pidfile;
 extern char	*kore_certfile;
 extern char	*kore_certkey;
-extern u_int8_t	worker_count;
-extern pid_t	mypid;
+
+extern u_int16_t		cpu_count;
+extern u_int8_t			worker_count;
+
+extern struct listener		server;
+extern struct kore_worker_h	kore_workers;
+
+void		kore_init(void);
+void		kore_worker_init(void);
+void		kore_worker_wait(int);
+void		kore_event_init(void);
+void		kore_event_wait(int);
+void		kore_set_proctitle(char *);
+void		kore_worker_spawn(u_int16_t);
+void		kore_worker_entry(struct kore_worker *);
+void		kore_worker_setcpu(struct kore_worker *);
+void		kore_event_schedule(int, int, int, void *);
+int		kore_connection_handle(struct connection *);
+int		kore_server_accept(struct listener *, struct connection **);
 
 void		kore_log_init(void);
 void		*kore_malloc(size_t);
diff --git a/src/bsd.c b/src/bsd.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2013 Joris Vink <joris@coders.se>
+ *
+ * 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 <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/event.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+#include <regex.h>
+#include <zlib.h>
+#include <unistd.h>
+
+#include "spdy.h"
+#include "kore.h"
+#include "http.h"
+
+#define KQUEUE_EVENTS		500
+static int			kfd = -1;
+static struct kevent		*events;
+static int			nchanges;
+static struct kevent		*changelist;
+
+void
+kore_init(void)
+{
+	cpu_count = 0;
+}
+
+void
+kore_worker_init(void)
+{
+	u_int16_t		i;
+
+	if (worker_count == 0)
+		fatal("no workers specified");
+
+	kore_debug("kore_worker_init(): starting %d workers", worker_count);
+
+	TAILQ_INIT(&kore_workers);
+	for (i = 0; i < worker_count; i++)
+		kore_worker_spawn(0);
+}
+
+void
+kore_worker_wait(int final)
+{
+	pid_t			pid;
+	int			status;
+	struct kore_worker	k, *kw, *next;
+
+	if (final)
+		pid = waitpid(WAIT_ANY, &status, 0);
+	else
+		pid = waitpid(WAIT_ANY, &status, WNOHANG);
+
+	if (pid == -1) {
+		kore_debug("waitpid(): %s", errno_s);
+		return;
+	}
+
+	if (pid == 0)
+		return;
+
+	for (kw = TAILQ_FIRST(&kore_workers); kw != NULL; kw = next) {
+		next = TAILQ_NEXT(kw, list);
+		if (kw->pid != pid)
+			continue;
+
+		k = *kw;
+		TAILQ_REMOVE(&kore_workers, kw, list);
+		kore_log(LOG_NOTICE, "worker %d (%d)-> status %d",
+		    kw->id, pid, status);
+		free(kw);
+
+		if (final)
+			continue;
+
+		if (WEXITSTATUS(status) || WTERMSIG(status) ||
+		    WCOREDUMP(status)) {
+			kore_log(LOG_NOTICE,
+			    "worker %d (pid: %d) gone, respawning new one",
+			    k.id, k.pid);
+			kore_worker_spawn(0);
+		}
+	}
+}
+
+void
+kore_worker_setcpu(struct kore_worker *kw)
+{
+}
+
+void
+kore_event_init(void)
+{
+	if ((kfd = kqueue()) == -1)
+		fatal("kqueue(): %s", errno_s);
+
+	nchanges = 0;
+	events = kore_calloc(KQUEUE_EVENTS, sizeof(struct kevent));
+	changelist = kore_calloc(KQUEUE_EVENTS, sizeof(struct kevent));
+	kore_event_schedule(server.fd, EVFILT_READ, EV_ADD, &server);
+}
+
+void
+kore_event_wait(int quit)
+{
+	struct connection	*c;
+	int			n, i, *fd;
+
+	n = kevent(kfd, changelist, nchanges, events, KQUEUE_EVENTS, NULL);
+	if (n == -1) {
+		if (errno == EINTR)
+			return;
+		fatal("kevent(): %s", errno_s);
+	}
+
+	nchanges = 0;
+	if (n > 0)
+		kore_debug("main(): %d sockets available", n);
+
+	for (i = 0; i < n; i++) {
+		fd = (int *)events[i].udata;
+
+		if (events[i].flags & EV_EOF ||
+		    events[i].flags & EV_ERROR) {
+			if (*fd == server.fd)
+				fatal("error on server socket");
+
+			c = (struct connection *)events[i].udata;
+			kore_server_disconnect(c);
+			continue;
+		}
+
+		if (*fd == server.fd) {
+			if (!quit) {
+				kore_server_accept(&server, &c);
+				if (c == NULL)
+					continue;
+
+				kore_event_schedule(c->fd, EVFILT_READ,
+				    EV_ADD, c);
+				kore_event_schedule(c->fd, EVFILT_WRITE,
+				    EV_ADD | EV_ONESHOT, c);
+			}
+		} else {
+			c = (struct connection *)events[i].udata;
+			if (events[i].filter == EVFILT_READ)
+				c->flags |= CONN_READ_POSSIBLE;
+			if (events[i].filter == EVFILT_WRITE)
+				c->flags |= CONN_WRITE_POSSIBLE;
+
+			if (!kore_connection_handle(c)) {
+				kore_server_disconnect(c);
+			} else {
+				if (!TAILQ_EMPTY(&(c->send_queue))) {
+					kore_event_schedule(c->fd, EVFILT_WRITE,
+					    EV_ADD | EV_ONESHOT, c);
+				}
+			}
+		}
+	}
+}
+
+void
+kore_event_schedule(int fd, int type, int flags, void *data)
+{
+	EV_SET(&changelist[nchanges], fd, type, flags, 0, 0, data);
+	nchanges++;
+}
+
+void
+kore_set_proctitle(char *title)
+{
+	setproctitle("%s", title);
+}
diff --git a/src/kore.c b/src/kore.c
@@ -18,8 +18,6 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/queue.h>
-#include <sys/epoll.h>
-#include <sys/prctl.h>
 #include <sys/wait.h>
 
 #include <netinet/in.h>
@@ -48,21 +46,18 @@
 #include "kore.h"
 #include "http.h"
 
-#define EPOLL_EVENTS	500
-
-static int	efd = -1;
 static SSL_CTX	*ssl_ctx = NULL;
 
 volatile sig_atomic_t			sig_recv;
-static struct listener			server;
 static TAILQ_HEAD(, connection)		disconnected;
 static TAILQ_HEAD(, connection)		worker_clients;
-static TAILQ_HEAD(, kore_worker)	kore_workers;
 static struct passwd			*pw = NULL;
 static u_int16_t			workerid = 0;
-static u_int16_t			cpu_count = 1;
 
+struct listener		server;
 pid_t			mypid = -1;
+u_int16_t		cpu_count = 1;
+struct kore_worker_h	kore_workers;
 int			kore_debug = 0;
 int			server_port = 0;
 u_int8_t		worker_count = 0;
@@ -75,17 +70,9 @@ char			*kore_pidfile = KORE_PIDFILE_DEFAULT;
 
 static void	usage(void);
 static void	kore_signal(int);
-static void	kore_worker_wait(int);
-static void	kore_worker_init(void);
 static void	kore_write_mypid(void);
 static int	kore_socket_nonblock(int);
 static int	kore_server_sslstart(void);
-static void	kore_event(int, int, void *);
-static void	kore_worker_spawn(u_int16_t);
-static int	kore_server_accept(struct listener *);
-static void	kore_worker_entry(struct kore_worker *);
-static void	kore_worker_setcpu(struct kore_worker *);
-static int	kore_connection_handle(struct connection *, int);
 static void	kore_server_final_disconnect(struct connection *);
 static int	kore_server_bind(struct listener *, const char *, int);
 static int	kore_ssl_npn_cb(SSL *, const u_char **, unsigned int *, void *);
@@ -146,10 +133,7 @@ main(int argc, char *argv[])
 	if (kore_certfile == NULL || kore_certkey == NULL)
 		fatal("missing certificate information");
 
-	if ((cpu_count = sysconf(_SC_NPROCESSORS_ONLN)) == -1) {
-		kore_debug("could not get number of cpu's falling back to 1");
-		cpu_count = 1;
-	}
+	kore_init();
 
 	if (!kore_server_sslstart())
 		fatal("cannot initiate SSL");
@@ -163,9 +147,7 @@ main(int argc, char *argv[])
 
 	kore_log(LOG_NOTICE, "kore is starting up");
 	kore_worker_init();
-
-	if (prctl(PR_SET_NAME, "kore [main]"))
-		kore_debug("cannot set process title");
+	kore_set_proctitle("kore [parent]");
 
 	sig_recv = 0;
 	signal(SIGHUP, kore_signal);
@@ -213,102 +195,15 @@ main(int argc, char *argv[])
 	return (0);
 }
 
-void
-kore_server_disconnect(struct connection *c)
-{
-	if (c->state != CONN_STATE_DISCONNECTING) {
-		kore_debug("preparing %p for disconnection", c);
-		c->state = CONN_STATE_DISCONNECTING;
-		TAILQ_REMOVE(&worker_clients, c, list);
-		TAILQ_INSERT_TAIL(&disconnected, c, list);
-	}
-}
-
-static int
-kore_server_sslstart(void)
-{
-	kore_debug("kore_server_sslstart()");
-
-	SSL_library_init();
-	SSL_load_error_strings();
-	ssl_ctx = SSL_CTX_new(SSLv23_server_method());
-	if (ssl_ctx == NULL) {
-		kore_debug("SSL_ctx_new(): %s", ssl_errno_s);
-		return (KORE_RESULT_ERROR);
-	}
-
-	if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, kore_certfile)) {
-		kore_debug("SSL_CTX_use_certificate_file(): %s", ssl_errno_s);
-		return (KORE_RESULT_ERROR);
-	}
-
-	if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, kore_certkey,
-	    SSL_FILETYPE_PEM)) {
-		kore_debug("SSL_CTX_use_PrivateKey_file(): %s", ssl_errno_s);
-		return (KORE_RESULT_ERROR);
-	}
-
-	SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
-	SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
-	SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
-	SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, kore_ssl_npn_cb, NULL);
-
-	return (KORE_RESULT_OK);
-}
-
-static int
-kore_server_bind(struct listener *l, const char *ip, int port)
-{
-	int	on;
-
-	kore_debug("kore_server_bind(%p, %s, %d)", l, ip, port);
-
-	if ((l->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
-		kore_debug("socket(): %s", errno_s);
-		return (KORE_RESULT_ERROR);
-	}
-
-	if (!kore_socket_nonblock(l->fd)) {
-		close(l->fd);
-		return (KORE_RESULT_ERROR);
-	}
-
-	on = 1;
-	if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on,
-	    sizeof(on)) == -1) {
-		kore_debug("setsockopt(): %s", errno_s);
-		close(l->fd);
-		return (KORE_RESULT_ERROR);
-	}
-
-	memset(&(l->sin), 0, sizeof(l->sin));
-	l->sin.sin_family = AF_INET;
-	l->sin.sin_port = htons(port);
-	l->sin.sin_addr.s_addr = inet_addr(ip);
-
-	if (bind(l->fd, (struct sockaddr *)&(l->sin), sizeof(l->sin)) == -1) {
-		close(l->fd);
-		kore_debug("bind(): %s", errno_s);
-		return (KORE_RESULT_ERROR);
-	}
-
-	if (listen(l->fd, 50) == -1) {
-		close(l->fd);
-		kore_debug("listen(): %s", errno_s);
-		return (KORE_RESULT_ERROR);
-	}
-
-	return (KORE_RESULT_OK);
-}
-
-static int
-kore_server_accept(struct listener *l)
+int
+kore_server_accept(struct listener *l, struct connection **out)
 {
 	socklen_t		len;
 	struct connection	*c;
 
 	kore_debug("kore_server_accept(%p)", l);
 
+	*out = NULL;
 	len = sizeof(struct sockaddr_in);
 	c = (struct connection *)kore_malloc(sizeof(*c));
 	if ((c->fd = accept(l->fd, (struct sockaddr *)&(c->sin), &len)) == -1) {
@@ -334,75 +229,32 @@ kore_server_accept(struct listener *l)
 
 	TAILQ_INIT(&(c->send_queue));
 	TAILQ_INIT(&(c->recv_queue));
-	TAILQ_INIT(&(c->spdy_streams));;
+	TAILQ_INIT(&(c->spdy_streams));
 	TAILQ_INSERT_TAIL(&worker_clients, c, list);
 
-	kore_event(c->fd, EPOLLIN | EPOLLOUT | EPOLLET, c);
-
+	*out = c;
 	return (KORE_RESULT_OK);
 }
 
-static void
-kore_server_final_disconnect(struct connection *c)
+void
+kore_server_disconnect(struct connection *c)
 {
-	struct netbuf		*nb, *next;
-	struct spdy_stream	*s, *snext;
-
-	kore_debug("kore_server_final_disconnect(%p)", c);
-
-	if (c->ssl != NULL)
-		SSL_free(c->ssl);
-
-	TAILQ_REMOVE(&disconnected, c, list);
-	close(c->fd);
-	if (c->inflate_started)
-		inflateEnd(&(c->z_inflate));
-	if (c->deflate_started)
-		deflateEnd(&(c->z_deflate));
-
-	for (nb = TAILQ_FIRST(&(c->send_queue)); nb != NULL; nb = next) {
-		next = TAILQ_NEXT(nb, list);
-		TAILQ_REMOVE(&(c->send_queue), nb, list);
-		free(nb->buf);
-		free(nb);
-	}
-
-	for (nb = TAILQ_FIRST(&(c->recv_queue)); nb != NULL; nb = next) {
-		next = TAILQ_NEXT(nb, list);
-		TAILQ_REMOVE(&(c->recv_queue), nb, list);
-		free(nb->buf);
-		free(nb);
-	}
-
-	for (s = TAILQ_FIRST(&(c->spdy_streams)); s != NULL; s = snext) {
-		snext = TAILQ_NEXT(s, list);
-		TAILQ_REMOVE(&(c->spdy_streams), s, list);
-
-		if (s->hblock != NULL) {
-			if (s->hblock->header_block != NULL)
-				free(s->hblock->header_block);
-			free(s->hblock);
-		}
-
-		free(s);
+	if (c->state != CONN_STATE_DISCONNECTING) {
+		kore_debug("preparing %p for disconnection", c);
+		c->state = CONN_STATE_DISCONNECTING;
+		TAILQ_REMOVE(&worker_clients, c, list);
+		TAILQ_INSERT_TAIL(&disconnected, c, list);
 	}
-
-	free(c);
 }
 
-static int
-kore_connection_handle(struct connection *c, int flags)
+int
+kore_connection_handle(struct connection *c)
 {
 	int			r;
 	u_int32_t		len;
 	const u_char		*data;
 
-	kore_debug("kore_connection_handle(%p, %d)", c, flags);
-
-	if (flags & EPOLLIN)
-		c->flags |= CONN_READ_POSSIBLE;
-	if (flags & EPOLLOUT)
-		c->flags |= CONN_WRITE_POSSIBLE;
+	kore_debug("kore_connection_handle(%p)", c);
 
 	switch (c->state) {
 	case CONN_STATE_SSL_SHAKE:
@@ -473,29 +325,7 @@ kore_connection_handle(struct connection *c, int flags)
 	return (KORE_RESULT_OK);
 }
 
-static void
-kore_worker_init(void)
-{
-	u_int16_t		i, cpu;
-
-	if (worker_count == 0)
-		fatal("no workers specified");
-
-	kore_debug("kore_worker_init(): system has %d cpu's", cpu_count);
-	kore_debug("kore_worker_init(): starting %d workers", worker_count);
-	if (worker_count > cpu_count)
-		kore_debug("kore_worker_init(): more workers then cpu's");
-
-	cpu = 0;
-	TAILQ_INIT(&kore_workers);
-	for (i = 0; i < worker_count; i++) {
-		kore_worker_spawn(cpu++);
-		if (cpu == cpu_count)
-			cpu = 0;
-	}
-}
-
-static void
+void
 kore_worker_spawn(u_int16_t cpu)
 {
 	struct kore_worker	*kw;
@@ -516,78 +346,13 @@ kore_worker_spawn(u_int16_t cpu)
 	TAILQ_INSERT_TAIL(&kore_workers, kw, list);
 }
 
-static void
-kore_worker_wait(int final)
-{
-	int			r;
-	siginfo_t		info;
-	struct kore_worker	k, *kw, *next;
-
-	memset(&info, 0, sizeof(info));
-	if (final)
-		r = waitid(P_ALL, 0, &info, WEXITED);
-	else
-		r = waitid(P_ALL, 0, &info, WEXITED | WNOHANG);
-	if (r == -1) {
-		kore_debug("waitid(): %s", errno_s);
-		return;
-	}
-
-	if (info.si_pid == 0)
-		return;
-
-	for (kw = TAILQ_FIRST(&kore_workers); kw != NULL; kw = next) {
-		next = TAILQ_NEXT(kw, list);
-		if (kw->pid != info.si_pid)
-			continue;
-
-		k = *kw;
-		TAILQ_REMOVE(&kore_workers, kw, list);
-		kore_log(LOG_NOTICE, "worker %d (%d)-> status %d (%d)",
-		    kw->id, info.si_pid, info.si_status, info.si_code);
-		free(kw);
-
-		if (final)
-			continue;
-
-		if (info.si_code == CLD_EXITED ||
-		    info.si_code == CLD_KILLED ||
-		    info.si_code == CLD_DUMPED) {
-			kore_log(LOG_NOTICE,
-			    "worker %d (pid: %d) gone, respawning new one",
-			    k.id, k.pid);
-			kore_worker_spawn(k.cpu);
-		}
-	}
-}
-
-static void
-kore_worker_setcpu(struct kore_worker *kw)
-{
-	cpu_set_t	cpuset;
-
-	CPU_ZERO(&cpuset);
-	CPU_SET(kw->cpu, &cpuset);
-	if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) == -1) {
-		kore_debug("kore_worker_setcpu(): %s", errno_s);
-	} else {
-		kore_debug("kore_worker_setcpu(): worker %d on cpu %d",
-		    kw->id, kw->cpu);
-	}
-}
-
-static void
+void
 kore_worker_entry(struct kore_worker *kw)
 {
+	int			quit;
 	char			buf[16];
-	struct epoll_event	*events;
 	struct connection	*c, *cnext;
 	struct kore_worker	*k, *next;
-	int			n, i, *fd, quit;
-
-	snprintf(buf, sizeof(buf), "kore [wrk %d]", kw->id);
-	if (prctl(PR_SET_NAME, buf) == -1)
-		kore_debug("cannot set process title");
 
 	if (chroot(chroot_path) == -1)
 		fatal("cannot chroot(): %s", errno_s);
@@ -597,6 +362,8 @@ kore_worker_entry(struct kore_worker *kw)
 	    pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
 		fatal("unable to drop privileges");
 
+	snprintf(buf, sizeof(buf), "kore [wrk %d]", kw->id);
+	kore_set_proctitle(buf);
 	kore_worker_setcpu(kw);
 
 	for (k = TAILQ_FIRST(&kore_workers); k != NULL; k = next) {
@@ -606,8 +373,6 @@ kore_worker_entry(struct kore_worker *kw)
 	}
 
 	mypid = kw->pid;
-	if ((efd = epoll_create(1000)) == -1)
-		fatal("epoll_create(): %s", errno_s);
 
 	sig_recv = 0;
 	signal(SIGHUP, kore_signal);
@@ -618,8 +383,7 @@ kore_worker_entry(struct kore_worker *kw)
 	TAILQ_INIT(&worker_clients);
 
 	quit = 0;
-	kore_event(server.fd, EPOLLIN, &server);
-	events = kore_calloc(EPOLL_EVENTS, sizeof(struct epoll_event));
+	kore_event_init();
 
 	kore_log(LOG_NOTICE, "worker %d going to work (CPU: %d)",
 	    kw->id, kw->cpu);
@@ -632,40 +396,7 @@ kore_worker_entry(struct kore_worker *kw)
 			sig_recv = 0;
 		}
 
-		n = epoll_wait(efd, events, EPOLL_EVENTS, 100);
-		if (n == -1) {
-			if (errno == EINTR)
-				continue;
-			fatal("epoll_wait(): %s", errno_s);
-		}
-
-		if (n > 0)
-			kore_debug("main(): %d sockets available", n);
-
-		for (i = 0; i < n; i++) {
-			fd = (int *)events[i].data.ptr;
-
-			if (events[i].events & EPOLLERR ||
-			    events[i].events & EPOLLHUP) {
-				if (*fd == server.fd)
-					fatal("error on server socket");
-
-				c = (struct connection *)events[i].data.ptr;
-				kore_server_disconnect(c);
-				continue;
-			}
-
-			if (*fd == server.fd) {
-				if (!quit)
-					kore_server_accept(&server);
-			} else {
-				c = (struct connection *)events[i].data.ptr;
-				if (!kore_connection_handle(c,
-				    events[i].events))
-					kore_server_disconnect(c);
-			}
-		}
-
+		kore_event_wait(quit);
 		http_process();
 
 		for (c = TAILQ_FIRST(&disconnected); c != NULL; c = cnext) {
@@ -694,6 +425,131 @@ kore_worker_entry(struct kore_worker *kw)
 }
 
 static int
+kore_server_sslstart(void)
+{
+	kore_debug("kore_server_sslstart()");
+
+	SSL_library_init();
+	SSL_load_error_strings();
+	ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+	if (ssl_ctx == NULL) {
+		kore_debug("SSL_ctx_new(): %s", ssl_errno_s);
+		return (KORE_RESULT_ERROR);
+	}
+
+	if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, kore_certfile)) {
+		kore_debug("SSL_CTX_use_certificate_file(): %s", ssl_errno_s);
+		return (KORE_RESULT_ERROR);
+	}
+
+	if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, kore_certkey,
+	    SSL_FILETYPE_PEM)) {
+		kore_debug("SSL_CTX_use_PrivateKey_file(): %s", ssl_errno_s);
+		return (KORE_RESULT_ERROR);
+	}
+
+	SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
+	SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+	SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
+	SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, kore_ssl_npn_cb, NULL);
+
+	return (KORE_RESULT_OK);
+}
+
+static int
+kore_server_bind(struct listener *l, const char *ip, int port)
+{
+	int	on;
+
+	kore_debug("kore_server_bind(%p, %s, %d)", l, ip, port);
+
+	if ((l->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+		kore_debug("socket(): %s", errno_s);
+		return (KORE_RESULT_ERROR);
+	}
+
+	if (!kore_socket_nonblock(l->fd)) {
+		close(l->fd);
+		return (KORE_RESULT_ERROR);
+	}
+
+	on = 1;
+	if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on,
+	    sizeof(on)) == -1) {
+		kore_debug("setsockopt(): %s", errno_s);
+		close(l->fd);
+		return (KORE_RESULT_ERROR);
+	}
+
+	memset(&(l->sin), 0, sizeof(l->sin));
+	l->sin.sin_family = AF_INET;
+	l->sin.sin_port = htons(port);
+	l->sin.sin_addr.s_addr = inet_addr(ip);
+
+	if (bind(l->fd, (struct sockaddr *)&(l->sin), sizeof(l->sin)) == -1) {
+		close(l->fd);
+		kore_debug("bind(): %s", errno_s);
+		return (KORE_RESULT_ERROR);
+	}
+
+	if (listen(l->fd, 50) == -1) {
+		close(l->fd);
+		kore_debug("listen(): %s", errno_s);
+		return (KORE_RESULT_ERROR);
+	}
+
+	return (KORE_RESULT_OK);
+}
+
+static void
+kore_server_final_disconnect(struct connection *c)
+{
+	struct netbuf		*nb, *next;
+	struct spdy_stream	*s, *snext;
+
+	kore_debug("kore_server_final_disconnect(%p)", c);
+
+	if (c->ssl != NULL)
+		SSL_free(c->ssl);
+
+	TAILQ_REMOVE(&disconnected, c, list);
+	close(c->fd);
+	if (c->inflate_started)
+		inflateEnd(&(c->z_inflate));
+	if (c->deflate_started)
+		deflateEnd(&(c->z_deflate));
+
+	for (nb = TAILQ_FIRST(&(c->send_queue)); nb != NULL; nb = next) {
+		next = TAILQ_NEXT(nb, list);
+		TAILQ_REMOVE(&(c->send_queue), nb, list);
+		free(nb->buf);
+		free(nb);
+	}
+
+	for (nb = TAILQ_FIRST(&(c->recv_queue)); nb != NULL; nb = next) {
+		next = TAILQ_NEXT(nb, list);
+		TAILQ_REMOVE(&(c->recv_queue), nb, list);
+		free(nb->buf);
+		free(nb);
+	}
+
+	for (s = TAILQ_FIRST(&(c->spdy_streams)); s != NULL; s = snext) {
+		snext = TAILQ_NEXT(s, list);
+		TAILQ_REMOVE(&(c->spdy_streams), s, list);
+
+		if (s->hblock != NULL) {
+			if (s->hblock->header_block != NULL)
+				free(s->hblock->header_block);
+			free(s->hblock);
+		}
+
+		free(s);
+	}
+
+	free(c);
+}
+
+static int
 kore_socket_nonblock(int fd)
 {
 	int		flags;
@@ -726,25 +582,6 @@ kore_ssl_npn_cb(SSL *ssl, const u_char **data, unsigned int *len, void *arg)
 }
 
 static void
-kore_event(int fd, int flags, void *udata)
-{
-	struct epoll_event	evt;
-
-	kore_debug("kore_event(%d, %d, %p)", fd, flags, udata);
-
-	evt.events = flags;
-	evt.data.ptr = udata;
-	if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &evt) == -1) {
-		if (errno == EEXIST) {
-			if (epoll_ctl(efd, EPOLL_CTL_MOD, fd, &evt) == -1)
-				fatal("epoll_ctl() MOD: %s", errno_s);
-		} else {
-			fatal("epoll_ctl() ADD: %s", errno_s);
-		}
-	}
-}
-
-static void
 kore_write_mypid(void)
 {
 	FILE		*fp;
diff --git a/src/linux.c b/src/linux.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2013 Joris Vink <joris@coders.se>
+ *
+ * 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 <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+#include <regex.h>
+#include <zlib.h>
+#include <unistd.h>
+
+#include "spdy.h"
+#include "kore.h"
+#include "http.h"
+
+#define EPOLL_EVENTS	500
+
+static int			efd = -1;
+static struct epoll_event	*events = NULL;
+
+void
+kore_init(void)
+{
+	if ((cpu_count = sysconf(_SC_NPROCESSORS_ONLN)) == -1) {
+		kore_debug("could not get number of cpu's falling back to 1");
+		cpu_count = 1;
+	}
+}
+
+void
+kore_worker_init(void)
+{
+	u_int16_t		i, cpu;
+
+	if (worker_count == 0)
+		fatal("no workers specified");
+
+	kore_debug("kore_worker_init(): system has %d cpu's", cpu_count);
+	kore_debug("kore_worker_init(): starting %d workers", worker_count);
+	if (worker_count > cpu_count)
+		kore_debug("kore_worker_init(): more workers then cpu's");
+
+	cpu = 0;
+	TAILQ_INIT(&kore_workers);
+	for (i = 0; i < worker_count; i++) {
+		kore_worker_spawn(cpu++);
+		if (cpu == cpu_count)
+			cpu = 0;
+	}
+}
+
+void
+kore_worker_wait(int final)
+{
+	int			r;
+	siginfo_t		info;
+	struct kore_worker	k, *kw, *next;
+
+	memset(&info, 0, sizeof(info));
+	if (final)
+		r = waitid(P_ALL, 0, &info, WEXITED);
+	else
+		r = waitid(P_ALL, 0, &info, WEXITED | WNOHANG);
+	if (r == -1) {
+		kore_debug("waitid(): %s", errno_s);
+		return;
+	}
+
+	if (info.si_pid == 0)
+		return;
+
+	for (kw = TAILQ_FIRST(&kore_workers); kw != NULL; kw = next) {
+		next = TAILQ_NEXT(kw, list);
+		if (kw->pid != info.si_pid)
+			continue;
+
+		k = *kw;
+		TAILQ_REMOVE(&kore_workers, kw, list);
+		kore_log(LOG_NOTICE, "worker %d (%d)-> status %d (%d)",
+		    kw->id, info.si_pid, info.si_status, info.si_code);
+		free(kw);
+
+		if (final)
+			continue;
+
+		if (info.si_code == CLD_EXITED ||
+		    info.si_code == CLD_KILLED ||
+		    info.si_code == CLD_DUMPED) {
+			kore_log(LOG_NOTICE,
+			    "worker %d (pid: %d) gone, respawning new one",
+			    k.id, k.pid);
+			kore_worker_spawn(k.cpu);
+		}
+	}
+}
+
+void
+kore_worker_setcpu(struct kore_worker *kw)
+{
+	cpu_set_t	cpuset;
+
+	CPU_ZERO(&cpuset);
+	CPU_SET(kw->cpu, &cpuset);
+	if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) == -1) {
+		kore_debug("kore_worker_setcpu(): %s", errno_s);
+	} else {
+		kore_debug("kore_worker_setcpu(): worker %d on cpu %d",
+		    kw->id, kw->cpu);
+	}
+}
+
+void
+kore_event_init(void)
+{
+	if ((efd = epoll_create(1000)) == -1)
+		fatal("epoll_create(): %s", errno_s);
+
+	events = kore_calloc(EPOLL_EVENTS, sizeof(struct epoll_event));
+	kore_event_schedule(server.fd, EPOLLIN, 0, &server);
+}
+
+void
+kore_event_wait(int quit)
+{
+	struct connection	*c;
+	int			n, i, *fd;
+
+	n = epoll_wait(efd, events, EPOLL_EVENTS, 100);
+	if (n == -1) {
+		if (errno == EINTR)
+			return;
+		fatal("epoll_wait(): %s", errno_s);
+	}
+
+	if (n > 0)
+		kore_debug("main(): %d sockets available", n);
+
+	for (i = 0; i < n; i++) {
+		fd = (int *)events[i].data.ptr;
+
+		if (events[i].events & EPOLLERR ||
+		    events[i].events & EPOLLHUP) {
+			if (*fd == server.fd)
+				fatal("error on server socket");
+
+			c = (struct connection *)events[i].data.ptr;
+			kore_server_disconnect(c);
+			continue;
+		}
+
+		if (*fd == server.fd) {
+			if (!quit) {
+				kore_server_accept(&server, &c);
+				if (c == NULL)
+					continue;
+
+				kore_event_schedule(c->fd,
+				    EPOLLIN | EPOLLOUT | EPOLLET, 0, c);
+			}
+		} else {
+			c = (struct connection *)events[i].data.ptr;
+			if (events[i].events & EPOLLIN)
+				c->flags |= CONN_READ_POSSIBLE;
+			if (events[i].events & EPOLLOUT)
+				c->flags |= CONN_WRITE_POSSIBLE;
+
+			if (!kore_connection_handle(c))
+				kore_server_disconnect(c);
+		}
+	}
+}
+
+void
+kore_event_schedule(int fd, int type, int flags, void *udata)
+{
+	struct epoll_event	evt;
+
+	kore_debug("kore_event(%d, %d, %d, %p)", fd, type, flags, udata);
+
+	evt.events = type;
+	evt.data.ptr = udata;
+	if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &evt) == -1) {
+		if (errno == EEXIST) {
+			if (epoll_ctl(efd, EPOLL_CTL_MOD, fd, &evt) == -1)
+				fatal("epoll_ctl() MOD: %s", errno_s);
+		} else {
+			fatal("epoll_ctl() ADD: %s", errno_s);
+		}
+	}
+}
+
+void
+kore_set_proctitle(char *title)
+{
+	if (prctl(PR_SET_NAME, title) == -1)
+		kore_debug("prctl(): %s", errno_s);
+}
diff --git a/src/net.c b/src/net.c
@@ -18,7 +18,6 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/queue.h>
-#include <sys/epoll.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>