1 /* MIT License
2 *
3 * Copyright (c) 2024 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #include "ares_setup.h"
27 #include "ares.h"
28 #include "ares_private.h"
29 #include "ares_event.h"
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34 #ifdef HAVE_SYS_EVENT_H
35 # include <sys/event.h>
36 #endif
37 #ifdef HAVE_SYS_TIME_H
38 # include <sys/time.h>
39 #endif
40 #ifdef HAVE_FCNTL_H
41 # include <fcntl.h>
42 #endif
43
44 #ifdef HAVE_KQUEUE
45
46 typedef struct {
47 int kqueue_fd;
48 struct kevent *changelist;
49 size_t nchanges;
50 size_t nchanges_alloc;
51 } ares_evsys_kqueue_t;
52
ares_evsys_kqueue_destroy(ares_event_thread_t * e)53 static void ares_evsys_kqueue_destroy(ares_event_thread_t *e)
54 {
55 ares_evsys_kqueue_t *kq = NULL;
56
57 if (e == NULL) {
58 return;
59 }
60
61 kq = e->ev_sys_data;
62 if (kq == NULL) {
63 return;
64 }
65
66 if (kq->kqueue_fd != -1) {
67 close(kq->kqueue_fd);
68 }
69
70 ares_free(kq->changelist);
71 ares_free(kq);
72 e->ev_sys_data = NULL;
73 }
74
ares_evsys_kqueue_init(ares_event_thread_t * e)75 static ares_bool_t ares_evsys_kqueue_init(ares_event_thread_t *e)
76 {
77 ares_evsys_kqueue_t *kq = NULL;
78
79 kq = ares_malloc_zero(sizeof(*kq));
80 if (kq == NULL) {
81 return ARES_FALSE;
82 }
83
84 e->ev_sys_data = kq;
85
86 kq->kqueue_fd = kqueue();
87 if (kq->kqueue_fd == -1) {
88 ares_evsys_kqueue_destroy(e);
89 return ARES_FALSE;
90 }
91
92 # ifdef FD_CLOEXEC
93 fcntl(kq->kqueue_fd, F_SETFD, FD_CLOEXEC);
94 # endif
95
96 kq->nchanges_alloc = 8;
97 kq->changelist =
98 ares_malloc_zero(sizeof(*kq->changelist) * kq->nchanges_alloc);
99 if (kq->changelist == NULL) {
100 ares_evsys_kqueue_destroy(e);
101 return ARES_FALSE;
102 }
103
104 e->ev_signal = ares_pipeevent_create(e);
105 if (e->ev_signal == NULL) {
106 ares_evsys_kqueue_destroy(e);
107 return ARES_FALSE;
108 }
109
110 return ARES_TRUE;
111 }
112
ares_evsys_kqueue_enqueue(ares_evsys_kqueue_t * kq,int fd,int16_t filter,uint16_t flags)113 static void ares_evsys_kqueue_enqueue(ares_evsys_kqueue_t *kq, int fd,
114 int16_t filter, uint16_t flags)
115 {
116 size_t idx;
117
118 if (kq == NULL) {
119 return;
120 }
121
122 idx = kq->nchanges;
123
124 kq->nchanges++;
125
126 if (kq->nchanges > kq->nchanges_alloc) {
127 kq->nchanges_alloc <<= 1;
128 kq->changelist = ares_realloc_zero(kq->changelist, kq->nchanges_alloc >> 1,
129 kq->nchanges_alloc);
130 }
131
132 EV_SET(&kq->changelist[idx], fd, filter, flags, 0, 0, 0);
133 }
134
ares_evsys_kqueue_event_process(ares_event_t * event,ares_event_flags_t old_flags,ares_event_flags_t new_flags)135 static void ares_evsys_kqueue_event_process(ares_event_t *event,
136 ares_event_flags_t old_flags,
137 ares_event_flags_t new_flags)
138 {
139 ares_event_thread_t *e = event->e;
140 ares_evsys_kqueue_t *kq;
141
142 if (e == NULL) {
143 return;
144 }
145
146 kq = e->ev_sys_data;
147 if (kq == NULL) {
148 return;
149 }
150
151 if (new_flags & ARES_EVENT_FLAG_READ && !(old_flags & ARES_EVENT_FLAG_READ)) {
152 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_READ, EV_ADD | EV_ENABLE);
153 }
154
155 if (!(new_flags & ARES_EVENT_FLAG_READ) && old_flags & ARES_EVENT_FLAG_READ) {
156 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_READ, EV_DELETE);
157 }
158
159 if (new_flags & ARES_EVENT_FLAG_WRITE &&
160 !(old_flags & ARES_EVENT_FLAG_WRITE)) {
161 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_WRITE, EV_ADD | EV_ENABLE);
162 }
163
164 if (!(new_flags & ARES_EVENT_FLAG_WRITE) &&
165 old_flags & ARES_EVENT_FLAG_WRITE) {
166 ares_evsys_kqueue_enqueue(kq, event->fd, EVFILT_WRITE, EV_DELETE);
167 }
168 }
169
ares_evsys_kqueue_event_add(ares_event_t * event)170 static ares_bool_t ares_evsys_kqueue_event_add(ares_event_t *event)
171 {
172 ares_evsys_kqueue_event_process(event, 0, event->flags);
173 return ARES_TRUE;
174 }
175
ares_evsys_kqueue_event_del(ares_event_t * event)176 static void ares_evsys_kqueue_event_del(ares_event_t *event)
177 {
178 ares_evsys_kqueue_event_process(event, event->flags, 0);
179 }
180
ares_evsys_kqueue_event_mod(ares_event_t * event,ares_event_flags_t new_flags)181 static void ares_evsys_kqueue_event_mod(ares_event_t *event,
182 ares_event_flags_t new_flags)
183 {
184 ares_evsys_kqueue_event_process(event, event->flags, new_flags);
185 }
186
ares_evsys_kqueue_wait(ares_event_thread_t * e,unsigned long timeout_ms)187 static size_t ares_evsys_kqueue_wait(ares_event_thread_t *e,
188 unsigned long timeout_ms)
189 {
190 struct kevent events[8];
191 size_t nevents = sizeof(events) / sizeof(*events);
192 ares_evsys_kqueue_t *kq = e->ev_sys_data;
193 int rv;
194 size_t i;
195 struct timespec ts;
196 struct timespec *timeout = NULL;
197 size_t cnt = 0;
198
199 if (timeout_ms != 0) {
200 ts.tv_sec = timeout_ms / 1000;
201 ts.tv_nsec = (timeout_ms % 1000) * 1000 * 1000;
202 timeout = &ts;
203 }
204
205 memset(events, 0, sizeof(events));
206
207 rv = kevent(kq->kqueue_fd, kq->changelist, (int)kq->nchanges, events,
208 (int)nevents, timeout);
209 if (rv < 0) {
210 return 0;
211 }
212
213 /* Changelist was consumed */
214 kq->nchanges = 0;
215 nevents = (size_t)rv;
216
217 for (i = 0; i < nevents; i++) {
218 ares_event_t *ev;
219 ares_event_flags_t flags = 0;
220
221 ev = ares__htable_asvp_get_direct(e->ev_handles,
222 (ares_socket_t)events[i].ident);
223 if (ev == NULL || ev->cb == NULL) {
224 continue;
225 }
226
227 cnt++;
228
229 if (events[i].filter == EVFILT_READ ||
230 events[i].flags & (EV_EOF | EV_ERROR)) {
231 flags |= ARES_EVENT_FLAG_READ;
232 } else {
233 flags |= ARES_EVENT_FLAG_WRITE;
234 }
235
236 ev->cb(e, ev->fd, ev->data, flags);
237 }
238
239 return cnt;
240 }
241
242 const ares_event_sys_t ares_evsys_kqueue = { "kqueue",
243 ares_evsys_kqueue_init,
244 ares_evsys_kqueue_destroy,
245 ares_evsys_kqueue_event_add,
246 ares_evsys_kqueue_event_del,
247 ares_evsys_kqueue_event_mod,
248 ares_evsys_kqueue_wait };
249 #endif
250