2 * Copyright (c) 2013-2015 Todd C. Miller <Todd.Miller@sudo.ws>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
19 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
24 #include <sys/param.h> /* for howmany() on Linux */
26 #ifdef HAVE_SYS_SYSMACROS_H
27 # include <sys/sysmacros.h> /* for howmany() on Solaris */
29 #ifdef HAVE_SYS_SELECT_H
30 # include <sys/select.h>
31 #endif /* HAVE_SYS_SELECT_H */
37 # include "compat/stdbool.h"
38 #endif /* HAVE_STDBOOL_H */
41 #endif /* HAVE_STRING_H */
44 #endif /* HAVE_STRINGS_H */
49 #include "sudo_compat.h"
50 #include "sudo_util.h"
51 #include "sudo_fatal.h"
52 #include "sudo_debug.h"
53 #include "sudo_event.h"
56 sudo_ev_base_alloc_impl(struct sudo_event_base *base)
58 debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT)
60 base->maxfd = NFDBITS - 1;
61 base->readfds_in = calloc(1, sizeof(fd_mask));
62 base->writefds_in = calloc(1, sizeof(fd_mask));
63 base->readfds_out = calloc(1, sizeof(fd_mask));
64 base->writefds_out = calloc(1, sizeof(fd_mask));
66 if (base->readfds_in == NULL || base->writefds_in == NULL ||
67 base->readfds_out == NULL || base->writefds_out == NULL) {
68 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
69 "%s: unable to calloc(1, %zu)", __func__, sizeof(fd_mask));
70 sudo_ev_base_free_impl(base);
77 sudo_ev_base_free_impl(struct sudo_event_base *base)
79 debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT)
80 free(base->readfds_in);
81 free(base->writefds_in);
82 free(base->readfds_out);
83 free(base->writefds_out);
88 sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
90 debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT)
92 /* If out of space in fd sets, realloc. */
93 if (ev->fd > base->maxfd) {
94 const int o = (base->maxfd + 1) / NFDBITS;
95 const int n = howmany(ev->fd + 1, NFDBITS);
96 const size_t used_bytes = o * sizeof(fd_mask);
97 const size_t new_bytes = (n - o) * sizeof(fd_mask);
98 fd_set *rfds_in, *wfds_in, *rfds_out, *wfds_out;
100 rfds_in = reallocarray(base->readfds_in, n, sizeof(fd_mask));
101 wfds_in = reallocarray(base->writefds_in, n, sizeof(fd_mask));
102 rfds_out = reallocarray(base->readfds_out, n, sizeof(fd_mask));
103 wfds_out = reallocarray(base->writefds_out, n, sizeof(fd_mask));
104 if (rfds_in == NULL || wfds_in == NULL ||
105 rfds_out == NULL || wfds_out == NULL) {
106 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
107 "%s: unable to reallocarray(%d, %zu)",
108 __func__, n, sizeof(fd_mask));
113 debug_return_int(-1);
116 /* Clear newly allocated space. */
117 memset((char *)rfds_in + used_bytes, 0, new_bytes);
118 memset((char *)wfds_in + used_bytes, 0, new_bytes);
119 memset((char *)rfds_out + used_bytes, 0, new_bytes);
120 memset((char *)wfds_out + used_bytes, 0, new_bytes);
123 base->readfds_in = rfds_in;
124 base->writefds_in = wfds_in;
125 base->readfds_out = rfds_out;
126 base->writefds_out = wfds_out;
127 base->maxfd = (n * NFDBITS) - 1;
130 /* Set events and adjust high fd as needed. */
131 if (ISSET(ev->events, SUDO_EV_READ)) {
132 sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to readfs",
134 FD_SET(ev->fd, base->readfds_in);
136 if (ISSET(ev->events, SUDO_EV_WRITE)) {
137 sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to writefds",
139 FD_SET(ev->fd, base->writefds_in);
141 if (ev->fd > base->highfd)
142 base->highfd = ev->fd;
148 sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
150 debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT)
152 /* Remove from readfds and writefds and adjust high fd. */
153 if (ISSET(ev->events, SUDO_EV_READ)) {
154 sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: removed fd %d from readfds",
156 FD_CLR(ev->fd, base->readfds_in);
158 if (ISSET(ev->events, SUDO_EV_WRITE)) {
159 sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: removed fd %d from writefds",
161 FD_CLR(ev->fd, base->writefds_in);
163 if (base->highfd == ev->fd) {
165 if (FD_ISSET(base->highfd, base->readfds_in) ||
166 FD_ISSET(base->highfd, base->writefds_in))
168 if (--base->highfd < 0)
178 sudo_ev_select(int nfds, fd_set *readfds, fd_set *writefds,
179 fd_set *exceptfds, const struct timespec *timeout)
181 return pselect(nfds, readfds, writefds, exceptfds, timeout, NULL);
185 sudo_ev_select(int nfds, fd_set *readfds, fd_set *writefds,
186 fd_set *exceptfds, const struct timespec *timeout)
188 struct timeval tvbuf, *tv = NULL;
190 if (timeout != NULL) {
191 TIMESPEC_TO_TIMEVAL(&tvbuf, timeout);
194 return select(nfds, readfds, writefds, exceptfds, tv);
196 #endif /* HAVE_PSELECT */
199 sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
201 struct timespec now, ts, *timeout;
202 struct sudo_event *ev;
205 debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
207 if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
208 sudo_gettime_mono(&now);
209 sudo_timespecsub(&ev->timeout, &now, &ts);
211 sudo_timespecclear(&ts);
214 if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
215 sudo_timespecclear(&ts);
222 /* select() overwrites readfds/writefds so make a copy. */
223 setsize = howmany(base->highfd + 1, NFDBITS) * sizeof(fd_mask);
224 memcpy(base->readfds_out, base->readfds_in, setsize);
225 memcpy(base->writefds_out, base->writefds_in, setsize);
227 sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: select high fd %d",
228 __func__, base->highfd);
229 nready = sudo_ev_select(base->highfd + 1, base->readfds_out,
230 base->writefds_out, NULL, timeout);
231 sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
234 /* Error or interrupted by signal. */
235 debug_return_int(-1);
237 /* Front end will activate timeout events. */
240 /* Activate each I/O event that fired. */
241 TAILQ_FOREACH(ev, &base->events, entries) {
244 if (FD_ISSET(ev->fd, base->readfds_out))
245 what |= (ev->events & SUDO_EV_READ);
246 if (FD_ISSET(ev->fd, base->writefds_out))
247 what |= (ev->events & SUDO_EV_WRITE);
249 /* Make event active. */
250 sudo_debug_printf(SUDO_DEBUG_DEBUG,
251 "%s: selected fd %d, events %d, activating %p",
252 __func__, ev->fd, what, ev);
254 sudo_ev_activate(base, ev);
260 debug_return_int(nready);