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
+}