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_EPOLL_H 32# include <sys/epoll.h> 33#endif 34#ifdef HAVE_FCNTL_H 35# include <fcntl.h> 36#endif 37 38#ifdef HAVE_EPOLL 39 40typedef struct { 41 int epoll_fd; 42} ares_evsys_epoll_t; 43 44static void ares_evsys_epoll_destroy(ares_event_thread_t *e) 45{ 46 ares_evsys_epoll_t *ep = NULL; 47 48 if (e == NULL) { 49 return; 50 } 51 52 ep = e->ev_sys_data; 53 if (ep == NULL) { 54 return; 55 } 56 57 if (ep->epoll_fd != -1) { 58 close(ep->epoll_fd); 59 } 60 61 ares_free(ep); 62 e->ev_sys_data = NULL; 63} 64 65static ares_bool_t ares_evsys_epoll_init(ares_event_thread_t *e) 66{ 67 ares_evsys_epoll_t *ep = NULL; 68 69 ep = ares_malloc_zero(sizeof(*ep)); 70 if (ep == NULL) { 71 return ARES_FALSE; 72 } 73 74 e->ev_sys_data = ep; 75 76 ep->epoll_fd = epoll_create1(0); 77 if (ep->epoll_fd == -1) { 78 ares_evsys_epoll_destroy(e); 79 return ARES_FALSE; 80 } 81 82# ifdef FD_CLOEXEC 83 fcntl(ep->epoll_fd, F_SETFD, FD_CLOEXEC); 84# endif 85 86 e->ev_signal = ares_pipeevent_create(e); 87 if (e->ev_signal == NULL) { 88 ares_evsys_epoll_destroy(e); 89 return ARES_FALSE; 90 } 91 92 return ARES_TRUE; 93} 94 95static ares_bool_t ares_evsys_epoll_event_add(ares_event_t *event) 96{ 97 const ares_event_thread_t *e = event->e; 98 const ares_evsys_epoll_t *ep = e->ev_sys_data; 99 struct epoll_event epev; 100 101 memset(&epev, 0, sizeof(epev)); 102 epev.data.fd = event->fd; 103 epev.events = EPOLLRDHUP | EPOLLERR | EPOLLHUP; 104 if (event->flags & ARES_EVENT_FLAG_READ) { 105 epev.events |= EPOLLIN; 106 } 107 if (event->flags & ARES_EVENT_FLAG_WRITE) { 108 epev.events |= EPOLLOUT; 109 } 110 if (epoll_ctl(ep->epoll_fd, EPOLL_CTL_ADD, event->fd, &epev) != 0) { 111 return ARES_FALSE; 112 } 113 return ARES_TRUE; 114} 115 116static void ares_evsys_epoll_event_del(ares_event_t *event) 117{ 118 const ares_event_thread_t *e = event->e; 119 const ares_evsys_epoll_t *ep = e->ev_sys_data; 120 struct epoll_event epev; 121 122 memset(&epev, 0, sizeof(epev)); 123 epev.data.fd = event->fd; 124 epoll_ctl(ep->epoll_fd, EPOLL_CTL_DEL, event->fd, &epev); 125} 126 127static void ares_evsys_epoll_event_mod(ares_event_t *event, 128 ares_event_flags_t new_flags) 129{ 130 const ares_event_thread_t *e = event->e; 131 const ares_evsys_epoll_t *ep = e->ev_sys_data; 132 struct epoll_event epev; 133 134 memset(&epev, 0, sizeof(epev)); 135 epev.data.fd = event->fd; 136 epev.events = EPOLLRDHUP | EPOLLERR | EPOLLHUP; 137 if (new_flags & ARES_EVENT_FLAG_READ) { 138 epev.events |= EPOLLIN; 139 } 140 if (new_flags & ARES_EVENT_FLAG_WRITE) { 141 epev.events |= EPOLLOUT; 142 } 143 epoll_ctl(ep->epoll_fd, EPOLL_CTL_MOD, event->fd, &epev); 144} 145 146static size_t ares_evsys_epoll_wait(ares_event_thread_t *e, 147 unsigned long timeout_ms) 148{ 149 struct epoll_event events[8]; 150 size_t nevents = sizeof(events) / sizeof(*events); 151 const ares_evsys_epoll_t *ep = e->ev_sys_data; 152 int rv; 153 size_t i; 154 size_t cnt = 0; 155 156 memset(events, 0, sizeof(events)); 157 158 rv = epoll_wait(ep->epoll_fd, events, (int)nevents, 159 (timeout_ms == 0) ? -1 : (int)timeout_ms); 160 if (rv < 0) { 161 return 0; 162 } 163 164 nevents = (size_t)rv; 165 166 for (i = 0; i < nevents; i++) { 167 ares_event_t *ev; 168 ares_event_flags_t flags = 0; 169 170 ev = ares__htable_asvp_get_direct(e->ev_handles, 171 (ares_socket_t)events[i].data.fd); 172 if (ev == NULL || ev->cb == NULL) { 173 continue; 174 } 175 176 cnt++; 177 178 if (events[i].events & (EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR)) { 179 flags |= ARES_EVENT_FLAG_READ; 180 } 181 if (events[i].events & EPOLLOUT) { 182 flags |= ARES_EVENT_FLAG_WRITE; 183 } 184 185 ev->cb(e, ev->fd, ev->data, flags); 186 } 187 188 return cnt; 189} 190 191const ares_event_sys_t ares_evsys_epoll = { "epoll", 192 ares_evsys_epoll_init, 193 ares_evsys_epoll_destroy, 194 ares_evsys_epoll_event_add, 195 ares_evsys_epoll_event_del, 196 ares_evsys_epoll_event_mod, 197 ares_evsys_epoll_wait }; 198#endif 199