Split event.c into per-API files
[tinc] / src / bsd / event.c
1 /*
2     event.c -- kqueue support for the BSD family
3     Copyright (C) 2012-2022 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 <sys/event.h>
23
24 #include "../event.h"
25 #include "../utils.h"
26 #include "../net.h"
27
28 static bool running = false;
29 static int kq = 0;
30
31 static inline void event_init(void) {
32         if(!kq) {
33                 kq = kqueue();
34
35                 if(kq == -1) {
36                         logger(DEBUG_ALWAYS, LOG_EMERG, "Could not initialize kqueue: %s", strerror(errno));
37                         abort();
38                 }
39         }
40 }
41
42 static void event_deinit(void) {
43         if(kq) {
44                 close(kq);
45                 kq = 0;
46         }
47 }
48
49 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
50         if(io->cb) {
51                 return;
52         }
53
54         io->fd = fd;
55         io->cb = cb;
56         io->data = data;
57         io->node.data = io;
58
59         io_set(io, flags);
60 }
61
62 void io_set(io_t *io, int flags) {
63         event_init();
64
65         if(flags == io->flags) {
66                 return;
67         }
68
69         io->flags = flags;
70
71         if(io->fd == -1) {
72                 return;
73         }
74
75         const struct kevent change[] = {
76                 {
77                         .ident = io->fd,
78                         .filter = EVFILT_READ,
79                         .flags = EV_RECEIPT | (flags & IO_READ ? EV_ADD : EV_DELETE),
80                         .udata = io,
81                 },
82                 {
83                         .ident = io->fd,
84                         .filter = EVFILT_WRITE,
85                         .flags = EV_RECEIPT | (flags & IO_WRITE ? EV_ADD : EV_DELETE),
86                         .udata = io,
87                 },
88         };
89         struct kevent result[2];
90
91         if(kevent(kq, change, 2, result, 2, NULL) < 0) {
92                 logger(DEBUG_ALWAYS, LOG_EMERG, "kevent failed: %s", strerror(errno));
93                 abort();
94         }
95
96         int rerr = (int)result[0].data;
97         int werr = (int)result[1].data;
98
99         if((rerr && rerr != ENOENT) || (werr && werr != ENOENT)) {
100                 logger(DEBUG_ALWAYS, LOG_EMERG, "kevent errors: %s, %s", strerror(rerr), strerror(werr));
101                 abort();
102         }
103
104         if(!flags) {
105                 io_tree.generation++;
106         }
107 }
108
109 void io_del(io_t *io) {
110         if(io->cb) {
111                 io_set(io, 0);
112                 io->cb = NULL;
113         }
114 }
115
116 bool event_loop(void) {
117         event_init();
118         running = true;
119
120         while(running) {
121                 struct timeval diff;
122                 struct timeval *tv = timeout_execute(&diff);
123                 struct kevent events[MAX_EVENTS_PER_LOOP];
124
125                 const struct timespec ts = {
126                         .tv_sec = tv->tv_sec,
127                         .tv_nsec = tv->tv_usec * 1000,
128                 };
129
130                 int n = kevent(kq, NULL, 0, events, MAX_EVENTS_PER_LOOP, &ts);
131
132                 if(n < 0) {
133                         if(sockwouldblock(sockerrno)) {
134                                 continue;
135                         } else {
136                                 return false;
137                         }
138                 }
139
140                 if(!n) {
141                         continue;
142                 }
143
144                 unsigned int curgen = io_tree.generation;
145
146                 for(int i = 0; i < n; i++) {
147                         const struct kevent *evt = &events[i];
148                         const io_t *io = evt->udata;
149
150                         if(evt->filter == EVFILT_WRITE) {
151                                 io->cb(io->data, IO_WRITE);
152                         } else if(evt->filter == EVFILT_READ) {
153                                 io->cb(io->data, IO_READ);
154                         } else {
155                                 continue;
156                         }
157
158                         if(curgen != io_tree.generation) {
159                                 break;
160                         }
161                 }
162         }
163
164         event_deinit();
165         return true;
166 }
167
168 void event_exit(void) {
169         running = false;
170 }