Use Ed25519 keys.
[oweals/tinc.git] / 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
27 struct timeval now;
28
29 static fd_set readfds;
30 static fd_set writefds;
31 static volatile bool running;
32
33 static int io_compare(const io_t *a, const io_t *b) {
34         return a->fd - b->fd;
35 }
36
37 static int timeout_compare(const timeout_t *a, const timeout_t *b) {
38         struct timeval diff;
39         timersub(&a->tv, &b->tv, &diff);
40         if(diff.tv_sec < 0)
41                 return -1;
42         if(diff.tv_sec > 0)
43                 return 1;
44         if(diff.tv_usec < 0)
45                 return -1;
46         if(diff.tv_usec > 0)
47                 return 1;
48         if(a < b)
49                 return -1;
50         if(a > b)
51                 return 1;
52         return 0;
53 }
54
55 static splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
56 static splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
57
58 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
59         if(io->cb)
60                 return;
61
62         io->fd = fd;
63         io->cb = cb;
64         io->data = data;
65         io->node.data = io;
66
67         io_set(io, flags);
68
69         if(!splay_insert_node(&io_tree, &io->node))
70                 abort();
71 }
72
73 void io_set(io_t *io, int flags) {
74         io->flags = flags;
75
76         if(flags & IO_READ)
77                 FD_SET(io->fd, &readfds);
78         else
79                 FD_CLR(io->fd, &readfds);
80
81         if(flags & IO_WRITE)
82                 FD_SET(io->fd, &writefds);
83         else
84                 FD_CLR(io->fd, &writefds);
85 }
86
87 void io_del(io_t *io) {
88         if(!io->cb)
89                 return;
90
91         io_set(io, 0);
92
93         splay_unlink_node(&io_tree, &io->node);
94         io->cb = NULL;
95 }
96
97 void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv) {
98         timeout->cb = cb;
99         timeout->data = data;
100         timeout->node.data = timeout;
101
102         timeout_set(timeout, tv);
103 }
104
105 void timeout_set(timeout_t *timeout, struct timeval *tv) {
106         if(timerisset(&timeout->tv))
107                 splay_unlink_node(&timeout_tree, &timeout->node);
108
109         if(!now.tv_sec)
110                 gettimeofday(&now, NULL);
111
112         timeradd(&now, tv, &timeout->tv);
113
114         if(!splay_insert_node(&timeout_tree, &timeout->node))
115                 abort();
116 }
117
118 void timeout_del(timeout_t *timeout) {
119         if(!timeout->cb)
120                 return;
121
122         splay_unlink_node(&timeout_tree, &timeout->node);
123         timeout->cb = 0;
124         timeout->tv = (struct timeval){0, 0};
125 }
126
127 #ifndef HAVE_MINGW
128 static int signal_compare(const signal_t *a, const signal_t *b) {
129         return a->signum - b->signum;
130 }
131
132 static io_t signalio;
133 static int pipefd[2] = {-1, -1};
134 static splay_tree_t signal_tree = {.compare = (splay_compare_t)signal_compare};
135
136 static void signal_handler(int signum) {
137         unsigned char num = signum;
138         write(pipefd[1], &num, 1);
139 }
140
141 static void signalio_handler(void *data, int flags) {
142         unsigned char signum;
143         if(read(pipefd[0], &signum, 1) != 1)
144                 return;
145
146         signal_t *sig = splay_search(&signal_tree, &((signal_t){.signum = signum}));
147         if(sig)
148                 sig->cb(sig->data);
149 }
150
151 static void pipe_init(void) {
152         if(!pipe(pipefd))
153                 io_add(&signalio, signalio_handler, NULL, pipefd[0], IO_READ);
154 }
155
156 void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum) {
157         if(sig->cb)
158                 return;
159
160         sig->cb = cb;
161         sig->data = data;
162         sig->signum = signum;
163         sig->node.data = sig;
164
165         if(pipefd[0] == -1)
166                 pipe_init();
167
168         signal(sig->signum, signal_handler);
169
170         if(!splay_insert_node(&signal_tree, &sig->node))
171                 abort();
172 }
173
174 void signal_del(signal_t *sig) {
175         if(!sig->cb)
176                 return;
177
178         signal(sig->signum, SIG_DFL);
179
180         splay_unlink_node(&signal_tree, &sig->node);
181         sig->cb = NULL;
182 }
183 #endif
184
185 bool event_loop(void) {
186         running = true;
187
188         fd_set readable;
189         fd_set writable;
190
191         while(running) {
192                 gettimeofday(&now, NULL);
193                 struct timeval diff, *tv = NULL;
194
195                 while(timeout_tree.head) {
196                         timeout_t *timeout = timeout_tree.head->data;
197                         timersub(&timeout->tv, &now, &diff);
198
199                         if(diff.tv_sec < 0) {
200                                 timeout->cb(timeout->data);
201                                 if(timercmp(&timeout->tv, &now, <))
202                                         timeout_del(timeout);
203                         } else {
204                                 tv = &diff;
205                                 break;
206                         }
207                 }
208
209                 memcpy(&readable, &readfds, sizeof readable);
210                 memcpy(&writable, &writefds, sizeof writable);
211
212                 int fds = 0;
213
214                 if(io_tree.tail) {
215                         io_t *last = io_tree.tail->data;
216                         fds = last->fd + 1;
217                 }
218
219 #ifdef HAVE_MINGW
220                 LeaveCriticalSection(&mutex);
221 #endif
222                 int n = select(fds, &readable, &writable, NULL, tv);
223 #ifdef HAVE_MINGW
224                 EnterCriticalSection(&mutex);
225 #endif
226
227                 if(n < 0) {
228                         if(sockwouldblock(errno))
229                                 continue;
230                         else
231                                 return false;
232                 }
233
234                 if(!n)
235                         continue;
236
237                 for splay_each(io_t, io, &io_tree) {
238                         if(FD_ISSET(io->fd, &writable))
239                                 io->cb(io->data, IO_WRITE);
240                         else if(FD_ISSET(io->fd, &readable))
241                                 io->cb(io->data, IO_READ);
242                 }
243         }
244
245         return true;
246 }
247
248 void event_flush_output(void) {
249         for splay_each(io_t, io, &io_tree)
250                 if(FD_ISSET(io->fd, &writefds))
251                         io->cb(io->data, IO_WRITE);
252 }
253
254 void event_exit(void) {
255         running = false;
256 }