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