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