blob: fcddeb19f56d2375634c0c4f78b7054eaf160c5a [file] [log] [blame]
/*
* 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-2021 OpenVPN Inc <sales@openvpn.net>
*
* 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=%" PRIi64 "/%ld",
ses->maxfd,
(int64_t)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=%" PRIi64 "/%ld",
ses->maxfd, (int64_t)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);
}
}