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#ifdef HAVE_SYS_SELECT_H
31#  include <sys/select.h>
32#endif
33
34/* All systems have select(), but not all have a way to wake, so we require
35 * pipe() to wake the select() */
36#if defined(HAVE_PIPE)
37
38static ares_bool_t ares_evsys_select_init(ares_event_thread_t *e)
39{
40  e->ev_signal = ares_pipeevent_create(e);
41  if (e->ev_signal == NULL) {
42    return ARES_FALSE;
43  }
44  return ARES_TRUE;
45}
46
47static void ares_evsys_select_destroy(ares_event_thread_t *e)
48{
49  (void)e;
50}
51
52static ares_bool_t ares_evsys_select_event_add(ares_event_t *event)
53{
54  (void)event;
55  return ARES_TRUE;
56}
57
58static void ares_evsys_select_event_del(ares_event_t *event)
59{
60  (void)event;
61}
62
63static void ares_evsys_select_event_mod(ares_event_t      *event,
64                                        ares_event_flags_t new_flags)
65{
66  (void)event;
67  (void)new_flags;
68}
69
70static size_t ares_evsys_select_wait(ares_event_thread_t *e,
71                                     unsigned long        timeout_ms)
72{
73  size_t          num_fds = 0;
74  ares_socket_t  *fdlist  = ares__htable_asvp_keys(e->ev_handles, &num_fds);
75  int             rv;
76  size_t          cnt = 0;
77  size_t          i;
78  fd_set          read_fds;
79  fd_set          write_fds;
80  int             nfds = 0;
81  struct timeval  tv;
82  struct timeval *tout = NULL;
83
84  FD_ZERO(&read_fds);
85  FD_ZERO(&write_fds);
86
87  for (i = 0; i < num_fds; i++) {
88    const ares_event_t *ev =
89      ares__htable_asvp_get_direct(e->ev_handles, fdlist[i]);
90    if (ev->flags & ARES_EVENT_FLAG_READ) {
91      FD_SET(ev->fd, &read_fds);
92    }
93    if (ev->flags & ARES_EVENT_FLAG_WRITE) {
94      FD_SET(ev->fd, &write_fds);
95    }
96    if (ev->fd + 1 > nfds) {
97      nfds = ev->fd + 1;
98    }
99  }
100
101  if (timeout_ms) {
102    tv.tv_sec  = (int)(timeout_ms / 1000);
103    tv.tv_usec = (int)((timeout_ms % 1000) * 1000);
104    tout       = &tv;
105  }
106
107  rv = select(nfds, &read_fds, &write_fds, NULL, tout);
108  if (rv > 0) {
109    for (i = 0; i < num_fds; i++) {
110      ares_event_t      *ev;
111      ares_event_flags_t flags = 0;
112
113      ev = ares__htable_asvp_get_direct(e->ev_handles, fdlist[i]);
114      if (ev == NULL || ev->cb == NULL) {
115        continue;
116      }
117
118      if (FD_ISSET(fdlist[i], &read_fds)) {
119        flags |= ARES_EVENT_FLAG_READ;
120      }
121
122      if (FD_ISSET(fdlist[i], &write_fds)) {
123        flags |= ARES_EVENT_FLAG_WRITE;
124      }
125
126      if (flags == 0) {
127        continue;
128      }
129
130      cnt++;
131
132      ev->cb(e, fdlist[i], ev->data, flags);
133    }
134  }
135
136  ares_free(fdlist);
137
138  return cnt;
139}
140
141const ares_event_sys_t ares_evsys_select = {
142  "select",
143  ares_evsys_select_init,
144  ares_evsys_select_destroy,   /* NoOp */
145  ares_evsys_select_event_add, /* NoOp */
146  ares_evsys_select_event_del, /* NoOp */
147  ares_evsys_select_event_mod, /* NoOp */
148  ares_evsys_select_wait
149};
150
151#endif
152