Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/util/event_poll.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/resource.h>
22
23
#include <limits.h>
24
#include <poll.h>
25
#include <stdlib.h>
26
#include <time.h>
27
28
#include <sudo_compat.h>
29
#include <sudo_util.h>
30
#include <sudo_fatal.h>
31
#include <sudo_debug.h>
32
#include <sudo_event.h>
33
34
#if defined(OPEN_MAX) && OPEN_MAX > 256
35
# define SUDO_OPEN_MAX OPEN_MAX
36
#else
37
# define SUDO_OPEN_MAX 256
38
#endif
39
40
int
41
sudo_ev_base_alloc_impl(struct sudo_event_base *base)
42
{
43
int i;
44
debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT);
45
46
base->pfd_high = -1;
47
base->pfd_max = 32;
48
base->pfds = reallocarray(NULL, (size_t)base->pfd_max, sizeof(struct pollfd));
49
if (base->pfds == NULL) {
50
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
51
"%s: unable to allocate %d pollfds", __func__, base->pfd_max);
52
base->pfd_max = 0;
53
debug_return_int(-1);
54
}
55
for (i = 0; i < base->pfd_max; i++) {
56
base->pfds[i].fd = -1;
57
}
58
59
debug_return_int(0);
60
}
61
62
void
63
sudo_ev_base_free_impl(struct sudo_event_base *base)
64
{
65
debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT);
66
free(base->pfds);
67
debug_return;
68
}
69
70
int
71
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
72
{
73
static int nofile_max = -1;
74
struct pollfd *pfd;
75
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT);
76
77
if (nofile_max == -1) {
78
struct rlimit rlim;
79
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
80
nofile_max = (int)rlim.rlim_cur;
81
} else {
82
nofile_max = SUDO_OPEN_MAX;
83
}
84
}
85
86
/* If out of space in pfds array, realloc. */
87
if (base->pfd_free == base->pfd_max) {
88
struct pollfd *pfds;
89
int i, new_max;
90
91
/* Don't allow pfd_max to go over RLIM_NOFILE */
92
new_max = base->pfd_max * 2;
93
if (new_max > nofile_max)
94
new_max = nofile_max;
95
if (base->pfd_free == new_max) {
96
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
97
"%s: out of fds (max %d)", __func__, nofile_max);
98
debug_return_int(-1);
99
}
100
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
101
"%s: pfd_max %d -> %d", __func__, base->pfd_max, new_max);
102
pfds = reallocarray(base->pfds, (size_t)new_max, sizeof(struct pollfd));
103
if (pfds == NULL) {
104
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
105
"%s: unable to allocate %d pollfds", __func__, new_max);
106
debug_return_int(-1);
107
}
108
base->pfds = pfds;
109
base->pfd_max = new_max;
110
for (i = base->pfd_free; i < base->pfd_max; i++) {
111
base->pfds[i].fd = -1;
112
}
113
}
114
115
/* Fill in pfd entry. */
116
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
117
"%s: choosing free slot %d", __func__, base->pfd_free);
118
ev->pfd_idx = (short)base->pfd_free;
119
pfd = &base->pfds[ev->pfd_idx];
120
pfd->fd = ev->fd;
121
pfd->events = 0;
122
if (ISSET(ev->events, SUDO_EV_READ))
123
pfd->events |= POLLIN;
124
if (ISSET(ev->events, SUDO_EV_WRITE))
125
pfd->events |= POLLOUT;
126
127
/* Update pfd_high and pfd_free. */
128
if (ev->pfd_idx > base->pfd_high)
129
base->pfd_high = ev->pfd_idx;
130
for (;;) {
131
if (++base->pfd_free == base->pfd_max)
132
break;
133
if (base->pfds[base->pfd_free].fd == -1)
134
break;
135
}
136
137
debug_return_int(0);
138
}
139
140
int
141
sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
142
{
143
debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT);
144
145
/* Mark pfd entry unused, add to free list and adjust high slot. */
146
base->pfds[ev->pfd_idx].fd = -1;
147
if (ev->pfd_idx < base->pfd_free) {
148
base->pfd_free = ev->pfd_idx;
149
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
150
"%s: new free slot %d", __func__, base->pfd_free);
151
}
152
while (base->pfd_high >= 0 && base->pfds[base->pfd_high].fd == -1)
153
base->pfd_high--;
154
155
debug_return_int(0);
156
}
157
158
#ifdef HAVE_PPOLL
159
static int
160
sudo_ev_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timo)
161
{
162
return ppoll(fds, nfds, timo, NULL);
163
}
164
#else
165
static int
166
sudo_ev_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timo)
167
{
168
const int timeout =
169
timo ? (timo->tv_sec * 1000) + (timo->tv_nsec / 1000000) : -1;
170
171
return poll(fds, nfds, timeout);
172
}
173
#endif /* HAVE_PPOLL */
174
175
int
176
sudo_ev_scan_impl(struct sudo_event_base *base, unsigned int flags)
177
{
178
struct timespec now, ts, *timeout;
179
struct sudo_event *ev;
180
int nready;
181
debug_decl(sudo_ev_scan_impl, SUDO_DEBUG_EVENT);
182
183
if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
184
sudo_gettime_mono(&now);
185
sudo_timespecsub(&ev->timeout, &now, &ts);
186
if (ts.tv_sec < 0)
187
sudo_timespecclear(&ts);
188
timeout = &ts;
189
} else {
190
if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
191
sudo_timespecclear(&ts);
192
timeout = &ts;
193
} else {
194
timeout = NULL;
195
}
196
}
197
198
nready = sudo_ev_poll(base->pfds, (nfds_t)base->pfd_high + 1, timeout);
199
switch (nready) {
200
case -1:
201
/* Error: EINTR (signal) or EINVAL (nfds > RLIMIT_NOFILE) */
202
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
203
"sudo_ev_poll");
204
break;
205
case 0:
206
/* Front end will activate timeout events. */
207
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: timeout", __func__);
208
break;
209
default:
210
/* Activate each I/O event that fired. */
211
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__,
212
nready);
213
TAILQ_FOREACH(ev, &base->events, entries) {
214
if (ev->pfd_idx != -1 && base->pfds[ev->pfd_idx].revents) {
215
int what = 0;
216
if (base->pfds[ev->pfd_idx].revents & (POLLIN|POLLHUP|POLLNVAL|POLLERR))
217
what |= (ev->events & SUDO_EV_READ);
218
if (base->pfds[ev->pfd_idx].revents & (POLLOUT|POLLHUP|POLLNVAL|POLLERR))
219
what |= (ev->events & SUDO_EV_WRITE);
220
/* Make event active. */
221
sudo_debug_printf(SUDO_DEBUG_DEBUG,
222
"%s: polled fd %d, events %d, activating %p",
223
__func__, ev->fd, what, ev);
224
ev->revents = (short)what;
225
sudo_ev_activate(base, ev);
226
}
227
}
228
break;
229
}
230
debug_return_int(nready);
231
}
232
233