1d83a80eeSchristos /*
2d83a80eeSchristos * netio.c -- network I/O support.
3d83a80eeSchristos *
4d83a80eeSchristos * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5d83a80eeSchristos *
6d83a80eeSchristos * See LICENSE for the license.
7d83a80eeSchristos *
8d83a80eeSchristos */
9d83a80eeSchristos #include "config.h"
10d83a80eeSchristos
11d83a80eeSchristos #include <assert.h>
12d83a80eeSchristos #include <errno.h>
13d83a80eeSchristos #include <sys/time.h>
14d83a80eeSchristos #include <string.h>
15d83a80eeSchristos #include <stdlib.h>
16d83a80eeSchristos #include <poll.h>
17d83a80eeSchristos
18d83a80eeSchristos #include "netio.h"
19d83a80eeSchristos #include "util.h"
20d83a80eeSchristos
21d83a80eeSchristos #define MAX_NETIO_FDS 1024
22d83a80eeSchristos
23d83a80eeSchristos netio_type *
netio_create(region_type * region)24d83a80eeSchristos netio_create(region_type *region)
25d83a80eeSchristos {
26d83a80eeSchristos netio_type *result;
27d83a80eeSchristos
28d83a80eeSchristos assert(region);
29d83a80eeSchristos
30d83a80eeSchristos result = (netio_type *) region_alloc(region, sizeof(netio_type));
31d83a80eeSchristos result->region = region;
32d83a80eeSchristos result->handlers = NULL;
33d83a80eeSchristos result->deallocated = NULL;
34d83a80eeSchristos result->dispatch_next = NULL;
35d83a80eeSchristos return result;
36d83a80eeSchristos }
37d83a80eeSchristos
38d83a80eeSchristos void
netio_add_handler(netio_type * netio,netio_handler_type * handler)39d83a80eeSchristos netio_add_handler(netio_type *netio, netio_handler_type *handler)
40d83a80eeSchristos {
41d83a80eeSchristos netio_handler_list_type *elt;
42d83a80eeSchristos
43d83a80eeSchristos assert(netio);
44d83a80eeSchristos assert(handler);
45d83a80eeSchristos
46d83a80eeSchristos if (netio->deallocated) {
47d83a80eeSchristos /*
48d83a80eeSchristos * If we have deallocated handler list elements, reuse
49d83a80eeSchristos * the first one.
50d83a80eeSchristos */
51d83a80eeSchristos elt = netio->deallocated;
52d83a80eeSchristos netio->deallocated = elt->next;
53d83a80eeSchristos } else {
54d83a80eeSchristos /*
55d83a80eeSchristos * Allocate a new one.
56d83a80eeSchristos */
57d83a80eeSchristos elt = (netio_handler_list_type *) region_alloc(
58d83a80eeSchristos netio->region, sizeof(netio_handler_list_type));
59d83a80eeSchristos }
60d83a80eeSchristos
61d83a80eeSchristos elt->next = netio->handlers;
62d83a80eeSchristos elt->handler = handler;
63d83a80eeSchristos elt->handler->pfd = -1;
64d83a80eeSchristos netio->handlers = elt;
65d83a80eeSchristos }
66d83a80eeSchristos
67d83a80eeSchristos void
netio_remove_handler(netio_type * netio,netio_handler_type * handler)68d83a80eeSchristos netio_remove_handler(netio_type *netio, netio_handler_type *handler)
69d83a80eeSchristos {
70d83a80eeSchristos netio_handler_list_type **elt_ptr;
71d83a80eeSchristos
72d83a80eeSchristos assert(netio);
73d83a80eeSchristos assert(handler);
74d83a80eeSchristos
75d83a80eeSchristos for (elt_ptr = &netio->handlers; *elt_ptr; elt_ptr = &(*elt_ptr)->next) {
76d83a80eeSchristos if ((*elt_ptr)->handler == handler) {
77d83a80eeSchristos netio_handler_list_type *next = (*elt_ptr)->next;
78d83a80eeSchristos if ((*elt_ptr) == netio->dispatch_next)
79d83a80eeSchristos netio->dispatch_next = next;
80d83a80eeSchristos (*elt_ptr)->handler = NULL;
81d83a80eeSchristos (*elt_ptr)->next = netio->deallocated;
82d83a80eeSchristos netio->deallocated = *elt_ptr;
83d83a80eeSchristos *elt_ptr = next;
84d83a80eeSchristos break;
85d83a80eeSchristos }
86d83a80eeSchristos }
87d83a80eeSchristos }
88d83a80eeSchristos
89d83a80eeSchristos const struct timespec *
netio_current_time(netio_type * netio)90d83a80eeSchristos netio_current_time(netio_type *netio)
91d83a80eeSchristos {
92d83a80eeSchristos assert(netio);
93d83a80eeSchristos
94d83a80eeSchristos if (!netio->have_current_time) {
95d83a80eeSchristos struct timeval current_timeval;
96d83a80eeSchristos if (gettimeofday(¤t_timeval, NULL) == -1) {
97*3fb62404Schristos log_msg(LOG_ERR, "gettimeofday: %s, aborting.", strerror(errno));
98d83a80eeSchristos abort();
99d83a80eeSchristos }
100d83a80eeSchristos timeval_to_timespec(&netio->cached_current_time, ¤t_timeval);
101d83a80eeSchristos netio->have_current_time = 1;
102d83a80eeSchristos }
103d83a80eeSchristos
104d83a80eeSchristos return &netio->cached_current_time;
105d83a80eeSchristos }
106d83a80eeSchristos
107d83a80eeSchristos int
netio_dispatch(netio_type * netio,const struct timespec * timeout,const sigset_t * sigmask)108d83a80eeSchristos netio_dispatch(netio_type *netio, const struct timespec *timeout, const sigset_t *sigmask)
109d83a80eeSchristos {
110d83a80eeSchristos /* static arrays to avoid allocation */
111d83a80eeSchristos static struct pollfd fds[MAX_NETIO_FDS];
112d83a80eeSchristos int numfd;
113d83a80eeSchristos int have_timeout = 0;
114d83a80eeSchristos struct timespec minimum_timeout;
115d83a80eeSchristos netio_handler_type *timeout_handler = NULL;
116d83a80eeSchristos netio_handler_list_type *elt;
117d83a80eeSchristos int rc;
118d83a80eeSchristos int result = 0;
119d83a80eeSchristos #ifndef HAVE_PPOLL
120d83a80eeSchristos sigset_t origmask;
121d83a80eeSchristos #endif
122d83a80eeSchristos
123d83a80eeSchristos assert(netio);
124d83a80eeSchristos
125d83a80eeSchristos /*
126d83a80eeSchristos * Clear the cached current time.
127d83a80eeSchristos */
128d83a80eeSchristos netio->have_current_time = 0;
129d83a80eeSchristos
130d83a80eeSchristos /*
131d83a80eeSchristos * Initialize the minimum timeout with the timeout parameter.
132d83a80eeSchristos */
133d83a80eeSchristos if (timeout) {
134d83a80eeSchristos have_timeout = 1;
135d83a80eeSchristos memcpy(&minimum_timeout, timeout, sizeof(struct timespec));
136d83a80eeSchristos }
137d83a80eeSchristos
138d83a80eeSchristos /*
139d83a80eeSchristos * Initialize the fd_sets and timeout based on the handler
140d83a80eeSchristos * information.
141d83a80eeSchristos */
142d83a80eeSchristos numfd = 0;
143d83a80eeSchristos
144d83a80eeSchristos for (elt = netio->handlers; elt; elt = elt->next) {
145d83a80eeSchristos netio_handler_type *handler = elt->handler;
146d83a80eeSchristos if (handler->fd != -1 && numfd < MAX_NETIO_FDS) {
147d83a80eeSchristos fds[numfd].fd = handler->fd;
148d83a80eeSchristos fds[numfd].events = 0;
149d83a80eeSchristos fds[numfd].revents = 0;
150d83a80eeSchristos handler->pfd = numfd;
151d83a80eeSchristos if (handler->event_types & NETIO_EVENT_READ) {
152d83a80eeSchristos fds[numfd].events |= POLLIN;
153d83a80eeSchristos }
154d83a80eeSchristos if (handler->event_types & NETIO_EVENT_WRITE) {
155d83a80eeSchristos fds[numfd].events |= POLLOUT;
156d83a80eeSchristos }
157d83a80eeSchristos numfd++;
158d83a80eeSchristos } else {
159d83a80eeSchristos handler->pfd = -1;
160d83a80eeSchristos }
161d83a80eeSchristos if (handler->timeout && (handler->event_types & NETIO_EVENT_TIMEOUT)) {
162d83a80eeSchristos struct timespec relative;
163d83a80eeSchristos
164d83a80eeSchristos relative.tv_sec = handler->timeout->tv_sec;
165d83a80eeSchristos relative.tv_nsec = handler->timeout->tv_nsec;
166d83a80eeSchristos timespec_subtract(&relative, netio_current_time(netio));
167d83a80eeSchristos
168d83a80eeSchristos if (!have_timeout ||
169d83a80eeSchristos timespec_compare(&relative, &minimum_timeout) < 0)
170d83a80eeSchristos {
171d83a80eeSchristos have_timeout = 1;
172d83a80eeSchristos minimum_timeout.tv_sec = relative.tv_sec;
173d83a80eeSchristos minimum_timeout.tv_nsec = relative.tv_nsec;
174d83a80eeSchristos timeout_handler = handler;
175d83a80eeSchristos }
176d83a80eeSchristos }
177d83a80eeSchristos }
178d83a80eeSchristos
179d83a80eeSchristos if (have_timeout && minimum_timeout.tv_sec < 0) {
180d83a80eeSchristos /*
181d83a80eeSchristos * On negative timeout for a handler, immediately
182d83a80eeSchristos * dispatch the timeout event without checking for
183d83a80eeSchristos * other events.
184d83a80eeSchristos */
185d83a80eeSchristos if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
186d83a80eeSchristos timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
187d83a80eeSchristos }
188d83a80eeSchristos return result;
189d83a80eeSchristos }
190d83a80eeSchristos
191d83a80eeSchristos /* Check for events. */
192d83a80eeSchristos #ifdef HAVE_PPOLL
193d83a80eeSchristos rc = ppoll(fds, numfd, (have_timeout?&minimum_timeout:NULL), sigmask);
194d83a80eeSchristos #else
195d83a80eeSchristos sigprocmask(SIG_SETMASK, sigmask, &origmask);
196d83a80eeSchristos rc = poll(fds, numfd, (have_timeout?minimum_timeout.tv_sec*1000+
197d83a80eeSchristos minimum_timeout.tv_nsec/1000000:-1));
198d83a80eeSchristos sigprocmask(SIG_SETMASK, &origmask, NULL);
199d83a80eeSchristos #endif /* HAVE_PPOLL */
200d83a80eeSchristos if (rc == -1) {
201d83a80eeSchristos if(errno == EINVAL || errno == EACCES || errno == EBADF) {
202d83a80eeSchristos log_msg(LOG_ERR, "fatal error poll: %s.",
203d83a80eeSchristos strerror(errno));
204d83a80eeSchristos exit(1);
205d83a80eeSchristos }
206d83a80eeSchristos return -1;
207d83a80eeSchristos }
208d83a80eeSchristos
209d83a80eeSchristos /*
210d83a80eeSchristos * Clear the cached current_time (pselect(2) may block for
211d83a80eeSchristos * some time so the cached value is likely to be old).
212d83a80eeSchristos */
213d83a80eeSchristos netio->have_current_time = 0;
214d83a80eeSchristos
215d83a80eeSchristos if (rc == 0) {
216d83a80eeSchristos /*
217d83a80eeSchristos * No events before the minimum timeout expired.
218d83a80eeSchristos * Dispatch to handler if interested.
219d83a80eeSchristos */
220d83a80eeSchristos if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
221d83a80eeSchristos timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
222d83a80eeSchristos }
223d83a80eeSchristos } else {
224d83a80eeSchristos /*
225d83a80eeSchristos * Dispatch all the events to interested handlers
226d83a80eeSchristos * based on the fd_sets. Note that a handler might
227d83a80eeSchristos * deinstall itself, so store the next handler before
228d83a80eeSchristos * calling the current handler!
229d83a80eeSchristos */
230d83a80eeSchristos assert(netio->dispatch_next == NULL);
231d83a80eeSchristos
232d83a80eeSchristos for (elt = netio->handlers; elt && rc; ) {
233d83a80eeSchristos netio_handler_type *handler = elt->handler;
234d83a80eeSchristos netio->dispatch_next = elt->next;
235d83a80eeSchristos if (handler->fd != -1 && handler->pfd != -1) {
236d83a80eeSchristos netio_event_types_type event_types
237d83a80eeSchristos = NETIO_EVENT_NONE;
238d83a80eeSchristos if ((fds[handler->pfd].revents & POLLIN)) {
239d83a80eeSchristos event_types |= NETIO_EVENT_READ;
240d83a80eeSchristos }
241d83a80eeSchristos if ((fds[handler->pfd].revents & POLLOUT)) {
242d83a80eeSchristos event_types |= NETIO_EVENT_WRITE;
243d83a80eeSchristos }
244d83a80eeSchristos if ((fds[handler->pfd].revents &
245d83a80eeSchristos (POLLNVAL|POLLHUP|POLLERR))) {
246d83a80eeSchristos /* closed/error: give a read event,
247d83a80eeSchristos * or otherwise, a write event */
248d83a80eeSchristos if((handler->event_types&NETIO_EVENT_READ))
249d83a80eeSchristos event_types |= NETIO_EVENT_READ;
250d83a80eeSchristos else if((handler->event_types&NETIO_EVENT_WRITE))
251d83a80eeSchristos event_types |= NETIO_EVENT_WRITE;
252d83a80eeSchristos }
253d83a80eeSchristos
254d83a80eeSchristos if (event_types & handler->event_types) {
255d83a80eeSchristos handler->event_handler(netio, handler, event_types & handler->event_types);
256d83a80eeSchristos ++result;
257d83a80eeSchristos }
258d83a80eeSchristos }
259d83a80eeSchristos elt = netio->dispatch_next;
260d83a80eeSchristos }
261d83a80eeSchristos netio->dispatch_next = NULL;
262d83a80eeSchristos }
263d83a80eeSchristos
264d83a80eeSchristos return result;
265d83a80eeSchristos }
266