xref: /netbsd-src/external/bsd/nsd/dist/netio.c (revision 3fb6240442248a1e2fdadd3dfa982d687da01e01)
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(&current_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, &current_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