5edd3a6d0b7cf8efbef2100ba43012085ac5639d
[tinc] / src / event.c
1 /*
2     event.c -- I/O, timeout and signal event handling
3     Copyright (C) 2012-2013 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #include "dropin.h"
23 #include "event.h"
24 #include "net.h"
25 #include "utils.h"
26 #include "xalloc.h"
27
28 struct timeval now;
29
30 #ifndef HAVE_MINGW
31 static fd_set readfds;
32 static fd_set writefds;
33 #else
34 static const long READ_EVENTS = FD_READ | FD_ACCEPT | FD_CLOSE;
35 static const long WRITE_EVENTS = FD_WRITE | FD_CONNECT;
36 static DWORD event_count = 0;
37 #endif
38 static bool running;
39
40 static int io_compare(const io_t *a, const io_t *b) {
41 #ifndef HAVE_MINGW
42         return a->fd - b->fd;
43 #else
44         return a->event - b->event;
45 #endif
46 }
47
48 static int timeout_compare(const timeout_t *a, const timeout_t *b) {
49         struct timeval diff;
50         timersub(&a->tv, &b->tv, &diff);
51
52         if(diff.tv_sec < 0) {
53                 return -1;
54         }
55
56         if(diff.tv_sec > 0) {
57                 return 1;
58         }
59
60         if(diff.tv_usec < 0) {
61                 return -1;
62         }
63
64         if(diff.tv_usec > 0) {
65                 return 1;
66         }
67
68         if(a < b) {
69                 return -1;
70         }
71
72         if(a > b) {
73                 return 1;
74         }
75
76         return 0;
77 }
78
79 static splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
80 static splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
81
82 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
83         if(io->cb) {
84                 return;
85         }
86
87         io->fd = fd;
88 #ifdef HAVE_MINGW
89
90         if(io->fd != -1) {
91                 io->event = WSACreateEvent();
92
93                 if(io->event == WSA_INVALID_EVENT) {
94                         abort();
95                 }
96         }
97
98         event_count++;
99 #endif
100         io->cb = cb;
101         io->data = data;
102         io->node.data = io;
103
104         io_set(io, flags);
105
106         if(!splay_insert_node(&io_tree, &io->node)) {
107                 abort();
108         }
109 }
110
111 #ifdef HAVE_MINGW
112 void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
113         io->event = event;
114         io_add(io, cb, data, -1, 0);
115 }
116 #endif
117
118 void io_set(io_t *io, int flags) {
119         if(flags == io->flags) {
120                 return;
121         }
122
123         io->flags = flags;
124
125         if(io->fd == -1) {
126                 return;
127         }
128
129 #ifndef HAVE_MINGW
130
131         if(flags & IO_READ) {
132                 FD_SET(io->fd, &readfds);
133         } else {
134                 FD_CLR(io->fd, &readfds);
135         }
136
137         if(flags & IO_WRITE) {
138                 FD_SET(io->fd, &writefds);
139         } else {
140                 FD_CLR(io->fd, &writefds);
141         }
142
143 #else
144         long events = 0;
145
146         if(flags & IO_WRITE) {
147                 events |= WRITE_EVENTS;
148         }
149
150         if(flags & IO_READ) {
151                 events |= READ_EVENTS;
152         }
153
154         if(WSAEventSelect(io->fd, io->event, events) != 0) {
155                 abort();
156         }
157
158 #endif
159 }
160
161 void io_del(io_t *io) {
162         if(!io->cb) {
163                 return;
164         }
165
166         io_set(io, 0);
167 #ifdef HAVE_MINGW
168
169         if(io->fd != -1 && WSACloseEvent(io->event) == FALSE) {
170                 abort();
171         }
172
173         event_count--;
174 #endif
175
176         splay_unlink_node(&io_tree, &io->node);
177         io->cb = NULL;
178 }
179
180 void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv) {
181         timeout->cb = cb;
182         timeout->data = data;
183         timeout->node.data = timeout;
184
185         timeout_set(timeout, tv);
186 }
187
188 void timeout_set(timeout_t *timeout, struct timeval *tv) {
189         if(timerisset(&timeout->tv)) {
190                 splay_unlink_node(&timeout_tree, &timeout->node);
191         }
192
193         if(!now.tv_sec) {
194                 gettimeofday(&now, NULL);
195         }
196
197         timeradd(&now, tv, &timeout->tv);
198
199         if(!splay_insert_node(&timeout_tree, &timeout->node)) {
200                 abort();
201         }
202 }
203
204 void timeout_del(timeout_t *timeout) {
205         if(!timeout->cb) {
206                 return;
207         }
208
209         splay_unlink_node(&timeout_tree, &timeout->node);
210         timeout->cb = 0;
211         timeout->tv = (struct timeval) {
212                 0, 0
213         };
214 }
215
216 #ifndef HAVE_MINGW
217 static int signal_compare(const signal_t *a, const signal_t *b) {
218         return a->signum - b->signum;
219 }
220
221 static io_t signalio;
222 static int pipefd[2] = {-1, -1};
223 static splay_tree_t signal_tree = {.compare = (splay_compare_t)signal_compare};
224
225 static void signal_handler(int signum) {
226         unsigned char num = signum;
227         write(pipefd[1], &num, 1);
228 }
229
230 static void signalio_handler(void *data, int flags) {
231         unsigned char signum;
232
233         if(read(pipefd[0], &signum, 1) != 1) {
234                 return;
235         }
236
237         signal_t *sig = splay_search(&signal_tree, &((signal_t) {
238                 .signum = signum
239         }));
240
241         if(sig) {
242                 sig->cb(sig->data);
243         }
244 }
245
246 static void pipe_init(void) {
247         if(!pipe(pipefd)) {
248                 io_add(&signalio, signalio_handler, NULL, pipefd[0], IO_READ);
249         }
250 }
251
252 void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum) {
253         if(sig->cb) {
254                 return;
255         }
256
257         sig->cb = cb;
258         sig->data = data;
259         sig->signum = signum;
260         sig->node.data = sig;
261
262         if(pipefd[0] == -1) {
263                 pipe_init();
264         }
265
266         signal(sig->signum, signal_handler);
267
268         if(!splay_insert_node(&signal_tree, &sig->node)) {
269                 abort();
270         }
271 }
272
273 void signal_del(signal_t *sig) {
274         if(!sig->cb) {
275                 return;
276         }
277
278         signal(sig->signum, SIG_DFL);
279
280         splay_unlink_node(&signal_tree, &sig->node);
281         sig->cb = NULL;
282 }
283 #endif
284
285 static struct timeval *get_time_remaining(struct timeval *diff) {
286         gettimeofday(&now, NULL);
287         struct timeval *tv = NULL;
288
289         while(timeout_tree.head) {
290                 timeout_t *timeout = timeout_tree.head->data;
291                 timersub(&timeout->tv, &now, diff);
292
293                 if(diff->tv_sec < 0) {
294                         timeout->cb(timeout->data);
295
296                         if(timercmp(&timeout->tv, &now, <)) {
297                                 timeout_del(timeout);
298                         }
299                 } else {
300                         tv = diff;
301                         break;
302                 }
303         }
304
305         return tv;
306 }
307
308 bool event_loop(void) {
309         running = true;
310
311 #ifndef HAVE_MINGW
312         fd_set readable;
313         fd_set writable;
314
315         while(running) {
316                 struct timeval diff;
317                 struct timeval *tv = get_time_remaining(&diff);
318                 memcpy(&readable, &readfds, sizeof(readable));
319                 memcpy(&writable, &writefds, sizeof(writable));
320
321                 int fds = 0;
322
323                 if(io_tree.tail) {
324                         io_t *last = io_tree.tail->data;
325                         fds = last->fd + 1;
326                 }
327
328                 int n = select(fds, &readable, &writable, NULL, tv);
329
330                 if(n < 0) {
331                         if(sockwouldblock(sockerrno)) {
332                                 continue;
333                         } else {
334                                 return false;
335                         }
336                 }
337
338                 if(!n) {
339                         continue;
340                 }
341
342                 for splay_each(io_t, io, &io_tree) {
343                         if(FD_ISSET(io->fd, &writable)) {
344                                 io->cb(io->data, IO_WRITE);
345                         } else if(FD_ISSET(io->fd, &readable)) {
346                                 io->cb(io->data, IO_READ);
347                         } else {
348                                 continue;
349                         }
350
351                         /*
352                            There are scenarios in which the callback will remove another io_t from the tree
353                            (e.g. closing a double connection). Since splay_each does not support that, we
354                            need to exit the loop now. That's okay, since any remaining events will get picked
355                            up by the next select() call.
356                          */
357                         break;
358                 }
359         }
360
361 #else
362
363         while(running) {
364                 struct timeval diff;
365                 struct timeval *tv = get_time_remaining(&diff);
366                 DWORD timeout_ms = tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1) : WSA_INFINITE;
367
368                 if(!event_count) {
369                         Sleep(timeout_ms);
370                         continue;
371                 }
372
373                 /*
374                    For some reason, Microsoft decided to make the FD_WRITE event edge-triggered instead of level-triggered,
375                    which is the opposite of what select() does. In practice, that means that if a FD_WRITE event triggers,
376                    it will never trigger again until a send() returns EWOULDBLOCK. Since the semantics of this event loop
377                    is that write events are level-triggered (i.e. they continue firing until the socket is full), we need
378                    to emulate these semantics by making sure we fire each IO_WRITE that is still writeable.
379
380                    Note that technically FD_CLOSE has the same problem, but it's okay because user code does not rely on
381                    this event being fired again if ignored.
382                 */
383                 io_t *writeable_io = NULL;
384
385                 for splay_each(io_t, io, &io_tree)
386                         if(io->flags & IO_WRITE && send(io->fd, NULL, 0, 0) == 0) {
387                                 writeable_io = io;
388                                 break;
389                         }
390
391                 if(writeable_io) {
392                         writeable_io->cb(writeable_io->data, IO_WRITE);
393                         continue;
394                 }
395
396                 WSAEVENT *events = xmalloc(event_count * sizeof(*events));
397                 DWORD event_index = 0;
398
399                 for splay_each(io_t, io, &io_tree) {
400                         events[event_index] = io->event;
401                         event_index++;
402                 }
403
404                 DWORD result = WSAWaitForMultipleEvents(event_count, events, FALSE, timeout_ms, FALSE);
405
406                 WSAEVENT event;
407
408                 if(result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + event_count) {
409                         event = events[result - WSA_WAIT_EVENT_0];
410                 }
411
412                 free(events);
413
414                 if(result == WSA_WAIT_TIMEOUT) {
415                         continue;
416                 }
417
418                 if(result < WSA_WAIT_EVENT_0 || result >= WSA_WAIT_EVENT_0 + event_count) {
419                         return false;
420                 }
421
422                 io_t *io = splay_search(&io_tree, &((io_t) {
423                         .event = event
424                 }));
425
426                 if(!io) {
427                         abort();
428                 }
429
430                 if(io->fd == -1) {
431                         io->cb(io->data, 0);
432                 } else {
433                         WSANETWORKEVENTS network_events;
434
435                         if(WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0) {
436                                 return false;
437                         }
438
439                         if(network_events.lNetworkEvents & READ_EVENTS) {
440                                 io->cb(io->data, IO_READ);
441                         }
442
443                         /*
444                             The fd might be available for write too. However, if we already fired the read callback, that
445                             callback might have deleted the io (e.g. through terminate_connection()), so we can't fire the
446                             write callback here. Instead, we loop back and let the writable io loop above handle it.
447                          */
448                 }
449         }
450
451 #endif
452
453         return true;
454 }
455
456 void event_exit(void) {
457         running = false;
458 }