mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-09 13:41:06 +08:00

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@2959 e7ae566f-a301-0410-adde-c780ea21d3b5
1056 lines
23 KiB
C
1056 lines
23 KiB
C
/*
|
|
* 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-2005 OpenVPN Solutions LLC <info@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 (see the file COPYING included with this
|
|
* distribution); if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "syshead.h"
|
|
|
|
#include "buffer.h"
|
|
#include "error.h"
|
|
#include "integer.h"
|
|
#include "event.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=0x%08x rwflags=0x%04x arg=" ptr_format,
|
|
wes->n_events,
|
|
(unsigned int)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=0x%08x rwflags=0x%04x arg=" ptr_format,
|
|
i,
|
|
(unsigned int)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");
|
|
}
|
|
else
|
|
msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed");
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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)
|
|
FD_SET (event, &ses->readfds);
|
|
if (rwflags & EVENT_WRITE)
|
|
FD_SET (event, &ses->writefds);
|
|
}
|
|
else
|
|
{
|
|
if (rwflags & EVENT_READ)
|
|
FD_SET (event, &ses->readfds);
|
|
else
|
|
FD_CLR (event, &ses->readfds);
|
|
if (rwflags & EVENT_WRITE)
|
|
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=%d/%d",
|
|
ses->maxfd,
|
|
(int)tv_tmp.tv_sec,
|
|
(int)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=%d/%d",
|
|
ses->maxfd, (int)tv_tmp.tv_sec, (int)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
|
|
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
|
|
#error At least one of poll, select, or WSAWaitForMultipleEvents must be supported by the kernel
|
|
#endif
|
|
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
|
|
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);
|
|
}
|