Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b

Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/main/epoll.c b/src/main/epoll.c
new file mode 100644
index 0000000..dfe91fe
--- /dev/null
+++ b/src/main/epoll.c
@@ -0,0 +1,55 @@
+/**
+ * @file epoll.c  epoll specific routines
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <re_types.h>
+#include <re_mbuf.h>
+#include <re_sys.h>
+#include "main.h"
+
+
+#define DEBUG_MODULE "epoll"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/**
+ * Check for working epoll() kernel support
+ *
+ * @return true if support, false if not
+ */
+bool epoll_check(void)
+{
+	uint32_t osrel;
+	int err, epfd;
+
+	err = sys_rel_get(&osrel, NULL, NULL, NULL);
+	if (err)
+		return false;
+
+	if (osrel < 0x020542) {
+		DEBUG_INFO("epoll not supported in osrel=0x%08x\n", osrel);
+		return false;
+	}
+
+#ifdef OPENWRT
+	/* epoll is working again with 2.6.25.7 */
+	if (osrel < 0x020619) {
+		DEBUG_NOTICE("epoll is broken in osrel=0x%08x\n", osrel);
+		return false;
+	}
+#endif
+
+	epfd = epoll_create(64);
+	if (-1 == epfd) {
+		DEBUG_NOTICE("epoll_create: %m\n", errno);
+		return false;
+	}
+
+	(void)close(epfd);
+
+	return true;
+}
diff --git a/src/main/init.c b/src/main/init.c
new file mode 100644
index 0000000..3d5290b
--- /dev/null
+++ b/src/main/init.c
@@ -0,0 +1,58 @@
+/**
+ * @file init.c  Main initialisation routine
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_list.h>
+#include <re_net.h>
+#include <re_sys.h>
+#include <re_main.h>
+#include "main.h"
+
+
+/**
+ * Initialise main library
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int libre_init(void)
+{
+	int err;
+
+	rand_init();
+
+#ifdef USE_OPENSSL
+	err = openssl_init();
+	if (err)
+		goto out;
+#endif
+
+	err = net_sock_init();
+	if (err)
+		goto out;
+
+ out:
+	if (err) {
+		net_sock_close();
+#ifdef USE_OPENSSL
+		openssl_close();
+#endif
+	}
+
+	return err;
+}
+
+
+/**
+ * Close library and free up all resources
+ */
+void libre_close(void)
+{
+	(void)fd_setsize(0);
+	net_sock_close();
+#ifdef USE_OPENSSL
+	openssl_close();
+#endif
+}
diff --git a/src/main/main.c b/src/main/main.c
new file mode 100644
index 0000000..b90c139
--- /dev/null
+++ b/src/main/main.c
@@ -0,0 +1,1214 @@
+/**
+ * @file main.c  Main polling routine
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/types.h>
+#undef _STRICT_ANSI
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef WIN32
+#include <winsock.h>
+#endif
+#ifdef HAVE_SIGNAL
+#include <signal.h>
+#endif
+#ifdef HAVE_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_POLL
+#include <poll.h>
+#endif
+#ifdef HAVE_EPOLL
+#include <sys/epoll.h>
+#endif
+#ifdef HAVE_KQUEUE
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#undef LIST_INIT
+#undef LIST_FOREACH
+#endif
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_tmr.h>
+#include <re_main.h>
+#include "main.h"
+#ifdef HAVE_PTHREAD
+#define __USE_GNU 1
+#include <stdlib.h>
+#include <pthread.h>
+#endif
+
+
+#define DEBUG_MODULE "main"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+/*
+  epoll() has been tested successfully on the following kernels:
+
+  - Linux 2.6.16.29-xen   (Debian 4.0 etch)
+  - Linux 2.6.18-4-amd64  (Debian 4.0 etch)
+
+
+  TODO clean this up
+
+  - The polling method is selectable both in compile-time and run-time
+  - The polling method can be changed in run time. this is cool!
+  - Maximum number of fds can be set from application, but only once!
+  - Look at howto optimise main loop
+ */
+
+#if !defined (RELEASE) && !defined (MAIN_DEBUG)
+#define MAIN_DEBUG 1  /**< Enable main loop debugging */
+#endif
+
+
+/** Main loop values */
+enum {
+	MAX_BLOCKING = 100,    /**< Maximum time spent in handler in [ms] */
+#if defined (FD_SETSIZE)
+	DEFAULT_MAXFDS = FD_SETSIZE
+#else
+	DEFAULT_MAXFDS = 128
+#endif
+};
+
+
+/** Polling loop data */
+struct re {
+	/** File descriptor handler set */
+	struct {
+		int flags;           /**< Polling flags (Read, Write, etc.) */
+		fd_h *fh;            /**< Event handler                     */
+		void *arg;           /**< Handler argument                  */
+	} *fhs;
+	int maxfds;                  /**< Maximum number of polling fds     */
+	int nfds;                    /**< Number of active file descriptors */
+	enum poll_method method;     /**< The current polling method        */
+	bool update;                 /**< File descriptor set need updating */
+	bool polling;                /**< Is polling flag                   */
+	int sig;                     /**< Last caught signal                */
+	struct list tmrl;            /**< List of timers                    */
+
+#ifdef HAVE_POLL
+	struct pollfd *fds;          /**< Event set for poll()              */
+#endif
+
+#ifdef HAVE_EPOLL
+	struct epoll_event *events;  /**< Event set for epoll()             */
+	int epfd;                    /**< epoll control file descriptor     */
+#endif
+
+#ifdef HAVE_KQUEUE
+	struct kevent *evlist;
+	int kqfd;
+#endif
+
+#ifdef HAVE_PTHREAD
+	pthread_mutex_t mutex;       /**< Mutex for thread synchronization  */
+	pthread_mutex_t *mutexp;     /**< Pointer to active mutex           */
+#endif
+};
+
+static struct re global_re = {
+	NULL,
+	0,
+	0,
+	METHOD_NULL,
+	false,
+	false,
+	0,
+	LIST_INIT,
+#ifdef HAVE_POLL
+	NULL,
+#endif
+#ifdef HAVE_EPOLL
+	NULL,
+	-1,
+#endif
+#ifdef HAVE_KQUEUE
+	NULL,
+	-1,
+#endif
+#ifdef HAVE_PTHREAD
+#if MAIN_DEBUG && defined (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP)
+	PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP,
+#else
+	PTHREAD_MUTEX_INITIALIZER,
+#endif
+	&global_re.mutex,
+#endif
+};
+
+
+#ifdef HAVE_PTHREAD
+
+static void poll_close(struct re *re);
+
+static pthread_once_t pt_once = PTHREAD_ONCE_INIT;
+static pthread_key_t  pt_key;
+
+
+static void thread_destructor(void *arg)
+{
+	poll_close(arg);
+	free(arg);
+}
+
+
+static void re_once(void)
+{
+	pthread_key_create(&pt_key, thread_destructor);
+}
+
+
+static struct re *re_get(void)
+{
+	struct re *re;
+
+	pthread_once(&pt_once, re_once);
+
+	re = pthread_getspecific(pt_key);
+	if (!re) {
+		re = &global_re;
+	}
+
+	return re;
+}
+
+
+static inline void re_lock(struct re *re)
+{
+	int err;
+
+	err = pthread_mutex_lock(re->mutexp);
+	if (err) {
+		DEBUG_WARNING("re_lock: %m\n", err);
+	}
+}
+
+
+static inline void re_unlock(struct re *re)
+{
+	int err;
+
+	err = pthread_mutex_unlock(re->mutexp);
+	if (err) {
+		DEBUG_WARNING("re_unlock: %m\n", err);
+	}
+}
+
+
+#else
+
+static struct re *re_get(void)
+{
+	return &global_re;
+}
+
+#define re_lock(x)    /**< Stub */
+#define re_unlock(x)  /**< Stub */
+
+#endif
+
+
+#if MAIN_DEBUG
+/**
+ * Call the application event handler
+ *
+ * @param re     Poll state
+ * @param fd     File descriptor
+ * @param flags  Event flags
+ */
+static void fd_handler(struct re *re, int fd, int flags)
+{
+	const uint64_t tick = tmr_jiffies();
+	uint32_t diff;
+
+	DEBUG_INFO("event on fd=%d (flags=0x%02x)...\n", fd, flags);
+
+	re->fhs[fd].fh(flags, re->fhs[fd].arg);
+
+	diff = (uint32_t)(tmr_jiffies() - tick);
+
+	if (diff > MAX_BLOCKING) {
+		DEBUG_WARNING("long async blocking: %u>%u ms (h=%p arg=%p)\n",
+			      diff, MAX_BLOCKING,
+			      re->fhs[fd].fh, re->fhs[fd].arg);
+	}
+}
+#endif
+
+
+#ifdef HAVE_POLL
+static int set_poll_fds(struct re *re, int fd, int flags)
+{
+	if (!re->fds)
+		return 0;
+
+	if (flags)
+		re->fds[fd].fd = fd;
+	else
+		re->fds[fd].fd = -1;
+
+	re->fds[fd].events = 0;
+	if (flags & FD_READ)
+		re->fds[fd].events |= POLLIN;
+	if (flags & FD_WRITE)
+		re->fds[fd].events |= POLLOUT;
+	if (flags & FD_EXCEPT)
+		re->fds[fd].events |= POLLERR;
+
+	return 0;
+}
+#endif
+
+
+#ifdef HAVE_EPOLL
+static int set_epoll_fds(struct re *re, int fd, int flags)
+{
+	struct epoll_event event;
+	int err = 0;
+
+	if (re->epfd < 0)
+		return EBADFD;
+
+	memset(&event, 0, sizeof(event));
+
+	DEBUG_INFO("set_epoll_fds: fd=%d flags=0x%02x\n", fd, flags);
+
+	if (flags) {
+		event.data.fd = fd;
+
+		if (flags & FD_READ)
+			event.events |= EPOLLIN;
+		if (flags & FD_WRITE)
+			event.events |= EPOLLOUT;
+		if (flags & FD_EXCEPT)
+			event.events |= EPOLLERR;
+
+		/* Try to add it first */
+		if (-1 == epoll_ctl(re->epfd, EPOLL_CTL_ADD, fd, &event)) {
+
+			/* If already exist then modify it */
+			if (EEXIST == errno) {
+
+				if (-1 == epoll_ctl(re->epfd, EPOLL_CTL_MOD,
+						    fd, &event)) {
+					err = errno;
+					DEBUG_WARNING("epoll_ctl:"
+						      " EPOLL_CTL_MOD:"
+						      " fd=%d (%m)\n",
+						      fd, err);
+				}
+			}
+			else {
+				err = errno;
+				DEBUG_WARNING("epoll_ctl: EPOLL_CTL_ADD:"
+					      " fd=%d (%m)\n",
+					      fd, err);
+			}
+		}
+	}
+	else {
+		if (-1 == epoll_ctl(re->epfd, EPOLL_CTL_DEL, fd, &event)) {
+			err = errno;
+			DEBUG_INFO("epoll_ctl: EPOLL_CTL_DEL: fd=%d (%m)\n",
+				   fd, err);
+		}
+	}
+
+	return err;
+}
+#endif
+
+
+#ifdef HAVE_KQUEUE
+static int set_kqueue_fds(struct re *re, int fd, int flags)
+{
+	struct kevent kev[2];
+	int r, n = 0;
+
+	memset(kev, 0, sizeof(kev));
+
+	/* always delete the events */
+	EV_SET(&kev[0], fd, EVFILT_READ,  EV_DELETE, 0, 0, 0);
+	EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
+	kevent(re->kqfd, kev, 2, NULL, 0, NULL);
+
+	memset(kev, 0, sizeof(kev));
+
+	if (flags & FD_WRITE) {
+		EV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+		++n;
+	}
+	if (flags & FD_READ) {
+		EV_SET(&kev[n], fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+		++n;
+	}
+
+	if (n) {
+		r = kevent(re->kqfd, kev, n, NULL, 0, NULL);
+		if (r < 0) {
+			int err = errno;
+
+			DEBUG_WARNING("set: [fd=%d, flags=%x] kevent: %m\n",
+				      fd, flags, err);
+			return err;
+		}
+	}
+
+	return 0;
+}
+#endif
+
+
+/**
+ * Rebuild the file descriptor mapping table. This must be done whenever
+ * the polling method is changed.
+ */
+static int rebuild_fds(struct re *re)
+{
+	int i, err = 0;
+
+	DEBUG_INFO("rebuilding fds (nfds=%d)\n", re->nfds);
+
+	/* Update fd sets */
+	for (i=0; i<re->nfds; i++) {
+		if (!re->fhs[i].fh)
+			continue;
+
+		switch (re->method) {
+
+#ifdef HAVE_POLL
+		case METHOD_POLL:
+			err = set_poll_fds(re, i, re->fhs[i].flags);
+			break;
+#endif
+#ifdef HAVE_EPOLL
+		case METHOD_EPOLL:
+			err = set_epoll_fds(re, i, re->fhs[i].flags);
+			break;
+#endif
+
+#ifdef HAVE_KQUEUE
+		case METHOD_KQUEUE:
+			err = set_kqueue_fds(re, i, re->fhs[i].flags);
+			break;
+#endif
+
+		default:
+			break;
+		}
+
+		if (err)
+			break;
+	}
+
+	return err;
+}
+
+
+static int poll_init(struct re *re)
+{
+	DEBUG_INFO("poll init (maxfds=%d)\n", re->maxfds);
+
+	if (!re->maxfds) {
+		DEBUG_WARNING("poll init: maxfds is 0\n");
+		return EINVAL;
+	}
+
+	switch (re->method) {
+
+#ifdef HAVE_POLL
+	case METHOD_POLL:
+		if (!re->fds) {
+			re->fds = mem_zalloc(re->maxfds * sizeof(*re->fds),
+					    NULL);
+			if (!re->fds)
+				return ENOMEM;
+		}
+		break;
+#endif
+#ifdef HAVE_EPOLL
+	case METHOD_EPOLL:
+		if (!re->events) {
+			DEBUG_INFO("allocate %u bytes for epoll set\n",
+				   re->maxfds * sizeof(*re->events));
+			re->events = mem_zalloc(re->maxfds*sizeof(*re->events),
+					      NULL);
+			if (!re->events)
+				return ENOMEM;
+		}
+
+		if (re->epfd < 0
+		    && -1 == (re->epfd = epoll_create(re->maxfds))) {
+
+			int err = errno;
+
+			DEBUG_WARNING("epoll_create: %m (maxfds=%d)\n",
+				      err, re->maxfds);
+			return err;
+		}
+		DEBUG_INFO("init: epoll_create() epfd=%d\n", re->epfd);
+		break;
+#endif
+
+#ifdef HAVE_KQUEUE
+	case METHOD_KQUEUE:
+
+		if (!re->evlist) {
+			size_t sz = re->maxfds * sizeof(*re->evlist);
+			re->evlist = mem_zalloc(sz, NULL);
+			if (!re->evlist)
+				return ENOMEM;
+		}
+
+		if (re->kqfd < 0) {
+			re->kqfd = kqueue();
+			if (re->kqfd < 0)
+				return errno;
+			DEBUG_INFO("kqueue: fd=%d\n", re->kqfd);
+		}
+
+		break;
+#endif
+
+	default:
+		break;
+	}
+	return 0;
+}
+
+
+/** Free all resources */
+static void poll_close(struct re *re)
+{
+	DEBUG_INFO("poll close\n");
+
+	re->fhs = mem_deref(re->fhs);
+	re->maxfds = 0;
+
+#ifdef HAVE_POLL
+	re->fds = mem_deref(re->fds);
+#endif
+#ifdef HAVE_EPOLL
+	DEBUG_INFO("poll_close: epfd=%d\n", re->epfd);
+
+	if (re->epfd >= 0) {
+		(void)close(re->epfd);
+		re->epfd = -1;
+	}
+
+	re->events = mem_deref(re->events);
+#endif
+
+#ifdef HAVE_KQUEUE
+	if (re->kqfd >= 0) {
+		close(re->kqfd);
+		re->kqfd = -1;
+	}
+
+	re->evlist = mem_deref(re->evlist);
+#endif
+}
+
+
+static int poll_setup(struct re *re)
+{
+	int err;
+
+	err = fd_setsize(DEFAULT_MAXFDS);
+	if (err)
+		goto out;
+
+	if (METHOD_NULL == re->method) {
+		err = poll_method_set(poll_method_best());
+		if (err)
+			goto out;
+
+		DEBUG_INFO("poll setup: poll method not set - set to `%s'\n",
+			   poll_method_name(re->method));
+	}
+
+	err = poll_init(re);
+
+ out:
+	if (err)
+		poll_close(re);
+
+	return err;
+}
+
+
+/**
+ * Listen for events on a file descriptor
+ *
+ * @param fd     File descriptor
+ * @param flags  Wanted event flags
+ * @param fh     Event handler
+ * @param arg    Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int fd_listen(int fd, int flags, fd_h *fh, void *arg)
+{
+	struct re *re = re_get();
+	int err = 0;
+
+	DEBUG_INFO("fd_listen: fd=%d flags=0x%02x\n", fd, flags);
+
+	if (fd < 0) {
+		DEBUG_WARNING("fd_listen: corrupt fd %d\n", fd);
+		return EBADF;
+	}
+
+	if (flags || fh) {
+		err = poll_setup(re);
+		if (err)
+			return err;
+	}
+
+	if (fd >= re->maxfds) {
+		if (flags) {
+			DEBUG_WARNING("fd_listen: fd=%d flags=0x%02x"
+				      " - Max %d fds\n",
+				      fd, flags, re->maxfds);
+		}
+		return EMFILE;
+	}
+
+	/* Update fh set */
+	if (re->fhs) {
+		re->fhs[fd].flags = flags;
+		re->fhs[fd].fh    = fh;
+		re->fhs[fd].arg   = arg;
+	}
+
+	re->nfds = max(re->nfds, fd+1);
+
+	switch (re->method) {
+
+#ifdef HAVE_POLL
+	case METHOD_POLL:
+		err = set_poll_fds(re, fd, flags);
+		break;
+#endif
+
+#ifdef HAVE_EPOLL
+	case METHOD_EPOLL:
+		if (re->epfd < 0)
+			return EBADFD;
+		err = set_epoll_fds(re, fd, flags);
+		break;
+#endif
+
+#ifdef HAVE_KQUEUE
+	case METHOD_KQUEUE:
+		err = set_kqueue_fds(re, fd, flags);
+		break;
+#endif
+
+	default:
+		break;
+	}
+
+	if (err) {
+		if (flags && fh) {
+			fd_close(fd);
+			DEBUG_WARNING("fd_listen: fd=%d flags=0x%02x (%m)\n",
+				      fd, flags, err);
+		}
+	}
+
+	return err;
+}
+
+
+/**
+ * Stop listening for events on a file descriptor
+ *
+ * @param fd     File descriptor
+ */
+void fd_close(int fd)
+{
+	(void)fd_listen(fd, 0, NULL, NULL);
+}
+
+
+/**
+ * Polling loop
+ *
+ * @param re Poll state.
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+static int fd_poll(struct re *re)
+{
+	const uint64_t to = tmr_next_timeout(&re->tmrl);
+	int i, n;
+#ifdef HAVE_SELECT
+	fd_set rfds, wfds, efds;
+#endif
+
+	DEBUG_INFO("next timer: %llu ms\n", to);
+
+	/* Wait for I/O */
+	switch (re->method) {
+
+#ifdef HAVE_POLL
+	case METHOD_POLL:
+		re_unlock(re);
+		n = poll(re->fds, re->nfds, to ? (int)to : -1);
+		re_lock(re);
+		break;
+#endif
+#ifdef HAVE_SELECT
+	case METHOD_SELECT: {
+		struct timeval tv;
+
+		/* Clear and update fd sets */
+		FD_ZERO(&rfds);
+		FD_ZERO(&wfds);
+		FD_ZERO(&efds);
+
+		for (i=0; i<re->nfds; i++) {
+			if (!re->fhs[i].fh)
+				continue;
+
+			if (re->fhs[i].flags & FD_READ)
+				FD_SET(i, &rfds);
+			if (re->fhs[i].flags & FD_WRITE)
+				FD_SET(i, &wfds);
+			if (re->fhs[i].flags & FD_EXCEPT)
+				FD_SET(i, &efds);
+		}
+
+#ifdef WIN32
+		tv.tv_sec  = (long) to / 1000;
+#else
+		tv.tv_sec  = (time_t) to / 1000;
+#endif
+		tv.tv_usec = (uint32_t) (to % 1000) * 1000;
+		re_unlock(re);
+		n = select(re->nfds, &rfds, &wfds, &efds, to ? &tv : NULL);
+		re_lock(re);
+	}
+		break;
+#endif
+#ifdef HAVE_EPOLL
+	case METHOD_EPOLL:
+		re_unlock(re);
+		n = epoll_wait(re->epfd, re->events, re->maxfds,
+			       to ? (int)to : -1);
+		re_lock(re);
+		break;
+#endif
+
+#ifdef HAVE_KQUEUE
+	case METHOD_KQUEUE: {
+		struct timespec timeout;
+
+		timeout.tv_sec = (time_t) (to / 1000);
+		timeout.tv_nsec = (to % 1000) * 1000000;
+
+		re_unlock(re);
+		n = kevent(re->kqfd, NULL, 0, re->evlist, re->maxfds,
+			   to ? &timeout : NULL);
+		re_lock(re);
+		}
+		break;
+#endif
+
+	default:
+		(void)to;
+		DEBUG_WARNING("no polling method set\n");
+		return EINVAL;
+	}
+
+	if (n < 0)
+		return errno;
+
+	/* Check for events */
+	for (i=0; (n > 0) && (i < re->nfds); i++) {
+		int fd, flags = 0;
+
+		switch (re->method) {
+
+#ifdef HAVE_POLL
+		case METHOD_POLL:
+			fd = i;
+			if (re->fds[fd].revents & POLLIN)
+				flags |= FD_READ;
+			if (re->fds[fd].revents & POLLOUT)
+				flags |= FD_WRITE;
+			if (re->fds[fd].revents & (POLLERR|POLLHUP|POLLNVAL))
+				flags |= FD_EXCEPT;
+			if (re->fds[fd].revents & POLLNVAL) {
+				DEBUG_WARNING("event: fd=%d POLLNVAL"
+					      " (fds.fd=%d,"
+					      " fds.events=0x%02x)\n",
+					      fd, re->fds[fd].fd,
+					      re->fds[fd].events);
+			}
+			/* Clear events */
+			re->fds[fd].revents = 0;
+			break;
+#endif
+#ifdef HAVE_SELECT
+		case METHOD_SELECT:
+			fd = i;
+			if (FD_ISSET(fd, &rfds))
+				flags |= FD_READ;
+			if (FD_ISSET(fd, &wfds))
+				flags |= FD_WRITE;
+			if (FD_ISSET(fd, &efds))
+				flags |= FD_EXCEPT;
+			break;
+#endif
+#ifdef HAVE_EPOLL
+		case METHOD_EPOLL:
+			fd = re->events[i].data.fd;
+
+			if (re->events[i].events & EPOLLIN)
+				flags |= FD_READ;
+			if (re->events[i].events & EPOLLOUT)
+				flags |= FD_WRITE;
+			if (re->events[i].events & (EPOLLERR|EPOLLHUP))
+				flags |= FD_EXCEPT;
+
+			if (!flags) {
+				DEBUG_WARNING("epoll: no flags fd=%d\n", fd);
+			}
+
+			break;
+#endif
+
+#ifdef HAVE_KQUEUE
+		case METHOD_KQUEUE: {
+
+			struct kevent *kev = &re->evlist[i];
+
+			fd = (int)kev->ident;
+
+			if (fd >= re->maxfds) {
+				DEBUG_WARNING("large fd=%d\n", fd);
+				break;
+			}
+
+			if (kev->filter == EVFILT_READ)
+				flags |= FD_READ;
+			else if (kev->filter == EVFILT_WRITE)
+				flags |= FD_WRITE;
+			else {
+				DEBUG_WARNING("kqueue: unhandled "
+					      "filter %x\n",
+					      kev->filter);
+			}
+
+			if (kev->flags & EV_EOF) {
+				flags |= FD_EXCEPT;
+			}
+			if (kev->flags & EV_ERROR) {
+				DEBUG_WARNING("kqueue: EV_ERROR on fd %d\n",
+					      fd);
+			}
+
+			if (!flags) {
+				DEBUG_WARNING("kqueue: no flags fd=%d\n", fd);
+			}
+		}
+			break;
+#endif
+
+		default:
+			return EINVAL;
+		}
+
+		if (!flags)
+			continue;
+
+		if (re->fhs[fd].fh) {
+#if MAIN_DEBUG
+			fd_handler(re, fd, flags);
+#else
+			re->fhs[fd].fh(flags, re->fhs[fd].arg);
+#endif
+		}
+
+		/* Check if polling method was changed */
+		if (re->update) {
+			re->update = false;
+			return 0;
+		}
+
+		--n;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Set the maximum number of file descriptors
+ *
+ * @param maxfds Max FDs. 0 to free.
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int fd_setsize(int maxfds)
+{
+	struct re *re = re_get();
+
+	if (!maxfds) {
+		fd_debug();
+		poll_close(re);
+		return 0;
+	}
+
+	if (!re->maxfds)
+		re->maxfds = maxfds;
+
+	if (!re->fhs) {
+		DEBUG_INFO("fd_setsize: maxfds=%d, allocating %u bytes\n",
+			   re->maxfds, re->maxfds * sizeof(*re->fhs));
+
+		re->fhs = mem_zalloc(re->maxfds * sizeof(*re->fhs), NULL);
+		if (!re->fhs)
+			return ENOMEM;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Print all file descriptors in-use
+ */
+void fd_debug(void)
+{
+	const struct re *re = re_get();
+	int i;
+
+	if (!re->fhs)
+		return;
+
+	for (i=0; i<re->nfds; i++) {
+
+		if (!re->fhs[i].flags)
+			continue;
+
+		(void)re_fprintf(stderr,
+				 "fd %d in use: flags=%x fh=%p arg=%p\n",
+				 i, re->fhs[i].flags, re->fhs[i].fh,
+				 re->fhs[i].arg);
+	}
+}
+
+
+#ifdef HAVE_SIGNAL
+/* Thread-safe signal handling */
+static void signal_handler(int sig)
+{
+	(void)signal(sig, signal_handler);
+	re_get()->sig = sig;
+}
+#endif
+
+
+/**
+ * Main polling loop for async I/O events. This function will only return when
+ * re_cancel() is called or an error occured.
+ *
+ * @param signalh Optional Signal handler
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int re_main(re_signal_h *signalh)
+{
+	struct re *re = re_get();
+	int err;
+
+#ifdef HAVE_SIGNAL
+	if (signalh) {
+		(void)signal(SIGINT, signal_handler);
+		(void)signal(SIGALRM, signal_handler);
+		(void)signal(SIGTERM, signal_handler);
+	}
+#endif
+
+	if (re->polling) {
+		DEBUG_WARNING("main loop already polling\n");
+		return EALREADY;
+	}
+
+	err = poll_setup(re);
+	if (err)
+		goto out;
+
+	DEBUG_INFO("Using async I/O polling method: `%s'\n",
+		   poll_method_name(re->method));
+
+	re->polling = true;
+
+	re_lock(re);
+	for (;;) {
+
+		if (re->sig) {
+			if (signalh)
+				signalh(re->sig);
+
+			re->sig = 0;
+		}
+
+		if (!re->polling) {
+			err = 0;
+			break;
+		}
+
+		err = fd_poll(re);
+		if (err) {
+			if (EINTR == err)
+				continue;
+
+#ifdef DARWIN
+			/* NOTE: workaround for Darwin */
+			if (EBADF == err)
+				continue;
+#endif
+
+			break;
+		}
+
+		tmr_poll(&re->tmrl);
+	}
+	re_unlock(re);
+
+ out:
+	re->polling = false;
+
+	return err;
+}
+
+
+/**
+ * Cancel the main polling loop
+ */
+void re_cancel(void)
+{
+	struct re *re = re_get();
+
+	re->polling = false;
+}
+
+
+/**
+ * Debug the main polling loop
+ *
+ * @param pf     Print handler where debug output is printed to
+ * @param unused Unused parameter
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int re_debug(struct re_printf *pf, void *unused)
+{
+	struct re *re = re_get();
+	int err = 0;
+
+	(void)unused;
+
+	err |= re_hprintf(pf, "re main loop:\n");
+	err |= re_hprintf(pf, "  maxfds:  %d\n", re->maxfds);
+	err |= re_hprintf(pf, "  nfds:    %d\n", re->nfds);
+	err |= re_hprintf(pf, "  method:  %d (%s)\n", re->method,
+			  poll_method_name(re->method));
+
+	return err;
+}
+
+
+/**
+ * Set async I/O polling method. This function can also be called while the
+ * program is running.
+ *
+ * @param method New polling method
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int poll_method_set(enum poll_method method)
+{
+	struct re *re = re_get();
+	int err;
+
+	err = fd_setsize(DEFAULT_MAXFDS);
+	if (err)
+		return err;
+
+	switch (method) {
+
+#ifdef HAVE_POLL
+	case METHOD_POLL:
+		break;
+#endif
+#ifdef HAVE_SELECT
+	case METHOD_SELECT:
+		if (re->maxfds > (int)FD_SETSIZE) {
+			DEBUG_WARNING("SELECT: maxfds > FD_SETSIZE\n");
+			return EMFILE;
+		}
+		break;
+#endif
+#ifdef HAVE_EPOLL
+	case METHOD_EPOLL:
+		if (!epoll_check())
+			return EINVAL;
+		break;
+#endif
+#ifdef HAVE_KQUEUE
+	case METHOD_KQUEUE:
+		break;
+#endif
+	default:
+		DEBUG_WARNING("poll method not supported: '%s'\n",
+			      poll_method_name(method));
+		return EINVAL;
+	}
+
+	re->method = method;
+	re->update = true;
+
+	DEBUG_INFO("Setting async I/O polling method to `%s'\n",
+		   poll_method_name(re->method));
+
+	err = poll_init(re);
+	if (err)
+		return err;
+
+	return rebuild_fds(re);
+}
+
+
+/**
+ * Add a worker thread for this thread
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int re_thread_init(void)
+{
+#ifdef HAVE_PTHREAD
+	struct re *re;
+
+	pthread_once(&pt_once, re_once);
+
+	re = pthread_getspecific(pt_key);
+	if (re) {
+		DEBUG_WARNING("thread_init: already added for thread %d\n",
+			      pthread_self());
+		return EALREADY;
+	}
+
+	re = malloc(sizeof(*re));
+	if (!re)
+		return ENOMEM;
+
+	memset(re, 0, sizeof(*re));
+	pthread_mutex_init(&re->mutex, NULL);
+	re->mutexp = &re->mutex;
+
+#ifdef HAVE_EPOLL
+	re->epfd = -1;
+#endif
+
+#ifdef HAVE_KQUEUE
+	re->kqfd = -1;
+#endif
+
+	pthread_setspecific(pt_key, re);
+	return 0;
+#else
+	return ENOSYS;
+#endif
+}
+
+
+/**
+ * Remove the worker thread for this thread
+ */
+void re_thread_close(void)
+{
+#ifdef HAVE_PTHREAD
+	struct re *re;
+
+	pthread_once(&pt_once, re_once);
+
+	re = pthread_getspecific(pt_key);
+	if (re) {
+		poll_close(re);
+		free(re);
+		pthread_setspecific(pt_key, NULL);
+	}
+#endif
+}
+
+
+/**
+ * Enter an 're' thread
+ *
+ * @note Must only be called from a non-re thread
+ */
+void re_thread_enter(void)
+{
+	re_lock(re_get());
+}
+
+
+/**
+ * Leave an 're' thread
+ *
+ * @note Must only be called from a non-re thread
+ */
+void re_thread_leave(void)
+{
+	re_unlock(re_get());
+}
+
+
+/**
+ * Set an external mutex for this thread
+ *
+ * @param mutexp Pointer to external mutex, NULL to use internal
+ */
+void re_set_mutex(void *mutexp)
+{
+#ifdef HAVE_PTHREAD
+	struct re *re = re_get();
+
+	re->mutexp = mutexp ? mutexp : &re->mutex;
+#else
+	(void)mutexp;
+#endif
+}
+
+
+/**
+ * Get the timer-list for this thread
+ *
+ * @return Timer list
+ *
+ * @note only used by tmr module
+ */
+struct list *tmrl_get(void);
+struct list *tmrl_get(void)
+{
+	return &re_get()->tmrl;
+}
diff --git a/src/main/main.h b/src/main/main.h
new file mode 100644
index 0000000..a934c63
--- /dev/null
+++ b/src/main/main.h
@@ -0,0 +1,24 @@
+/**
+ * @file main.h  Internal interface to main polling loop
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+#ifdef HAVE_EPOLL
+bool epoll_check(void);
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef USE_OPENSSL
+int  openssl_init(void);
+void openssl_close(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/main/method.c b/src/main/method.c
new file mode 100644
index 0000000..d4fc1fb
--- /dev/null
+++ b/src/main/method.c
@@ -0,0 +1,102 @@
+/**
+ * @file method.c  Polling methods
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_main.h>
+#include "main.h"
+
+
+static const char str_poll[]   = "poll";     /**< POSIX.1-2001 poll       */
+static const char str_select[] = "select";   /**< POSIX.1-2001 select     */
+static const char str_epoll[]  = "epoll";    /**< Linux epoll             */
+static const char str_kqueue[] = "kqueue";
+
+
+/**
+ * Choose the best async I/O polling method
+ *
+ * @return Polling method
+ */
+enum poll_method poll_method_best(void)
+{
+	enum poll_method m = METHOD_NULL;
+
+#ifdef HAVE_EPOLL
+	/* Supported from Linux 2.5.66 */
+	if (METHOD_NULL == m) {
+		if (epoll_check())
+			m = METHOD_EPOLL;
+	}
+#endif
+
+#ifdef HAVE_KQUEUE
+	if (METHOD_NULL == m) {
+		m = METHOD_KQUEUE;
+	}
+#endif
+
+#ifdef HAVE_POLL
+	if (METHOD_NULL == m) {
+		m = METHOD_POLL;
+	}
+#endif
+#ifdef HAVE_SELECT
+	if (METHOD_NULL == m) {
+		m = METHOD_SELECT;
+	}
+#endif
+
+	return m;
+}
+
+
+/**
+ * Get the name of the polling method
+ *
+ * @param method Polling method
+ *
+ * @return Polling name string
+ */
+const char *poll_method_name(enum poll_method method)
+{
+	switch (method) {
+
+	case METHOD_POLL:      return str_poll;
+	case METHOD_SELECT:    return str_select;
+	case METHOD_EPOLL:     return str_epoll;
+	case METHOD_KQUEUE:    return str_kqueue;
+	default:               return "???";
+	}
+}
+
+
+/**
+ * Get the polling method type from a string
+ *
+ * @param method Returned polling method
+ * @param name   Polling method name string
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int poll_method_type(enum poll_method *method, const struct pl *name)
+{
+	if (!method || !name)
+		return EINVAL;
+
+	if (0 == pl_strcasecmp(name, str_poll))
+		*method = METHOD_POLL;
+	else if (0 == pl_strcasecmp(name, str_select))
+		*method = METHOD_SELECT;
+	else if (0 == pl_strcasecmp(name, str_epoll))
+		*method = METHOD_EPOLL;
+	else if (0 == pl_strcasecmp(name, str_kqueue))
+		*method = METHOD_KQUEUE;
+	else
+		return ENOENT;
+
+	return 0;
+}
diff --git a/src/main/mod.mk b/src/main/mod.mk
new file mode 100644
index 0000000..7adffae
--- /dev/null
+++ b/src/main/mod.mk
@@ -0,0 +1,17 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS	+= main/init.c
+SRCS	+= main/main.c
+SRCS	+= main/method.c
+
+ifneq ($(HAVE_EPOLL),)
+SRCS	+= main/epoll.c
+endif
+
+ifneq ($(USE_OPENSSL),)
+SRCS    += main/openssl.c
+endif
diff --git a/src/main/openssl.c b/src/main/openssl.c
new file mode 100644
index 0000000..dfeb91e
--- /dev/null
+++ b/src/main/openssl.c
@@ -0,0 +1,165 @@
+/**
+ * @file openssl.c  OpenSSL initialisation and multi-threading routines
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#ifdef HAVE_SIGNAL
+#include <signal.h>
+#endif
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+#include <openssl/crypto.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <re_types.h>
+#include <re_lock.h>
+#include <re_mem.h>
+#include "main.h"
+
+
+#if defined (HAVE_PTHREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
+
+
+static pthread_mutex_t *lockv;
+
+
+static inline unsigned long threadid(void)
+{
+#if defined (DARWIN) || defined (FREEBSD) || defined (OPENBSD) || \
+	defined (NETBSD) || defined (DRAGONFLY)
+	return (unsigned long)(void *)pthread_self();
+#else
+	return (unsigned long)pthread_self();
+#endif
+}
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000
+static void threadid_handler(CRYPTO_THREADID *id)
+{
+	CRYPTO_THREADID_set_numeric(id, threadid());
+}
+#else
+static unsigned long threadid_handler(void)
+{
+	return threadid();
+}
+#endif
+
+
+static void locking_handler(int mode, int type, const char *file, int line)
+{
+	(void)file;
+	(void)line;
+
+	if (mode & CRYPTO_LOCK)
+		(void)pthread_mutex_lock(&lockv[type]);
+	else
+		(void)pthread_mutex_unlock(&lockv[type]);
+}
+
+
+#endif
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static struct CRYPTO_dynlock_value *dynlock_create_handler(const char *file,
+							   int line)
+{
+	struct lock *lock;
+	(void)file;
+	(void)line;
+
+	if (lock_alloc(&lock))
+		return NULL;
+
+	return (struct CRYPTO_dynlock_value *)lock;
+}
+
+
+static void dynlock_lock_handler(int mode, struct CRYPTO_dynlock_value *l,
+				 const char *file, int line)
+{
+	struct lock *lock = (struct lock *)l;
+	(void)file;
+	(void)line;
+
+	if (mode & CRYPTO_LOCK)
+		lock_write_get(lock);
+	else
+		lock_rel(lock);
+}
+
+
+static void dynlock_destroy_handler(struct CRYPTO_dynlock_value *l,
+				    const char *file, int line)
+{
+	(void)file;
+	(void)line;
+
+	mem_deref(l);
+}
+#endif
+
+
+#ifdef SIGPIPE
+static void sigpipe_handler(int x)
+{
+	(void)x;
+	(void)signal(SIGPIPE, sigpipe_handler);
+}
+#endif
+
+
+int openssl_init(void)
+{
+#if defined (HAVE_PTHREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
+	int err, i;
+
+	lockv = mem_zalloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks(), NULL);
+	if (!lockv)
+		return ENOMEM;
+
+	for (i=0; i<CRYPTO_num_locks(); i++) {
+
+		err = pthread_mutex_init(&lockv[i], NULL);
+		if (err) {
+			lockv = mem_deref(lockv);
+			return err;
+		}
+	}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000
+	CRYPTO_THREADID_set_callback(threadid_handler);
+#else
+	CRYPTO_set_id_callback(threadid_handler);
+#endif
+
+	CRYPTO_set_locking_callback(locking_handler);
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	CRYPTO_set_dynlock_create_callback(dynlock_create_handler);
+	CRYPTO_set_dynlock_lock_callback(dynlock_lock_handler);
+	CRYPTO_set_dynlock_destroy_callback(dynlock_destroy_handler);
+#endif
+
+#ifdef SIGPIPE
+	(void)signal(SIGPIPE, sigpipe_handler);
+#endif
+
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	return 0;
+}
+
+
+void openssl_close(void)
+{
+	ERR_free_strings();
+#if defined (HAVE_PTHREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
+	lockv = mem_deref(lockv);
+#endif
+}