xref: /openbsd/usr.sbin/nsd/netio.c (revision ee5153b7)
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 
23 netio_type *
netio_create(region_type * region)24 netio_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 
38 void
netio_add_handler(netio_type * netio,netio_handler_type * handler)39 netio_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 
67 void
netio_remove_handler(netio_type * netio,netio_handler_type * handler)68 netio_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 
89 const struct timespec *
netio_current_time(netio_type * netio)90 netio_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 
107 int
netio_dispatch(netio_type * netio,const struct timespec * timeout,const sigset_t * sigmask)108 netio_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