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(&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  */
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