OpenDNSSEC-signer 2.1.13
netio.c
Go to the documentation of this file.
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
21int 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
30static const char* netio_str = "netio";
31
32
33/*
34 * Create a new netio instance.
35 * \return netio_type* netio instance
36 *
37 */
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 */
52void
54{
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 */
71void
73{
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 */
98static void
99timeval_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
109static int
110timespec_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
130void
131timespec_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
146static void
147timespec_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 */
162const struct timespec*
164{
165 struct timeval current_timeval;
166 ods_log_assert(netio);
167 if (!netio->have_current_time) {
168 if (gettimeofday(&current_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 &current_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 */
186int
187netio_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,
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,
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) {
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
335void
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
354void
356{
357 ods_log_assert(netio);
358 free(netio->handlers);
359 free(netio);
360}
361
const struct timespec * netio_current_time(netio_type *netio)
Definition: netio.c:163
void netio_cleanup_shallow(netio_type *netio)
Definition: netio.c:355
#define NANOSECONDS_PER_SECOND
Definition: netio.c:28
void netio_remove_handler(netio_type *netio, netio_handler_type *handler)
Definition: netio.c:72
int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)
void timespec_add(struct timespec *left, const struct timespec *right)
Definition: netio.c:131
void netio_cleanup(netio_type *netio)
Definition: netio.c:336
int netio_dispatch(netio_type *netio, const struct timespec *timeout, const sigset_t *sigmask)
Definition: netio.c:187
void netio_add_handler(netio_type *netio, netio_handler_type *handler)
Definition: netio.c:53
netio_type * netio_create()
Definition: netio.c:39
enum netio_events_enum netio_events_type
Definition: netio.h:76
@ NETIO_EVENT_WRITE
Definition: netio.h:72
@ NETIO_EVENT_EXCEPT
Definition: netio.h:73
@ NETIO_EVENT_TIMEOUT
Definition: netio.h:74
@ NETIO_EVENT_READ
Definition: netio.h:71
@ NETIO_EVENT_NONE
Definition: netio.h:70
netio_handler_list_type * next
Definition: netio.h:94
netio_handler_type * handler
Definition: netio.h:95
struct timespec * timeout
Definition: netio.h:115
netio_events_type event_types
Definition: netio.h:124
netio_event_handler_type event_handler
Definition: netio.h:131
void * user_data
Definition: netio.h:119
netio_handler_list_type * dispatch_next
Definition: netio.h:156
netio_handler_list_type * handlers
Definition: netio.h:140
int have_current_time
Definition: netio.h:150
struct timespec cached_current_time
Definition: netio.h:151