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