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 46typedef struct { 47 int kqueue_fd; 48 struct kevent *changelist; 49 size_t nchanges; 50 size_t nchanges_alloc; 51} ares_evsys_kqueue_t; 52 53static 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 75static 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 113static 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 135static 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 170static 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 176static void ares_evsys_kqueue_event_del(ares_event_t *event) 177{ 178 ares_evsys_kqueue_event_process(event, event->flags, 0); 179} 180 181static 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 187static 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 242const 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