]> git.meshlink.io Git - meshlink/blob - src/event.c
More fixes for Windows.
[meshlink] / src / event.c
1 /*
2     event.c -- I/O, timeout and signal event handling
3     Copyright (C) 2012 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 int signal_compare(const signal_t *a, const signal_t *b) {
56         return a->signum - b->signum;
57 }
58
59 static splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
60 static splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
61 static splay_tree_t signal_tree = {.compare = (splay_compare_t)signal_compare};
62
63 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
64         if(io->cb)
65                 return;
66
67         io->fd = fd;
68         io->cb = cb;
69         io->data = data;
70         io->node.data = io;
71
72         io_set(io, flags);
73
74         if(!splay_insert_node(&io_tree, &io->node))
75                 abort();
76 }
77
78 void io_set(io_t *io, int flags) {
79         io->flags = flags;
80
81         if(flags & IO_READ)
82                 FD_SET(io->fd, &readfds);
83         else
84                 FD_CLR(io->fd, &readfds);
85
86         if(flags & IO_WRITE)
87                 FD_SET(io->fd, &writefds);
88         else
89                 FD_CLR(io->fd, &writefds);
90 }
91
92 void io_del(io_t *io) {
93         if(!io->cb)
94                 return;
95
96         io_set(io, 0);
97
98         splay_unlink_node(&io_tree, &io->node);
99         io->cb = NULL;
100 }
101
102 void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv) {
103         timeout->cb = cb;
104         timeout->data = data;
105         timeout->node.data = timeout;
106
107         timeout_set(timeout, tv);
108 }
109
110 void timeout_set(timeout_t *timeout, struct timeval *tv) {
111         if(timerisset(&timeout->tv))
112                 splay_unlink_node(&timeout_tree, &timeout->node);
113
114         if(!now.tv_sec)
115                 gettimeofday(&now, NULL);
116
117         timeradd(&now, tv, &timeout->tv);
118
119         if(!splay_insert_node(&timeout_tree, &timeout->node))
120                 abort();
121 }
122
123 void timeout_del(timeout_t *timeout) {
124         if(!timeout->cb)
125                 return;
126
127         splay_unlink_node(&timeout_tree, &timeout->node);
128         timeout->cb = 0;
129         timeout->tv = (struct timeval){0, 0};
130 }
131
132 #ifndef HAVE_MINGW
133 static io_t signalio;
134 static int pipefd[2] = {-1, -1};
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_exit(void) {
249         running = false;
250 }