1 /*
2 * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
3 *
4 * See LICENSE for the license.
5 *
6 */
7
8 #include <config.h>
9
10 #include <assert.h>
11 #include <errno.h>
12 #include <sys/time.h>
13 #include <string.h>
14 #include <stdlib.h>
15
16 #include "log.h"
17 #include "wire/netio.h"
18
19
20 #ifndef HAVE_PSELECT
21 int pselect(int n, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
22 const struct timespec* timeout, const sigset_t* sigmask);
23 #else
24 #include <sys/select.h>
25 #endif
26
27 /* One second is 1e9 nanoseconds. */
28 #define NANOSECONDS_PER_SECOND 1000000000L
29
30 static const char* netio_str = "netio";
31
32
33 /*
34 * Create a new netio instance.
35 * \return netio_type* netio instance
36 *
37 */
38 netio_type*
netio_create()39 netio_create()
40 {
41 netio_type* netio = NULL;
42 CHECKALLOC(netio = (netio_type*) malloc(sizeof(netio_type)));
43 netio->handlers = NULL;
44 netio->dispatch_next = NULL;
45 return netio;
46 }
47
48 /*
49 * Add a new handler to netio.
50 *
51 */
52 void
netio_add_handler(netio_type * netio,netio_handler_type * handler)53 netio_add_handler(netio_type* netio, netio_handler_type* handler)
54 {
55 netio_handler_list_type* l = NULL;
56
57 ods_log_assert(netio);
58 ods_log_assert(handler);
59
60 CHECKALLOC(l = (netio_handler_list_type*) malloc(sizeof(netio_handler_list_type)));
61 l->next = netio->handlers;
62 l->handler = handler;
63 netio->handlers = l;
64 ods_log_debug("[%s] handler added", netio_str);
65 }
66
67 /*
68 * Remove the handler from netio. Caller is responsible for freeing
69 * handler afterwards.
70 */
71 void
netio_remove_handler(netio_type * netio,netio_handler_type * handler)72 netio_remove_handler(netio_type* netio, netio_handler_type* handler)
73 {
74 netio_handler_list_type** lptr;
75 if (!netio || !handler) {
76 return;
77 }
78 for (lptr = &netio->handlers; *lptr; lptr = &(*lptr)->next) {
79 if ((*lptr)->handler == handler) {
80 netio_handler_list_type* next = (*lptr)->next;
81 if ((*lptr) == netio->dispatch_next) {
82 netio->dispatch_next = next;
83 }
84 (*lptr)->handler = NULL;
85 free(*lptr);
86 *lptr = next;
87 break;
88 }
89 }
90 ods_log_debug("[%s] handler removed", netio_str);
91 }
92
93
94 /*
95 * Convert timeval to timespec.
96 *
97 */
98 static void
timeval_to_timespec(struct timespec * left,const struct timeval * right)99 timeval_to_timespec(struct timespec* left, const struct timeval* right)
100 {
101 left->tv_sec = right->tv_sec;
102 left->tv_nsec = 1000 * right->tv_usec;
103 }
104
105 /**
106 * Compare timespec.
107 *
108 */
109 static int
timespec_compare(const struct timespec * left,const struct timespec * right)110 timespec_compare(const struct timespec* left,
111 const struct timespec* right)
112 {
113 if (left->tv_sec < right->tv_sec) {
114 return -1;
115 } else if (left->tv_sec > right->tv_sec) {
116 return 1;
117 } else if (left->tv_nsec < right->tv_nsec) {
118 return -1;
119 } else if (left->tv_nsec > right->tv_nsec) {
120 return 1;
121 }
122 return 0;
123 }
124
125
126 /**
127 * Add timespecs.
128 *
129 */
130 void
timespec_add(struct timespec * left,const struct timespec * right)131 timespec_add(struct timespec* left, const struct timespec* right)
132 {
133 left->tv_sec += right->tv_sec;
134 left->tv_nsec += right->tv_nsec;
135 if (left->tv_nsec >= NANOSECONDS_PER_SECOND) {
136 ++left->tv_sec;
137 left->tv_nsec -= NANOSECONDS_PER_SECOND;
138 }
139 }
140
141
142 /**
143 * Substract timespecs.
144 *
145 */
146 static void
timespec_subtract(struct timespec * left,const struct timespec * right)147 timespec_subtract(struct timespec* left, const struct timespec* right)
148 {
149 left->tv_sec -= right->tv_sec;
150 left->tv_nsec -= right->tv_nsec;
151 if (left->tv_nsec < 0L) {
152 --left->tv_sec;
153 left->tv_nsec += NANOSECONDS_PER_SECOND;
154 }
155 }
156
157
158 /*
159 * Retrieve the current time (using gettimeofday(2)).
160 *
161 */
162 const struct timespec*
netio_current_time(netio_type * netio)163 netio_current_time(netio_type* netio)
164 {
165 struct timeval current_timeval;
166 ods_log_assert(netio);
167 if (!netio->have_current_time) {
168 if (gettimeofday(¤t_timeval, NULL) == -1) {
169 ods_log_crit("[%s] unable to get current time: "
170 "gettimeofday() failed (%s)", netio_str,
171 strerror(errno));
172 abort();
173 }
174 timeval_to_timespec(&netio->cached_current_time,
175 ¤t_timeval);
176 netio->have_current_time = 1;
177 }
178 return &netio->cached_current_time;
179 }
180
181
182 /*
183 * Check for events and dispatch them to the handlers.
184 *
185 */
186 int
netio_dispatch(netio_type * netio,const struct timespec * timeout,const sigset_t * sigmask)187 netio_dispatch(netio_type* netio, const struct timespec* timeout,
188 const sigset_t* sigmask)
189 {
190 fd_set readfds, writefds, exceptfds;
191 int max_fd;
192 int have_timeout = 0;
193 struct timespec minimum_timeout;
194 netio_handler_type* timeout_handler = NULL;
195 netio_handler_list_type* l = NULL;
196 int rc = 0;
197 int result = 0;
198
199 if (!netio || !netio->handlers) {
200 return 0;
201 }
202 /* Clear the cached current time */
203 netio->have_current_time = 0;
204 /* Initialize the minimum timeout with the timeout parameter */
205 if (timeout) {
206 have_timeout = 1;
207 memcpy(&minimum_timeout, timeout, sizeof(struct timespec));
208 }
209 /* Initialize the fd_sets and timeout based on the handler
210 * information */
211 max_fd = -1;
212 FD_ZERO(&readfds);
213 FD_ZERO(&writefds);
214 FD_ZERO(&exceptfds);
215 for (l = netio->handlers; l; l = l->next) {
216 netio_handler_type* handler = l->handler;
217 if (handler->fd >= 0 && handler->fd < (int) FD_SETSIZE) {
218 if (handler->fd > max_fd) {
219 max_fd = handler->fd;
220 }
221 if (handler->event_types & NETIO_EVENT_READ) {
222 FD_SET(handler->fd, &readfds);
223 }
224 if (handler->event_types & NETIO_EVENT_WRITE) {
225 FD_SET(handler->fd, &writefds);
226 }
227 if (handler->event_types & NETIO_EVENT_EXCEPT) {
228 FD_SET(handler->fd, &exceptfds);
229 }
230 }
231 if (handler->timeout &&
232 (handler->event_types & NETIO_EVENT_TIMEOUT)) {
233 struct timespec relative;
234 relative.tv_sec = handler->timeout->tv_sec;
235 relative.tv_nsec = handler->timeout->tv_nsec;
236 timespec_subtract(&relative, netio_current_time(netio));
237
238 if (!have_timeout ||
239 timespec_compare(&relative, &minimum_timeout) < 0) {
240 have_timeout = 1;
241 minimum_timeout.tv_sec = relative.tv_sec;
242 minimum_timeout.tv_nsec = relative.tv_nsec;
243 timeout_handler = handler;
244 }
245 }
246 }
247
248 if (have_timeout && minimum_timeout.tv_sec < 0) {
249 /*
250 * On negative timeout for a handler, immediately
251 * dispatch the timeout event without checking for other events.
252 */
253 ods_log_debug("[%s] dispatch timeout event without checking for "
254 "other events", netio_str);
255 if (timeout_handler &&
256 (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
257 timeout_handler->event_handler(netio, timeout_handler,
258 NETIO_EVENT_TIMEOUT);
259 }
260 return result;
261 }
262 /* Check for events. */
263 rc = pselect(max_fd + 1, &readfds, &writefds, &exceptfds,
264 have_timeout ? &minimum_timeout : NULL, sigmask);
265 if (rc == -1) {
266 if(errno == EINVAL || errno == EACCES || errno == EBADF) {
267 ods_fatal_exit("[%s] fatal error pselect: %s", netio_str,
268 strerror(errno));
269 }
270 return -1;
271 }
272
273 /* Clear the cached current_time (pselect(2) may block for
274 * some time so the cached value is likely to be old).
275 */
276 netio->have_current_time = 0;
277 if (rc == 0) {
278 ods_log_debug("[%s] no events before the minimum timeout "
279 "expired", netio_str);
280 /*
281 * No events before the minimum timeout expired.
282 * Dispatch to handler if interested.
283 */
284 if (timeout_handler &&
285 (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) {
286 timeout_handler->event_handler(netio, timeout_handler,
287 NETIO_EVENT_TIMEOUT);
288 }
289 } else {
290 /*
291 * Dispatch all the events to interested handlers
292 * based on the fd_sets. Note that a handler might
293 * deinstall itself, so store the next handler before
294 * calling the current handler!
295 */
296 ods_log_assert(netio->dispatch_next == NULL);
297 for (l = netio->handlers; l && rc; ) {
298 netio_handler_type* handler = l->handler;
299 netio->dispatch_next = l->next;
300 if (handler->fd >= 0 && handler->fd < (int) FD_SETSIZE) {
301 netio_events_type event_types = NETIO_EVENT_NONE;
302 if (FD_ISSET(handler->fd, &readfds)) {
303 event_types |= NETIO_EVENT_READ;
304 FD_CLR(handler->fd, &readfds);
305 rc--;
306 }
307 if (FD_ISSET(handler->fd, &writefds)) {
308 event_types |= NETIO_EVENT_WRITE;
309 FD_CLR(handler->fd, &writefds);
310 rc--;
311 }
312 if (FD_ISSET(handler->fd, &exceptfds)) {
313 event_types |= NETIO_EVENT_EXCEPT;
314 FD_CLR(handler->fd, &exceptfds);
315 rc--;
316 }
317 if (event_types & handler->event_types) {
318 handler->event_handler(netio, handler,
319 event_types & handler->event_types);
320 ++result;
321 }
322 }
323 l = netio->dispatch_next;
324 }
325 netio->dispatch_next = NULL;
326 }
327 return result;
328 }
329
330
331 /**
332 * Clean up netio instance
333 *
334 */
335 void
netio_cleanup(netio_type * netio)336 netio_cleanup(netio_type* netio)
337 {
338 ods_log_assert(netio);
339 while (netio->handlers) {
340 netio_handler_list_type* handler = netio->handlers;
341 netio->handlers = handler->next;
342 if (handler->handler->free_handler) {
343 free(handler->handler->user_data);
344 free(handler->handler);
345 }
346 free(handler);
347 }
348 free(netio);
349 }
350
351 /**
352 * Clean up netio instance
353 */
354 void
netio_cleanup_shallow(netio_type * netio)355 netio_cleanup_shallow(netio_type* netio)
356 {
357 ods_log_assert(netio);
358 free(netio->handlers);
359 free(netio);
360 }
361
362