/* * OpenVPN -- An application to securely tunnel IP networks * over a single TCP/UDP port, with support for SSL/TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2002-2018 OpenVPN Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) #include "config-msvc.h" #endif #include "syshead.h" #include "buffer.h" #include "error.h" #include "integer.h" #include "event.h" #include "fdmisc.h" #include "memdbg.h" /* * Some OSes will prefer select() over poll() * when both are available. */ #if defined(TARGET_DARWIN) #define SELECT_PREFERRED_OVER_POLL #endif /* * All non-windows OSes are assumed to have select() */ #ifdef _WIN32 #define SELECT 0 #else #define SELECT 1 #endif /* * This should be set to the highest file descriptor * which can be used in one of the FD_ macros. */ #ifdef FD_SETSIZE #define SELECT_MAX_FDS FD_SETSIZE #else #define SELECT_MAX_FDS 256 #endif static inline int tv_to_ms_timeout(const struct timeval *tv) { if (tv->tv_sec == 0 && tv->tv_usec == 0) { return 0; } else { return max_int(tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000, 1); } } #ifdef _WIN32 struct we_set { struct event_set_functions func; bool fast; HANDLE *events; struct event_set_return *esr; int n_events; int capacity; }; static inline void we_set_event(struct we_set *wes, int i, event_t event, unsigned int rwflags, void *arg) { ASSERT(i >= 0 && i < wes->capacity); if (rwflags == EVENT_READ) { ASSERT(event->read != NULL); wes->events[i] = event->read; } else if (rwflags == EVENT_WRITE) { ASSERT(event->write != NULL); wes->events[i] = event->write; } else { msg(M_FATAL, "fatal error in we_set_events: rwflags=%d", rwflags); } wes->esr[i].rwflags = rwflags; wes->esr[i].arg = arg; } static inline bool we_append_event(struct we_set *wes, event_t event, unsigned int rwflags, void *arg) { if (rwflags & EVENT_WRITE) { if (wes->n_events < wes->capacity) { we_set_event(wes, wes->n_events, event, EVENT_WRITE, arg); ++wes->n_events; } else { return false; } } if (rwflags & EVENT_READ) { if (wes->n_events < wes->capacity) { we_set_event(wes, wes->n_events, event, EVENT_READ, arg); ++wes->n_events; } else { return false; } } return true; } static void we_del_event(struct we_set *wes, event_t event) { int i, j = 0; const int len = wes->n_events; for (i = 0; i < len; ++i) { const HANDLE h = wes->events[i]; if (h == event->read || h == event->write) { --wes->n_events; } else { if (i != j) { wes->events[j] = wes->events[i]; wes->esr[j] = wes->esr[i]; } ++j; } } } static void we_del_index(struct we_set *wes, int index) { int i; ASSERT(index >= 0 && index < wes->n_events); for (i = index; i < wes->n_events - 1; ++i) { wes->events[i] = wes->events[i+1]; wes->esr[i] = wes->esr[i+1]; } --wes->n_events; } static void we_get_rw_indices(struct we_set *wes, event_t event, int *ri, int *wi) { int i; *ri = *wi = -1; for (i = 0; i < wes->n_events; ++i) { const HANDLE h = wes->events[i]; if (h == event->read) { ASSERT(*ri == -1); *ri = i; } else if (h == event->write) { ASSERT(*wi == -1); *wi = i; } } } static void we_free(struct event_set *es) { struct we_set *wes = (struct we_set *) es; free(wes->events); free(wes->esr); free(wes); } static void we_reset(struct event_set *es) { struct we_set *wes = (struct we_set *) es; ASSERT(wes->fast); wes->n_events = 0; } static void we_del(struct event_set *es, event_t event) { struct we_set *wes = (struct we_set *) es; ASSERT(!wes->fast); we_del_event(wes, event); } static void we_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) { struct we_set *wes = (struct we_set *) es; dmsg(D_EVENT_WAIT, "WE_CTL n=%d ev=%p rwflags=0x%04x arg=" ptr_format, wes->n_events, event, rwflags, (ptr_type)arg); if (wes->fast) { if (!we_append_event(wes, event, rwflags, arg)) { goto err; } } else { int ri, wi; int one = -1; int n = 0; we_get_rw_indices(wes, event, &ri, &wi); if (wi >= 0) { one = wi; ++n; } if (ri >= 0) { one = ri; ++n; } switch (rwflags) { case 0: switch (n) { case 0: break; case 1: we_del_index(wes, one); break; case 2: we_del_event(wes, event); break; default: ASSERT(0); } break; case EVENT_READ: switch (n) { case 0: if (!we_append_event(wes, event, EVENT_READ, arg)) { goto err; } break; case 1: we_set_event(wes, one, event, EVENT_READ, arg); break; case 2: we_del_index(wes, wi); break; default: ASSERT(0); } break; case EVENT_WRITE: switch (n) { case 0: if (!we_append_event(wes, event, EVENT_WRITE, arg)) { goto err; } break; case 1: we_set_event(wes, one, event, EVENT_WRITE, arg); break; case 2: we_del_index(wes, ri); break; default: ASSERT(0); } break; case EVENT_READ|EVENT_WRITE: switch (n) { case 0: if (!we_append_event(wes, event, EVENT_READ|EVENT_WRITE, arg)) { goto err; } break; case 1: if (ri == -1) { ASSERT(wi != -1); if (!we_append_event(wes, event, EVENT_READ, arg)) { goto err; } } else if (wi == -1) { if (!we_append_event(wes, event, EVENT_WRITE, arg)) { goto err; } } else { ASSERT(0); } break; case 2: break; default: ASSERT(0); } break; default: msg(M_FATAL, "fatal error in we_ctl: rwflags=%d", rwflags); } } return; err: msg(D_EVENT_ERRORS, "Error: Windows resource limit WSA_MAXIMUM_WAIT_EVENTS (%d) has been exceeded", WSA_MAXIMUM_WAIT_EVENTS); } static int we_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) { struct we_set *wes = (struct we_set *) es; const int timeout = tv_to_ms_timeout(tv); DWORD status; dmsg(D_EVENT_WAIT, "WE_WAIT enter n=%d to=%d", wes->n_events, timeout); #ifdef ENABLE_DEBUG if (check_debug_level(D_EVENT_WAIT)) { int i; for (i = 0; i < wes->n_events; ++i) { dmsg(D_EVENT_WAIT, "[%d] ev=%p rwflags=0x%04x arg=" ptr_format, i, wes->events[i], wes->esr[i].rwflags, (ptr_type)wes->esr[i].arg); } } #endif /* * First poll our event list with 0 timeout */ status = WSAWaitForMultipleEvents( (DWORD) wes->n_events, wes->events, FALSE, (DWORD) 0, FALSE); /* * If at least one event is already set, we must * individually poll the whole list. */ if (status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events) { int i; int j = 0; for (i = 0; i < wes->n_events; ++i) { if (j >= outlen) { break; } if (WaitForSingleObject(wes->events[i], 0) == WAIT_OBJECT_0) { *out = wes->esr[i]; dmsg(D_EVENT_WAIT, "WE_WAIT leave [%d,%d] rwflags=0x%04x arg=" ptr_format, i, j, out->rwflags, (ptr_type)out->arg); ++j; ++out; } } return j; } else { /* * If caller specified timeout > 0, we know at this point * that no events are set, so wait only for the first event * (or timeout) and return at most one event_set_return object. * * If caller specified timeout == 0, the second call to * WSAWaitForMultipleEvents would be redundant -- just * return 0 indicating timeout. */ if (timeout > 0) { status = WSAWaitForMultipleEvents( (DWORD) wes->n_events, wes->events, FALSE, (DWORD) timeout, FALSE); } if (outlen >= 1 && status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events) { *out = wes->esr[status - WSA_WAIT_EVENT_0]; dmsg(D_EVENT_WAIT, "WE_WAIT leave rwflags=0x%04x arg=" ptr_format, out->rwflags, (ptr_type)out->arg); return 1; } else if (status == WSA_WAIT_TIMEOUT) { return 0; } else { return -1; } } } static struct event_set * we_init(int *maxevents, unsigned int flags) { struct we_set *wes; dmsg(D_EVENT_WAIT, "WE_INIT maxevents=%d flags=0x%08x", *maxevents, flags); ALLOC_OBJ_CLEAR(wes, struct we_set); /* set dispatch functions */ wes->func.free = we_free; wes->func.reset = we_reset; wes->func.del = we_del; wes->func.ctl = we_ctl; wes->func.wait = we_wait; if (flags & EVENT_METHOD_FAST) { wes->fast = true; } wes->n_events = 0; /* Figure our event capacity */ ASSERT(*maxevents > 0); wes->capacity = min_int(*maxevents * 2, WSA_MAXIMUM_WAIT_EVENTS); *maxevents = min_int(*maxevents, WSA_MAXIMUM_WAIT_EVENTS); /* Allocate space for Win32 event handles */ ALLOC_ARRAY_CLEAR(wes->events, HANDLE, wes->capacity); /* Allocate space for event_set_return objects */ ALLOC_ARRAY_CLEAR(wes->esr, struct event_set_return, wes->capacity); dmsg(D_EVENT_WAIT, "WE_INIT maxevents=%d capacity=%d", *maxevents, wes->capacity); return (struct event_set *) wes; } #endif /* _WIN32 */ #if EPOLL struct ep_set { struct event_set_functions func; bool fast; int epfd; int maxevents; struct epoll_event *events; }; static void ep_free(struct event_set *es) { struct ep_set *eps = (struct ep_set *) es; close(eps->epfd); free(eps->events); free(eps); } static void ep_reset(struct event_set *es) { const struct ep_set *eps = (struct ep_set *) es; ASSERT(eps->fast); } static void ep_del(struct event_set *es, event_t event) { struct epoll_event ev; struct ep_set *eps = (struct ep_set *) es; dmsg(D_EVENT_WAIT, "EP_DEL ev=%d", (int)event); ASSERT(!eps->fast); CLEAR(ev); epoll_ctl(eps->epfd, EPOLL_CTL_DEL, event, &ev); } static void ep_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) { struct ep_set *eps = (struct ep_set *) es; struct epoll_event ev; CLEAR(ev); ev.data.ptr = arg; if (rwflags & EVENT_READ) { ev.events |= EPOLLIN; } if (rwflags & EVENT_WRITE) { ev.events |= EPOLLOUT; } dmsg(D_EVENT_WAIT, "EP_CTL fd=%d rwflags=0x%04x ev=0x%08x arg=" ptr_format, (int)event, rwflags, (unsigned int)ev.events, (ptr_type)ev.data.ptr); if (epoll_ctl(eps->epfd, EPOLL_CTL_MOD, event, &ev) < 0) { if (errno == ENOENT) { if (epoll_ctl(eps->epfd, EPOLL_CTL_ADD, event, &ev) < 0) { msg(M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed, sd=%d", (int)event); } } else { msg(M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed, sd=%d", (int)event); } } } static int ep_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) { struct ep_set *eps = (struct ep_set *) es; int stat; if (outlen > eps->maxevents) { outlen = eps->maxevents; } stat = epoll_wait(eps->epfd, eps->events, outlen, tv_to_ms_timeout(tv)); ASSERT(stat <= outlen); if (stat > 0) { int i; const struct epoll_event *ev = eps->events; struct event_set_return *esr = out; for (i = 0; i < stat; ++i) { esr->rwflags = 0; if (ev->events & (EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP)) { esr->rwflags |= EVENT_READ; } if (ev->events & EPOLLOUT) { esr->rwflags |= EVENT_WRITE; } esr->arg = ev->data.ptr; dmsg(D_EVENT_WAIT, "EP_WAIT[%d] rwflags=0x%04x ev=0x%08x arg=" ptr_format, i, esr->rwflags, ev->events, (ptr_type)ev->data.ptr); ++ev; ++esr; } } return stat; } static struct event_set * ep_init(int *maxevents, unsigned int flags) { struct ep_set *eps; int fd; dmsg(D_EVENT_WAIT, "EP_INIT maxevents=%d flags=0x%08x", *maxevents, flags); /* open epoll file descriptor */ fd = epoll_create(*maxevents); if (fd < 0) { return NULL; } set_cloexec(fd); ALLOC_OBJ_CLEAR(eps, struct ep_set); /* set dispatch functions */ eps->func.free = ep_free; eps->func.reset = ep_reset; eps->func.del = ep_del; eps->func.ctl = ep_ctl; eps->func.wait = ep_wait; /* fast method ("sort of") corresponds to epoll one-shot */ if (flags & EVENT_METHOD_FAST) { eps->fast = true; } /* allocate space for epoll_wait return */ ASSERT(*maxevents > 0); eps->maxevents = *maxevents; ALLOC_ARRAY_CLEAR(eps->events, struct epoll_event, eps->maxevents); /* set epoll control fd */ eps->epfd = fd; return (struct event_set *) eps; } #endif /* EPOLL */ #if POLL struct po_set { struct event_set_functions func; bool fast; struct pollfd *events; void **args; int n_events; int capacity; }; static void po_free(struct event_set *es) { struct po_set *pos = (struct po_set *) es; free(pos->events); free(pos->args); free(pos); } static void po_reset(struct event_set *es) { struct po_set *pos = (struct po_set *) es; ASSERT(pos->fast); pos->n_events = 0; } static void po_del(struct event_set *es, event_t event) { struct po_set *pos = (struct po_set *) es; int i; dmsg(D_EVENT_WAIT, "PO_DEL ev=%d", (int)event); ASSERT(!pos->fast); for (i = 0; i < pos->n_events; ++i) { if (pos->events[i].fd == event) { int j; for (j = i; j < pos->n_events - 1; ++j) { pos->events[j] = pos->events[j+1]; pos->args[j] = pos->args[j+1]; } --pos->n_events; break; } } } static inline void po_set_pollfd_events(struct pollfd *pfdp, unsigned int rwflags) { pfdp->events = 0; if (rwflags & EVENT_WRITE) { pfdp->events |= POLLOUT; } if (rwflags & EVENT_READ) { pfdp->events |= (POLLIN|POLLPRI); } } static inline bool po_append_event(struct po_set *pos, event_t event, unsigned int rwflags, void *arg) { if (pos->n_events < pos->capacity) { struct pollfd *pfdp = &pos->events[pos->n_events]; pfdp->fd = event; pos->args[pos->n_events] = arg; po_set_pollfd_events(pfdp, rwflags); ++pos->n_events; return true; } else { return false; } } static void po_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) { struct po_set *pos = (struct po_set *) es; dmsg(D_EVENT_WAIT, "PO_CTL rwflags=0x%04x ev=%d arg=" ptr_format, rwflags, (int)event, (ptr_type)arg); if (pos->fast) { if (!po_append_event(pos, event, rwflags, arg)) { goto err; } } else { int i; for (i = 0; i < pos->n_events; ++i) { struct pollfd *pfdp = &pos->events[i]; if (pfdp->fd == event) { pos->args[i] = arg; po_set_pollfd_events(pfdp, rwflags); goto done; } } if (!po_append_event(pos, event, rwflags, arg)) { goto err; } } done: return; err: msg(D_EVENT_ERRORS, "Error: poll: too many I/O wait events"); } static int po_wait(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) { struct po_set *pos = (struct po_set *) es; int stat; stat = poll(pos->events, pos->n_events, tv_to_ms_timeout(tv)); ASSERT(stat <= pos->n_events); if (stat > 0) { int i, j = 0; const struct pollfd *pfdp = pos->events; for (i = 0; i < pos->n_events && j < outlen; ++i) { if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP|POLLOUT)) { out->rwflags = 0; if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP)) { out->rwflags |= EVENT_READ; } if (pfdp->revents & POLLOUT) { out->rwflags |= EVENT_WRITE; } out->arg = pos->args[i]; dmsg(D_EVENT_WAIT, "PO_WAIT[%d,%d] fd=%d rev=0x%08x rwflags=0x%04x arg=" ptr_format " %s", i, j, pfdp->fd, pfdp->revents, out->rwflags, (ptr_type)out->arg, pos->fast ? "" : "[scalable]"); ++out; ++j; } else if (pfdp->revents) { msg(D_EVENT_ERRORS, "Error: poll: unknown revents=0x%04x", (unsigned int)pfdp->revents); } ++pfdp; } return j; } return stat; } static struct event_set * po_init(int *maxevents, unsigned int flags) { struct po_set *pos; dmsg(D_EVENT_WAIT, "PO_INIT maxevents=%d flags=0x%08x", *maxevents, flags); ALLOC_OBJ_CLEAR(pos, struct po_set); /* set dispatch functions */ pos->func.free = po_free; pos->func.reset = po_reset; pos->func.del = po_del; pos->func.ctl = po_ctl; pos->func.wait = po_wait; if (flags & EVENT_METHOD_FAST) { pos->fast = true; } pos->n_events = 0; /* Figure our event capacity */ ASSERT(*maxevents > 0); pos->capacity = *maxevents; /* Allocate space for pollfd structures to be passed to poll() */ ALLOC_ARRAY_CLEAR(pos->events, struct pollfd, pos->capacity); /* Allocate space for event_set_return objects */ ALLOC_ARRAY_CLEAR(pos->args, void *, pos->capacity); return (struct event_set *) pos; } #endif /* POLL */ #if SELECT struct se_set { struct event_set_functions func; bool fast; fd_set readfds; fd_set writefds; void **args; /* allocated to capacity size */ int maxfd; /* largest fd seen so far, always < capacity */ int capacity; /* fixed largest fd + 1 */ }; static void se_free(struct event_set *es) { struct se_set *ses = (struct se_set *) es; free(ses->args); free(ses); } static void se_reset(struct event_set *es) { struct se_set *ses = (struct se_set *) es; int i; ASSERT(ses->fast); dmsg(D_EVENT_WAIT, "SE_RESET"); FD_ZERO(&ses->readfds); FD_ZERO(&ses->writefds); for (i = 0; i <= ses->maxfd; ++i) { ses->args[i] = NULL; } ses->maxfd = -1; } static void se_del(struct event_set *es, event_t event) { struct se_set *ses = (struct se_set *) es; ASSERT(!ses->fast); dmsg(D_EVENT_WAIT, "SE_DEL ev=%d", (int)event); if (event >= 0 && event < ses->capacity) { FD_CLR(event, &ses->readfds); FD_CLR(event, &ses->writefds); ses->args[event] = NULL; } else { msg(D_EVENT_ERRORS, "Error: select/se_del: too many I/O wait events"); } return; } static void se_ctl(struct event_set *es, event_t event, unsigned int rwflags, void *arg) { struct se_set *ses = (struct se_set *) es; dmsg(D_EVENT_WAIT, "SE_CTL rwflags=0x%04x ev=%d fast=%d cap=%d maxfd=%d arg=" ptr_format, rwflags, (int)event, (int)ses->fast, ses->capacity, ses->maxfd, (ptr_type)arg); if (event >= 0 && event < ses->capacity) { ses->maxfd = max_int(event, ses->maxfd); ses->args[event] = arg; if (ses->fast) { if (rwflags & EVENT_READ) { openvpn_fd_set(event, &ses->readfds); } if (rwflags & EVENT_WRITE) { openvpn_fd_set(event, &ses->writefds); } } else { if (rwflags & EVENT_READ) { openvpn_fd_set(event, &ses->readfds); } else { FD_CLR(event, &ses->readfds); } if (rwflags & EVENT_WRITE) { openvpn_fd_set(event, &ses->writefds); } else { FD_CLR(event, &ses->writefds); } } } else { msg(D_EVENT_ERRORS, "Error: select: too many I/O wait events, fd=%d cap=%d", (int) event, ses->capacity); } } static int se_wait_return(struct se_set *ses, fd_set *read, fd_set *write, struct event_set_return *out, int outlen) { int i, j = 0; for (i = 0; i <= ses->maxfd && j < outlen; ++i) { const bool r = FD_ISSET(i, read); const bool w = FD_ISSET(i, write); if (r || w) { out->rwflags = 0; if (r) { out->rwflags |= EVENT_READ; } if (w) { out->rwflags |= EVENT_WRITE; } out->arg = ses->args[i]; dmsg(D_EVENT_WAIT, "SE_WAIT[%d,%d] rwflags=0x%04x arg=" ptr_format, i, j, out->rwflags, (ptr_type)out->arg); ++out; ++j; } } return j; } static int se_wait_fast(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) { struct se_set *ses = (struct se_set *) es; struct timeval tv_tmp = *tv; int stat; dmsg(D_EVENT_WAIT, "SE_WAIT_FAST maxfd=%d tv=%lld/%ld", ses->maxfd, (long long)tv_tmp.tv_sec, (long)tv_tmp.tv_usec); stat = select(ses->maxfd + 1, &ses->readfds, &ses->writefds, NULL, &tv_tmp); if (stat > 0) { stat = se_wait_return(ses, &ses->readfds, &ses->writefds, out, outlen); } return stat; } static int se_wait_scalable(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen) { struct se_set *ses = (struct se_set *) es; struct timeval tv_tmp = *tv; fd_set read = ses->readfds; fd_set write = ses->writefds; int stat; dmsg(D_EVENT_WAIT, "SE_WAIT_SCALEABLE maxfd=%d tv=%lld/%ld", ses->maxfd, (long long)tv_tmp.tv_sec, (long)tv_tmp.tv_usec); stat = select(ses->maxfd + 1, &read, &write, NULL, &tv_tmp); if (stat > 0) { stat = se_wait_return(ses, &read, &write, out, outlen); } return stat; } static struct event_set * se_init(int *maxevents, unsigned int flags) { struct se_set *ses; dmsg(D_EVENT_WAIT, "SE_INIT maxevents=%d flags=0x%08x", *maxevents, flags); ALLOC_OBJ_CLEAR(ses, struct se_set); /* set dispatch functions */ ses->func.free = se_free; ses->func.reset = se_reset; ses->func.del = se_del; ses->func.ctl = se_ctl; ses->func.wait = se_wait_scalable; if (flags & EVENT_METHOD_FAST) { ses->fast = true; ses->func.wait = se_wait_fast; } /* Select needs to be passed this value + 1 */ ses->maxfd = -1; /* Set our event capacity */ ASSERT(*maxevents > 0); *maxevents = min_int(*maxevents, SELECT_MAX_FDS); ses->capacity = SELECT_MAX_FDS; /* Allocate space for event_set_return void * args */ ALLOC_ARRAY_CLEAR(ses->args, void *, ses->capacity); return (struct event_set *) ses; } #endif /* SELECT */ static struct event_set * event_set_init_simple(int *maxevents, unsigned int flags) { struct event_set *ret = NULL; #ifdef _WIN32 ret = we_init(maxevents, flags); #elif POLL && SELECT #if 0 /* Define to 1 if EVENT_METHOD_US_TIMEOUT should cause select to be favored over poll */ if (flags & EVENT_METHOD_US_TIMEOUT) { ret = se_init(maxevents, flags); } #endif #ifdef SELECT_PREFERRED_OVER_POLL if (!ret) { ret = se_init(maxevents, flags); } if (!ret) { ret = po_init(maxevents, flags); } #else /* ifdef SELECT_PREFERRED_OVER_POLL */ if (!ret) { ret = po_init(maxevents, flags); } if (!ret) { ret = se_init(maxevents, flags); } #endif #elif POLL ret = po_init(maxevents, flags); #elif SELECT ret = se_init(maxevents, flags); #else /* ifdef _WIN32 */ #error At least one of poll, select, or WSAWaitForMultipleEvents must be supported by the kernel #endif /* ifdef _WIN32 */ ASSERT(ret); return ret; } static struct event_set * event_set_init_scalable(int *maxevents, unsigned int flags) { struct event_set *ret = NULL; #if EPOLL ret = ep_init(maxevents, flags); if (!ret) { msg(M_WARN, "Note: sys_epoll API is unavailable, falling back to poll/select API"); ret = event_set_init_simple(maxevents, flags); } #else /* if EPOLL */ ret = event_set_init_simple(maxevents, flags); #endif ASSERT(ret); return ret; } struct event_set * event_set_init(int *maxevents, unsigned int flags) { if (flags & EVENT_METHOD_FAST) { return event_set_init_simple(maxevents, flags); } else { return event_set_init_scalable(maxevents, flags); } }