xref: /openbsd-src/usr.sbin/nsd/netio.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
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 
17 #include "netio.h"
18 #include "util.h"
19 
20 
21 #ifndef HAVE_PSELECT
22 int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
23 	    const struct timespec *timeout, const sigset_t *sigmask);
24 #else
25 #include <sys/select.h>
26 #endif
27 
28 netio_type *
29 netio_create(region_type *region)
30 {
31 	netio_type *result;
32 
33 	assert(region);
34 
35 	result = (netio_type *) region_alloc(region, sizeof(netio_type));
36 	result->region = region;
37 	result->handlers = NULL;
38 	result->deallocated = NULL;
39 	result->dispatch_next = NULL;
40 	return result;
41 }
42 
43 void
44 netio_add_handler(netio_type *netio, netio_handler_type *handler)
45 {
46 	netio_handler_list_type *elt;
47 
48 	assert(netio);
49 	assert(handler);
50 
51 	if (netio->deallocated) {
52 		/*
53 		 * If we have deallocated handler list elements, reuse
54 		 * the first one.
55 		 */
56 		elt = netio->deallocated;
57 		netio->deallocated = elt->next;
58 	} else {
59 		/*
60 		 * Allocate a new one.
61 		 */
62 		elt = (netio_handler_list_type *) region_alloc(
63 			netio->region, sizeof(netio_handler_list_type));
64 	}
65 
66 	elt->next = netio->handlers;
67 	elt->handler = handler;
68 	netio->handlers = elt;
69 }
70 
71 void
72 netio_remove_handler(netio_type *netio, netio_handler_type *handler)
73 {
74 	netio_handler_list_type **elt_ptr;
75 
76 	assert(netio);
77 	assert(handler);
78 
79 	for (elt_ptr = &netio->handlers; *elt_ptr; elt_ptr = &(*elt_ptr)->next) {
80 		if ((*elt_ptr)->handler == handler) {
81 			netio_handler_list_type *next = (*elt_ptr)->next;
82 			if ((*elt_ptr) == netio->dispatch_next)
83 				netio->dispatch_next = next;
84 			(*elt_ptr)->handler = NULL;
85 			(*elt_ptr)->next = netio->deallocated;
86 			netio->deallocated = *elt_ptr;
87 			*elt_ptr = next;
88 			break;
89 		}
90 	}
91 }
92 
93 const struct timespec *
94 netio_current_time(netio_type *netio)
95 {
96 	assert(netio);
97 
98 	if (!netio->have_current_time) {
99 		struct timeval current_timeval;
100 		if (gettimeofday(&current_timeval, NULL) == -1) {
101 			log_msg(LOG_CRIT, "gettimeofday: %s, aborting.", strerror(errno));
102 			abort();
103 		}
104 		timeval_to_timespec(&netio->cached_current_time, &current_timeval);
105 		netio->have_current_time = 1;
106 	}
107 
108 	return &netio->cached_current_time;
109 }
110 
111 int
112 netio_dispatch(netio_type *netio, const struct timespec *timeout, const sigset_t *sigmask)
113 {
114 	fd_set readfds, writefds, exceptfds;
115 	int max_fd;
116 	int have_timeout = 0;
117 	struct timespec minimum_timeout;
118 	netio_handler_type *timeout_handler = NULL;
119 	netio_handler_list_type *elt;
120 	int rc;
121 	int result = 0;
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 	max_fd = -1;
143 	FD_ZERO(&readfds);
144 	FD_ZERO(&writefds);
145 	FD_ZERO(&exceptfds);
146 
147 	for (elt = netio->handlers; elt; elt = elt->next) {
148 		netio_handler_type *handler = elt->handler;
149 		if (handler->fd != -1 && handler->fd < (int)FD_SETSIZE) {
150 			if (handler->fd > max_fd) {
151 				max_fd = handler->fd;
152 			}
153 			if (handler->event_types & NETIO_EVENT_READ) {
154 				FD_SET(handler->fd, &readfds);
155 			}
156 			if (handler->event_types & NETIO_EVENT_WRITE) {
157 				FD_SET(handler->fd, &writefds);
158 			}
159 			if (handler->event_types & NETIO_EVENT_EXCEPT) {
160 				FD_SET(handler->fd, &exceptfds);
161 			}
162 		}
163 		if (handler->timeout && (handler->event_types & NETIO_EVENT_TIMEOUT)) {
164 			struct timespec relative;
165 
166 			relative.tv_sec = handler->timeout->tv_sec;
167 			relative.tv_nsec = handler->timeout->tv_nsec;
168 			timespec_subtract(&relative, netio_current_time(netio));
169 
170 			if (!have_timeout ||
171 			    timespec_compare(&relative, &minimum_timeout) < 0)
172 			{
173 				have_timeout = 1;
174 				minimum_timeout.tv_sec = relative.tv_sec;
175 				minimum_timeout.tv_nsec = relative.tv_nsec;
176 				timeout_handler = handler;
177 			}
178 		}
179 	}
180 
181 	if (have_timeout && minimum_timeout.tv_sec < 0) {
182 		/*
183 		 * On negative timeout for a handler, immediatly
184 		 * dispatch the timeout event without checking for
185 		 * other events.
186 		 */
187 		if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
188 			timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
189 		}
190 		return result;
191 	}
192 
193 	/* Check for events.  */
194 	rc = pselect(max_fd + 1, &readfds, &writefds, &exceptfds,
195 		     have_timeout ? &minimum_timeout : NULL,
196 		     sigmask);
197 	if (rc == -1) {
198 		if(errno == EINVAL || errno == EACCES || errno == EBADF) {
199 			log_msg(LOG_ERR, "fatal error pselect: %s.",
200 				strerror(errno));
201 			exit(1);
202 		}
203 		return -1;
204 	}
205 
206 	/*
207 	 * Clear the cached current_time (pselect(2) may block for
208 	 * some time so the cached value is likely to be old).
209 	 */
210 	netio->have_current_time = 0;
211 
212 	if (rc == 0) {
213 		/*
214 		 * No events before the minimum timeout expired.
215 		 * Dispatch to handler if interested.
216 		 */
217 		if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
218 			timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT);
219 		}
220 	} else {
221 		/*
222 		 * Dispatch all the events to interested handlers
223 		 * based on the fd_sets.  Note that a handler might
224 		 * deinstall itself, so store the next handler before
225 		 * calling the current handler!
226 		 */
227 		assert(netio->dispatch_next == NULL);
228 		for (elt = netio->handlers; elt && rc; ) {
229 			netio_handler_type *handler = elt->handler;
230 			netio->dispatch_next = elt->next;
231 			if (handler->fd != -1 && handler->fd < (int)FD_SETSIZE) {
232 				netio_event_types_type event_types
233 					= NETIO_EVENT_NONE;
234 				if (FD_ISSET(handler->fd, &readfds)) {
235 					event_types |= NETIO_EVENT_READ;
236 					FD_CLR(handler->fd, &readfds);
237 					rc--;
238 				}
239 				if (FD_ISSET(handler->fd, &writefds)) {
240 					event_types |= NETIO_EVENT_WRITE;
241 					FD_CLR(handler->fd, &writefds);
242 					rc--;
243 				}
244 				if (FD_ISSET(handler->fd, &exceptfds)) {
245 					event_types |= NETIO_EVENT_EXCEPT;
246 					FD_CLR(handler->fd, &exceptfds);
247 					rc--;
248 				}
249 
250 				if (event_types & handler->event_types) {
251 					handler->event_handler(netio, handler, event_types & handler->event_types);
252 					++result;
253 				}
254 			}
255 			elt = netio->dispatch_next;
256 		}
257 		netio->dispatch_next = NULL;
258 	}
259 
260 	return result;
261 }
262