uhub/src/network/epoll.c

318 lines
7.3 KiB
C
Raw Normal View History

/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
2010-01-18 14:55:40 +00:00
#include "uhub.h"
#ifdef USE_EPOLL
2010-01-18 14:55:40 +00:00
#include "network/connection.h"
#include "network/common.h"
2010-01-17 21:59:51 +00:00
#define EPOLL_EVBUFFER 512
struct net_connection
{
int sd;
uint32_t flags;
net_connection_cb callback;
2010-01-18 14:55:40 +00:00
void* ptr;
struct epoll_event ev;
struct timeout_evt* timeout;
};
2010-01-18 14:55:40 +00:00
static void net_con_print(const char* prefix, struct net_connection* con)
{
char buf[512];
int off = snprintf(buf, 512, "%s: net_connection={ sd=%d, flags=%u, callback=%p, ptr=%p, ev={ events=%s%s, data.ptr=%p }",
prefix, con->sd, con->flags, con->callback, con->ptr, (con->ev.events & EPOLLIN ? "R" : ""),(con->ev.events & EPOLLOUT ? "W" : "") , con->ev.data.ptr);
if (con->timeout)
{
sprintf(buf + off, ", timeout={ %d seconds left }", (int) (time(0) - con->timeout->timestamp));
}
else
{
sprintf(buf + off, ", timeout=NULL");
}
LOG_WARN(buf);
}
2010-01-17 21:59:51 +00:00
struct net_backend
{
int epfd;
size_t num;
size_t max;
struct net_connection** conns;
struct epoll_event events[EPOLL_EVBUFFER];
2010-01-18 14:55:40 +00:00
time_t now;
struct timeout_queue timeout_queue;
2010-01-17 21:59:51 +00:00
};
static struct net_backend* g_backend = 0;
/**
* Initialize the network backend.
* Returns 1 on success, or 0 on failure.
*/
int net_backend_initialize()
{
size_t max = net_get_max_sockets();
g_backend = hub_malloc(sizeof(struct net_backend));
g_backend->epfd = epoll_create(max);
if (g_backend->epfd == -1)
{
LOG_WARN("Unable to create epoll socket.");
return 0;
}
g_backend->num = 0;
g_backend->max = max;
g_backend->conns = hub_malloc_zero(sizeof(struct net_connection*) * max);
memset(g_backend->events, 0, sizeof(g_backend->events));
2010-01-18 14:55:40 +00:00
g_backend->now = time(0);
timeout_queue_initialize(&g_backend->timeout_queue, g_backend->now, 600); /* look max 10 minutes into the future. */
2010-01-17 21:59:51 +00:00
return 1;
}
/**
* Shutdown the network connection backend.
*/
void net_backend_shutdown()
{
close(g_backend->epfd);
hub_free(g_backend->conns);
hub_free(g_backend);
}
/**
* Process the network backend.
*/
int net_backend_process()
{
int n;
2010-01-18 14:55:40 +00:00
LOG_WARN("epoll_wait: fd=%d, events=%x, max=%zu", g_backend->epfd, g_backend->events, MIN(g_backend->num, EPOLL_EVBUFFER));
int res = epoll_wait(g_backend->epfd, g_backend->events, MIN(g_backend->num, EPOLL_EVBUFFER), 1000);
2010-01-17 21:59:51 +00:00
if (res == -1)
{
2010-01-18 14:55:40 +00:00
LOG_WARN("epoll_wait returned -1");
2010-01-17 21:59:51 +00:00
return 0;
}
for (n = 0; n < res; n++)
{
struct net_connection* con = (struct net_connection*) g_backend->events[n].data.ptr;
2010-01-18 14:55:40 +00:00
int ev = 0;
if (g_backend->events[n].events & EPOLLIN) ev |= NET_EVENT_READ;
if (g_backend->events[n].events & EPOLLOUT) ev |= NET_EVENT_WRITE;
con->callback(con, ev, con->ptr);
2010-01-17 21:59:51 +00:00
}
return 1;
}
struct net_connection* net_con_create()
{
struct net_connection* con = (struct net_connection*) hub_malloc_zero(sizeof(struct net_connection));
return con;
}
void net_con_destroy(struct net_connection* con)
{
hub_free(con);
}
void net_con_initialize(struct net_connection* con, int sd, net_connection_cb callback, const void* ptr, int events)
{
con->sd = sd;
con->flags = NET_INITIALIZED;
con->callback = callback;
con->ev.events = 0;
2010-01-18 14:55:40 +00:00
con->ptr = (void*) ptr;
con->ev.data.ptr = (void*) con;
net_set_nonblocking(con->sd, 1);
net_set_nosigpipe(con->sd, 1);
if (events & NET_EVENT_READ) con->ev.events |= EPOLLIN;
if (events & NET_EVENT_WRITE) con->ev.events |= EPOLLOUT;
2010-01-17 21:59:51 +00:00
g_backend->conns[sd] = con;
g_backend->num++;
if (epoll_ctl(g_backend->epfd, EPOLL_CTL_ADD, con->sd, &con->ev) == -1)
{
LOG_WARN("epoll_ctl() add failed.");
}
2010-01-18 14:55:40 +00:00
net_con_print("ADD", con);
}
void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events)
{
con->callback = callback;
2010-01-18 14:55:40 +00:00
con->ptr = (void*) ptr;
net_con_update(con, events);
}
void net_con_update(struct net_connection* con, int events)
{
con->ev.events = 0;
if (events & NET_EVENT_READ) con->ev.events |= EPOLLIN;
if (events & NET_EVENT_WRITE) con->ev.events |= EPOLLOUT;
2010-01-17 21:59:51 +00:00
if (epoll_ctl(g_backend->epfd, EPOLL_CTL_MOD, con->sd, &con->ev) == -1)
{
LOG_WARN("epoll_ctl() modify failed.");
}
2010-01-18 14:55:40 +00:00
net_con_print("MOD", con);
}
int net_con_close(struct net_connection* con)
{
if (!(con->flags & NET_INITIALIZED))
return 0;
con->flags &= ~NET_INITIALIZED;
2010-01-17 21:59:51 +00:00
if (con->sd != -1)
{
g_backend->conns[con->sd] = 0;
g_backend->num--;
}
2010-01-18 14:55:40 +00:00
if (timeout_evt_is_scheduled(con->timeout))
{
timeout_queue_remove(&g_backend->timeout_queue, con->timeout);
hub_free(con->timeout);
con->timeout = 0;
}
2010-01-17 21:59:51 +00:00
if (epoll_ctl(g_backend->epfd, EPOLL_CTL_DEL, con->sd, &con->ev) == -1)
{
LOG_WARN("epoll_ctl() delete failed.");
}
2010-01-18 14:55:40 +00:00
net_con_print("DEL", con);
return 0;
}
int net_con_get_sd(struct net_connection* con)
{
return con->sd;
}
void* net_con_get_ptr(struct net_connection* con)
{
2010-01-18 14:55:40 +00:00
return con->ptr;
}
ssize_t net_con_send(struct net_connection* con, const void* buf, size_t len)
{
int ret = net_send(con->sd, buf, len, UHUB_SEND_SIGNAL);
if (ret == -1)
{
if (net_error() == EWOULDBLOCK || net_error() == EINTR)
return 0;
return -1;
}
return ret;
}
ssize_t net_con_recv(struct net_connection* con, void* buf, size_t len)
{
uhub_assert(con);
#ifdef SSL_SUPPORT
if (!con->ssl)
{
#endif
int ret = net_recv(con->sd, buf, len, 0);
#ifdef NETWORK_DUMP_DEBUG
LOG_PROTO("net_recv: ret=%d", ret);
#endif
if (ret == -1)
{
if (net_error() == EWOULDBLOCK || net_error() == EINTR)
return 0;
return -1;
}
return ret;
#ifdef SSL_SUPPORT
}
else
{
int ret = SSL_read(con->ssl, buf, len);
#ifdef NETWORK_DUMP_DEBUG
LOG_PROTO("net_recv: ret=%d", ret);
#endif
if (ret > 0)
{
con->last_recv = time(0);
net_con_flag_unset(con, NET_WANT_SSL_WRITE);
}
else
{
return handle_openssl_error(con, ret);
}
return ret;
}
#endif
}
ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len)
{
int ret = net_recv(con->sd, buf, len, MSG_PEEK);
if (ret == -1)
{
if (net_error() == EWOULDBLOCK || net_error() == EINTR)
return 0;
return -1;
}
return ret;
}
2010-01-18 14:55:40 +00:00
void timeout_evt_initialize(struct timeout_evt*, timeout_evt_cb, void* ptr);
void timeout_evt_reset(struct timeout_evt*);
int timeout_evt_is_scheduled(struct timeout_evt*);
static void timeout_callback(struct timeout_evt* evt)
{
struct net_connection* con = (struct net_connection*) evt->ptr;
con->callback(con, NET_EVENT_TIMEOUT, con->ptr);
}
void net_con_set_timeout(struct net_connection* con, int seconds)
{
if (!con->timeout)
{
2010-01-18 14:55:40 +00:00
con->timeout = hub_malloc_zero(sizeof(struct timeout_evt));
timeout_evt_initialize(con->timeout, timeout_callback, con);
2010-01-18 14:55:40 +00:00
timeout_queue_insert(&g_backend->timeout_queue, con->timeout, seconds);
}
else
{
timeout_queue_reschedule(&g_backend->timeout_queue, con->timeout, seconds);
}
}
2010-01-17 21:59:51 +00:00
#endif /* USE_EPOLL */