162ac0c33Sjakob /*
262ac0c33Sjakob * netio.c -- network I/O support.
362ac0c33Sjakob *
4d3fecca9Ssthen * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob *
662ac0c33Sjakob * See LICENSE for the license.
762ac0c33Sjakob *
862ac0c33Sjakob */
9aee1b7aaSsthen #include "config.h"
1062ac0c33Sjakob
1162ac0c33Sjakob #include <assert.h>
1262ac0c33Sjakob #include <errno.h>
1362ac0c33Sjakob #include <sys/time.h>
1462ac0c33Sjakob #include <string.h>
1562ac0c33Sjakob #include <stdlib.h>
163126abd5Ssthen #include <poll.h>
1762ac0c33Sjakob
1862ac0c33Sjakob #include "netio.h"
1962ac0c33Sjakob #include "util.h"
2062ac0c33Sjakob
213126abd5Ssthen #define MAX_NETIO_FDS 1024
2262ac0c33Sjakob
2362ac0c33Sjakob netio_type *
netio_create(region_type * region)2462ac0c33Sjakob netio_create(region_type *region)
2562ac0c33Sjakob {
2662ac0c33Sjakob netio_type *result;
2762ac0c33Sjakob
2862ac0c33Sjakob assert(region);
2962ac0c33Sjakob
3062ac0c33Sjakob result = (netio_type *) region_alloc(region, sizeof(netio_type));
3162ac0c33Sjakob result->region = region;
3262ac0c33Sjakob result->handlers = NULL;
3362ac0c33Sjakob result->deallocated = NULL;
3462ac0c33Sjakob result->dispatch_next = NULL;
3562ac0c33Sjakob return result;
3662ac0c33Sjakob }
3762ac0c33Sjakob
3862ac0c33Sjakob void
netio_add_handler(netio_type * netio,netio_handler_type * handler)3962ac0c33Sjakob netio_add_handler(netio_type *netio, netio_handler_type *handler)
4062ac0c33Sjakob {
4162ac0c33Sjakob netio_handler_list_type *elt;
4262ac0c33Sjakob
4362ac0c33Sjakob assert(netio);
4462ac0c33Sjakob assert(handler);
4562ac0c33Sjakob
4662ac0c33Sjakob if (netio->deallocated) {
4762ac0c33Sjakob /*
4862ac0c33Sjakob * If we have deallocated handler list elements, reuse
4962ac0c33Sjakob * the first one.
5062ac0c33Sjakob */
5162ac0c33Sjakob elt = netio->deallocated;
5262ac0c33Sjakob netio->deallocated = elt->next;
5362ac0c33Sjakob } else {
5462ac0c33Sjakob /*
5562ac0c33Sjakob * Allocate a new one.
5662ac0c33Sjakob */
5762ac0c33Sjakob elt = (netio_handler_list_type *) region_alloc(
5862ac0c33Sjakob netio->region, sizeof(netio_handler_list_type));
5962ac0c33Sjakob }
6062ac0c33Sjakob
6162ac0c33Sjakob elt->next = netio->handlers;
6262ac0c33Sjakob elt->handler = handler;
633126abd5Ssthen elt->handler->pfd = -1;
6462ac0c33Sjakob netio->handlers = elt;
6562ac0c33Sjakob }
6662ac0c33Sjakob
6762ac0c33Sjakob void
netio_remove_handler(netio_type * netio,netio_handler_type * handler)6862ac0c33Sjakob netio_remove_handler(netio_type *netio, netio_handler_type *handler)
6962ac0c33Sjakob {
7062ac0c33Sjakob netio_handler_list_type **elt_ptr;
7162ac0c33Sjakob
7262ac0c33Sjakob assert(netio);
7362ac0c33Sjakob assert(handler);
7462ac0c33Sjakob
7562ac0c33Sjakob for (elt_ptr = &netio->handlers; *elt_ptr; elt_ptr = &(*elt_ptr)->next) {
7662ac0c33Sjakob if ((*elt_ptr)->handler == handler) {
7762ac0c33Sjakob netio_handler_list_type *next = (*elt_ptr)->next;
7862ac0c33Sjakob if ((*elt_ptr) == netio->dispatch_next)
7962ac0c33Sjakob netio->dispatch_next = next;
8062ac0c33Sjakob (*elt_ptr)->handler = NULL;
8162ac0c33Sjakob (*elt_ptr)->next = netio->deallocated;
8262ac0c33Sjakob netio->deallocated = *elt_ptr;
8362ac0c33Sjakob *elt_ptr = next;
8462ac0c33Sjakob break;
8562ac0c33Sjakob }
8662ac0c33Sjakob }
8762ac0c33Sjakob }
8862ac0c33Sjakob
8962ac0c33Sjakob const struct timespec *
netio_current_time(netio_type * netio)9062ac0c33Sjakob netio_current_time(netio_type *netio)
9162ac0c33Sjakob {
9262ac0c33Sjakob assert(netio);
9362ac0c33Sjakob
9462ac0c33Sjakob if (!netio->have_current_time) {
9562ac0c33Sjakob struct timeval current_timeval;
9662ac0c33Sjakob if (gettimeofday(¤t_timeval, NULL) == -1) {
97*ee5153b7Sflorian log_msg(LOG_ERR, "gettimeofday: %s, aborting.", strerror(errno));
9862ac0c33Sjakob abort();
9962ac0c33Sjakob }
10062ac0c33Sjakob timeval_to_timespec(&netio->cached_current_time, ¤t_timeval);
10162ac0c33Sjakob netio->have_current_time = 1;
10262ac0c33Sjakob }
10362ac0c33Sjakob
10462ac0c33Sjakob return &netio->cached_current_time;
10562ac0c33Sjakob }
10662ac0c33Sjakob
10762ac0c33Sjakob int
netio_dispatch(netio_type * netio,const struct timespec * timeout,const sigset_t * sigmask)10862ac0c33Sjakob netio_dispatch(netio_type *netio, const struct timespec *timeout, const sigset_t *sigmask)
10962ac0c33Sjakob {
1103126abd5Ssthen /* static arrays to avoid allocation */
1113126abd5Ssthen static struct pollfd fds[MAX_NETIO_FDS];
1123126abd5Ssthen int numfd;
11362ac0c33Sjakob int have_timeout = 0;
11462ac0c33Sjakob struct timespec minimum_timeout;
11562ac0c33Sjakob netio_handler_type *timeout_handler = NULL;
11662ac0c33Sjakob netio_handler_list_type *elt;
11762ac0c33Sjakob int rc;
11862ac0c33Sjakob int result = 0;
1193126abd5Ssthen #ifndef HAVE_PPOLL
1203126abd5Ssthen sigset_t origmask;
1213126abd5Ssthen #endif
12262ac0c33Sjakob
12362ac0c33Sjakob assert(netio);
12462ac0c33Sjakob
12562ac0c33Sjakob /*
12662ac0c33Sjakob * Clear the cached current time.
12762ac0c33Sjakob */
12862ac0c33Sjakob netio->have_current_time = 0;
12962ac0c33Sjakob
13062ac0c33Sjakob /*
13162ac0c33Sjakob * Initialize the minimum timeout with the timeout parameter.
13262ac0c33Sjakob */
13362ac0c33Sjakob if (timeout) {
13462ac0c33Sjakob have_timeout = 1;
13562ac0c33Sjakob memcpy(&minimum_timeout, timeout, sizeof(struct timespec));
13662ac0c33Sjakob }
13762ac0c33Sjakob
13862ac0c33Sjakob /*
13962ac0c33Sjakob * Initialize the fd_sets and timeout based on the handler
14062ac0c33Sjakob * information.
14162ac0c33Sjakob */
1423126abd5Ssthen numfd = 0;
14362ac0c33Sjakob
14462ac0c33Sjakob for (elt = netio->handlers; elt; elt = elt->next) {
14562ac0c33Sjakob netio_handler_type *handler = elt->handler;
1463126abd5Ssthen if (handler->fd != -1 && numfd < MAX_NETIO_FDS) {
1473126abd5Ssthen fds[numfd].fd = handler->fd;
1483126abd5Ssthen fds[numfd].events = 0;
1493126abd5Ssthen fds[numfd].revents = 0;
1503126abd5Ssthen handler->pfd = numfd;
15162ac0c33Sjakob if (handler->event_types & NETIO_EVENT_READ) {
1523126abd5Ssthen fds[numfd].events |= POLLIN;
15362ac0c33Sjakob }
15462ac0c33Sjakob if (handler->event_types & NETIO_EVENT_WRITE) {
1553126abd5Ssthen fds[numfd].events |= POLLOUT;
15662ac0c33Sjakob }
1573126abd5Ssthen numfd++;
1583126abd5Ssthen } else {
1593126abd5Ssthen handler->pfd = -1;
16062ac0c33Sjakob }
16162ac0c33Sjakob if (handler->timeout && (handler->event_types & NETIO_EVENT_TIMEOUT)) {
16262ac0c33Sjakob struct timespec relative;
16362ac0c33Sjakob
16462ac0c33Sjakob relative.tv_sec = handler->timeout->tv_sec;
16562ac0c33Sjakob relative.tv_nsec = handler->timeout->tv_nsec;
16662ac0c33Sjakob timespec_subtract(&relative, netio_current_time(netio));
16762ac0c33Sjakob
16862ac0c33Sjakob if (!have_timeout ||
16962ac0c33Sjakob timespec_compare(&relative, &minimum_timeout) < 0)
17062ac0c33Sjakob {
17162ac0c33Sjakob have_timeout = 1;
17262ac0c33Sjakob minimum_timeout.tv_sec = relative.tv_sec;
17362ac0c33Sjakob minimum_timeout.tv_nsec = relative.tv_nsec;
17462ac0c33Sjakob timeout_handler = handler;
17562ac0c33Sjakob }
17662ac0c33Sjakob }
17762ac0c33Sjakob }
17862ac0c33Sjakob
17962ac0c33Sjakob if (have_timeout && minimum_timeout.tv_sec < 0) {
18062ac0c33Sjakob /*
1813126abd5Ssthen * On negative timeout for a handler, immediately
18262ac0c33Sjakob * dispatch the timeout event without checking for
18362ac0c33Sjakob * other events.
18462ac0c33Sjakob */
18562ac0c33Sjakob if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
18662ac0c33Sjakob timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
18762ac0c33Sjakob }
18862ac0c33Sjakob return result;
18962ac0c33Sjakob }
19062ac0c33Sjakob
19162ac0c33Sjakob /* Check for events. */
1923126abd5Ssthen #ifdef HAVE_PPOLL
1933126abd5Ssthen rc = ppoll(fds, numfd, (have_timeout?&minimum_timeout:NULL), sigmask);
1943126abd5Ssthen #else
1953126abd5Ssthen sigprocmask(SIG_SETMASK, sigmask, &origmask);
1963126abd5Ssthen rc = poll(fds, numfd, (have_timeout?minimum_timeout.tv_sec*1000+
1973126abd5Ssthen minimum_timeout.tv_nsec/1000000:-1));
1983126abd5Ssthen sigprocmask(SIG_SETMASK, &origmask, NULL);
1993126abd5Ssthen #endif /* HAVE_PPOLL */
20062ac0c33Sjakob if (rc == -1) {
20162ac0c33Sjakob if(errno == EINVAL || errno == EACCES || errno == EBADF) {
2023126abd5Ssthen log_msg(LOG_ERR, "fatal error poll: %s.",
20362ac0c33Sjakob strerror(errno));
20462ac0c33Sjakob exit(1);
20562ac0c33Sjakob }
20662ac0c33Sjakob return -1;
20762ac0c33Sjakob }
20862ac0c33Sjakob
20962ac0c33Sjakob /*
21062ac0c33Sjakob * Clear the cached current_time (pselect(2) may block for
21162ac0c33Sjakob * some time so the cached value is likely to be old).
21262ac0c33Sjakob */
21362ac0c33Sjakob netio->have_current_time = 0;
21462ac0c33Sjakob
21562ac0c33Sjakob if (rc == 0) {
21662ac0c33Sjakob /*
21762ac0c33Sjakob * No events before the minimum timeout expired.
21862ac0c33Sjakob * Dispatch to handler if interested.
21962ac0c33Sjakob */
22062ac0c33Sjakob if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
22162ac0c33Sjakob timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
22262ac0c33Sjakob }
22362ac0c33Sjakob } else {
22462ac0c33Sjakob /*
22562ac0c33Sjakob * Dispatch all the events to interested handlers
22662ac0c33Sjakob * based on the fd_sets. Note that a handler might
22762ac0c33Sjakob * deinstall itself, so store the next handler before
22862ac0c33Sjakob * calling the current handler!
22962ac0c33Sjakob */
23062ac0c33Sjakob assert(netio->dispatch_next == NULL);
2313126abd5Ssthen
23262ac0c33Sjakob for (elt = netio->handlers; elt && rc; ) {
23362ac0c33Sjakob netio_handler_type *handler = elt->handler;
23462ac0c33Sjakob netio->dispatch_next = elt->next;
2353126abd5Ssthen if (handler->fd != -1 && handler->pfd != -1) {
23662ac0c33Sjakob netio_event_types_type event_types
23762ac0c33Sjakob = NETIO_EVENT_NONE;
2383126abd5Ssthen if ((fds[handler->pfd].revents & POLLIN)) {
23962ac0c33Sjakob event_types |= NETIO_EVENT_READ;
24062ac0c33Sjakob }
2413126abd5Ssthen if ((fds[handler->pfd].revents & POLLOUT)) {
24262ac0c33Sjakob event_types |= NETIO_EVENT_WRITE;
24362ac0c33Sjakob }
2443126abd5Ssthen if ((fds[handler->pfd].revents &
2453126abd5Ssthen (POLLNVAL|POLLHUP|POLLERR))) {
2463126abd5Ssthen /* closed/error: give a read event,
2473126abd5Ssthen * or otherwise, a write event */
2483126abd5Ssthen if((handler->event_types&NETIO_EVENT_READ))
2493126abd5Ssthen event_types |= NETIO_EVENT_READ;
2503126abd5Ssthen else if((handler->event_types&NETIO_EVENT_WRITE))
2513126abd5Ssthen event_types |= NETIO_EVENT_WRITE;
25262ac0c33Sjakob }
25362ac0c33Sjakob
25462ac0c33Sjakob if (event_types & handler->event_types) {
25562ac0c33Sjakob handler->event_handler(netio, handler, event_types & handler->event_types);
25662ac0c33Sjakob ++result;
25762ac0c33Sjakob }
25862ac0c33Sjakob }
25962ac0c33Sjakob elt = netio->dispatch_next;
26062ac0c33Sjakob }
26162ac0c33Sjakob netio->dispatch_next = NULL;
26262ac0c33Sjakob }
26362ac0c33Sjakob
26462ac0c33Sjakob return result;
26562ac0c33Sjakob }
266