• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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