Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/util/event_select.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2013-2015 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <sys/param.h> /* for howmany() on Linux */
22
#include <sys/time.h>
23
#ifdef HAVE_SYS_SYSMACROS_H
24
# include <sys/sysmacros.h> /* for howmany() on Solaris */
25
#endif
26
#ifdef HAVE_SYS_SELECT_H
27
# include <sys/select.h>
28
#endif /* HAVE_SYS_SELECT_H */
29
#include <stdlib.h>
30
#include <string.h>
31
#include <time.h>
32
33
#include <sudo_compat.h>
34
#include <sudo_util.h>
35
#include <sudo_fatal.h>
36
#include <sudo_debug.h>
37
#include <sudo_event.h>
38
39
int
40
sudo_ev_base_alloc_impl(struct sudo_event_base *base)
41
{
42
debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT);
43
44
base->maxfd = NFDBITS - 1;
45
base->readfds_in = calloc(1, sizeof(fd_mask));
46
base->writefds_in = calloc(1, sizeof(fd_mask));
47
base->readfds_out = calloc(1, sizeof(fd_mask));
48
base->writefds_out = calloc(1, sizeof(fd_mask));
49
50
if (base->readfds_in == NULL || base->writefds_in == NULL ||
51
base->readfds_out == NULL || base->writefds_out == NULL) {
52
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
53
"%s: unable to calloc(1, %zu)", __func__, sizeof(fd_mask));
54
sudo_ev_base_free_impl(base);
55
debug_return_int(-1);
56
}
57
debug_return_int(0);
58
}
59
60
void
61
sudo_ev_base_free_impl(struct sudo_event_base *base)
62
{
63
debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT);
64
free(base->readfds_in);
65
free(base->writefds_in);
66
free(base->readfds_out);
67
free(base->writefds_out);
68
debug_return;
69
}
70
71
int
72
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
73
{
74
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT);
75
76
/* If out of space in fd sets, realloc. */
77
if (ev->fd > base->maxfd) {
78
const int o = (base->maxfd + 1) / NFDBITS;
79
const int n = howmany(ev->fd + 1, NFDBITS);
80
const size_t used_bytes = (size_t)o * sizeof(fd_mask);
81
const size_t new_bytes = (size_t)(n - o) * sizeof(fd_mask);
82
fd_set *rfds_in, *wfds_in, *rfds_out, *wfds_out;
83
84
rfds_in = reallocarray(base->readfds_in, (size_t)n, sizeof(fd_mask));
85
wfds_in = reallocarray(base->writefds_in, (size_t)n, sizeof(fd_mask));
86
rfds_out = reallocarray(base->readfds_out, (size_t)n, sizeof(fd_mask));
87
wfds_out = reallocarray(base->writefds_out, (size_t)n, sizeof(fd_mask));
88
if (rfds_in == NULL || wfds_in == NULL ||
89
rfds_out == NULL || wfds_out == NULL) {
90
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
91
"%s: unable to reallocarray(%d, %zu)",
92
__func__, n, sizeof(fd_mask));
93
free(rfds_in);
94
free(wfds_in);
95
free(rfds_out);
96
free(wfds_out);
97
debug_return_int(-1);
98
}
99
100
/* Clear newly allocated space. */
101
memset((char *)rfds_in + used_bytes, 0, new_bytes);
102
memset((char *)wfds_in + used_bytes, 0, new_bytes);
103
memset((char *)rfds_out + used_bytes, 0, new_bytes);
104
memset((char *)wfds_out + used_bytes, 0, new_bytes);
105
106
/* Update base. */
107
base->readfds_in = rfds_in;
108
base->writefds_in = wfds_in;
109
base->readfds_out = rfds_out;
110
base->writefds_out = wfds_out;
111
base->maxfd = (n * NFDBITS) - 1;
112
}
113
114
/* Set events and adjust high fd as needed. */
115
if (ISSET(ev->events, SUDO_EV_READ)) {
116
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to readfs",
117
__func__, ev->fd);
118
FD_SET(ev->fd, (fd_set *)base->readfds_in);
119
}
120
if (ISSET(ev->events, SUDO_EV_WRITE)) {
121
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to writefds",
122
__func__, ev->fd);
123
FD_SET(ev->fd, (fd_set *)base->writefds_in);
124
}
125
if (ev->fd > base->highfd)
126
base->highfd = ev->fd;
127
128
debug_return_int(0);
129
}
130
131
int
132
sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
133
{
134
debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT);
135
136
/* Remove from readfds and writefds and adjust high fd. */
137
if (ISSET(ev->events, SUDO_EV_READ)) {
138
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: removed fd %d from readfds",
139
__func__, ev->fd);
140
FD_CLR(ev->fd, (fd_set *)base->readfds_in);
141
}
142
if (ISSET(ev->events, SUDO_EV_WRITE)) {
143
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: removed fd %d from writefds",
144
__func__, ev->fd);
145
FD_CLR(ev->fd, (fd_set *)base->writefds_in);
146
}
147
if (base->highfd == ev->fd) {
148
for (;;) {
149
if (FD_ISSET(base->highfd, (fd_set *)base->readfds_in) ||
150
FD_ISSET(base->highfd, (fd_set *)base->writefds_in))
151
break;
152
if (--base->highfd < 0)
153
break;
154
}
155
}
156
157
debug_return_int(0);
158
}
159
160
#ifdef HAVE_PSELECT
161
static int
162
sudo_ev_select(int nfds, fd_set *readfds, fd_set *writefds,
163
fd_set *exceptfds, const struct timespec *timeout)
164
{
165
return pselect(nfds, readfds, writefds, exceptfds, timeout, NULL);
166
}
167
#else
168
static int
169
sudo_ev_select(int nfds, fd_set *readfds, fd_set *writefds,
170
fd_set *exceptfds, const struct timespec *timeout)
171
{
172
struct timeval tvbuf, *tv = NULL;
173
174
if (timeout != NULL) {
175
TIMESPEC_TO_TIMEVAL(&tvbuf, timeout);
176
tv = &tvbuf;
177
}
178
return select(nfds, readfds, writefds, exceptfds, tv);
179
}
180
#endif /* HAVE_PSELECT */
181
182
int
183
sudo_ev_scan_impl(struct sudo_event_base *base, unsigned int flags)
184
{
185
struct timespec now, ts, *timeout;
186
struct sudo_event *ev;
187
size_t setsize;
188
int nready;
189
debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT);
190
191
if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
192
sudo_gettime_mono(&now);
193
sudo_timespecsub(&ev->timeout, &now, &ts);
194
if (ts.tv_sec < 0)
195
sudo_timespecclear(&ts);
196
timeout = &ts;
197
} else {
198
if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
199
sudo_timespecclear(&ts);
200
timeout = &ts;
201
} else {
202
timeout = NULL;
203
}
204
}
205
206
/* select() overwrites readfds/writefds so make a copy. */
207
setsize = (size_t)howmany(base->highfd + 1, NFDBITS) * sizeof(fd_mask);
208
memcpy(base->readfds_out, base->readfds_in, setsize);
209
memcpy(base->writefds_out, base->writefds_in, setsize);
210
211
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: select high fd %d",
212
__func__, base->highfd);
213
nready = sudo_ev_select(base->highfd + 1, base->readfds_out,
214
base->writefds_out, NULL, timeout);
215
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
216
switch (nready) {
217
case -1:
218
/* Error or interrupted by signal. */
219
break;
220
case 0:
221
/* Front end will activate timeout events. */
222
break;
223
default:
224
/* Activate each I/O event that fired. */
225
TAILQ_FOREACH(ev, &base->events, entries) {
226
if (ev->fd >= 0) {
227
short what = 0;
228
if (FD_ISSET(ev->fd, (fd_set *)base->readfds_out))
229
what |= (ev->events & SUDO_EV_READ);
230
if (FD_ISSET(ev->fd, (fd_set *)base->writefds_out))
231
what |= (ev->events & SUDO_EV_WRITE);
232
if (what != 0) {
233
/* Make event active. */
234
sudo_debug_printf(SUDO_DEBUG_DEBUG,
235
"%s: selected fd %d, events %hd, activating %p",
236
__func__, ev->fd, what, ev);
237
ev->revents = what;
238
sudo_ev_activate(base, ev);
239
}
240
}
241
}
242
break;
243
}
244
debug_return_int(nready);
245
}
246
247