1 /* $OpenBSD: poll.c,v 1.22 2016/09/03 11:31:17 nayden Exp $ */
2
3 /*
4 * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/queue.h>
33
34 #include <poll.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #ifdef CHECK_INVARIANTS
42 #include <assert.h>
43 #endif
44
45 #include "event.h"
46 #include "event-internal.h"
47 #include "evsignal.h"
48 #include "log.h"
49
50 struct pollop {
51 int event_count; /* Highest number alloc */
52 int nfds; /* Size of event_* */
53 int fd_count; /* Size of idxplus1_by_fd */
54 struct pollfd *event_set;
55 struct event **event_r_back;
56 struct event **event_w_back;
57 int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so
58 * that 0 (which is easy to memset) can mean
59 * "no entry." */
60 };
61
62 static void *poll_init (struct event_base *);
63 static int poll_add (void *, struct event *);
64 static int poll_del (void *, struct event *);
65 static int poll_dispatch (struct event_base *, void *, struct timeval *);
66 static void poll_dealloc (struct event_base *, void *);
67
68 const struct eventop pollops = {
69 "poll",
70 poll_init,
71 poll_add,
72 poll_del,
73 poll_dispatch,
74 poll_dealloc,
75 0
76 };
77
78 static void *
poll_init(struct event_base * base)79 poll_init(struct event_base *base)
80 {
81 struct pollop *pollop;
82
83 /* Disable poll when this environment variable is set */
84 if (!issetugid() && getenv("EVENT_NOPOLL"))
85 return (NULL);
86
87 if (!(pollop = calloc(1, sizeof(struct pollop))))
88 return (NULL);
89
90 evsignal_init(base);
91
92 return (pollop);
93 }
94
95 #ifdef CHECK_INVARIANTS
96 static void
poll_check_ok(struct pollop * pop)97 poll_check_ok(struct pollop *pop)
98 {
99 int i, idx;
100 struct event *ev;
101
102 for (i = 0; i < pop->fd_count; ++i) {
103 idx = pop->idxplus1_by_fd[i]-1;
104 if (idx < 0)
105 continue;
106 assert(pop->event_set[idx].fd == i);
107 if (pop->event_set[idx].events & POLLIN) {
108 ev = pop->event_r_back[idx];
109 assert(ev);
110 assert(ev->ev_events & EV_READ);
111 assert(ev->ev_fd == i);
112 }
113 if (pop->event_set[idx].events & POLLOUT) {
114 ev = pop->event_w_back[idx];
115 assert(ev);
116 assert(ev->ev_events & EV_WRITE);
117 assert(ev->ev_fd == i);
118 }
119 }
120 for (i = 0; i < pop->nfds; ++i) {
121 struct pollfd *pfd = &pop->event_set[i];
122 assert(pop->idxplus1_by_fd[pfd->fd] == i+1);
123 }
124 }
125 #else
126 #define poll_check_ok(pop)
127 #endif
128
129 static int
poll_dispatch(struct event_base * base,void * arg,struct timeval * tv)130 poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
131 {
132 int res, i, j, msec = -1, nfds;
133 struct pollop *pop = arg;
134
135 poll_check_ok(pop);
136
137 if (tv != NULL)
138 msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
139
140 nfds = pop->nfds;
141 res = poll(pop->event_set, nfds, msec);
142
143 if (res == -1) {
144 if (errno != EINTR) {
145 event_warn("poll");
146 return (-1);
147 }
148
149 evsignal_process(base);
150 return (0);
151 } else if (base->sig.evsignal_caught) {
152 evsignal_process(base);
153 }
154
155 event_debug(("%s: poll reports %d", __func__, res));
156
157 if (res == 0 || nfds == 0)
158 return (0);
159
160 i = arc4random_uniform(nfds);
161 for (j = 0; j < nfds; j++) {
162 struct event *r_ev = NULL, *w_ev = NULL;
163 int what;
164 if (++i == nfds)
165 i = 0;
166 what = pop->event_set[i].revents;
167
168 if (!what)
169 continue;
170
171 res = 0;
172
173 /* If the file gets closed notify */
174 if (what & (POLLHUP|POLLERR))
175 what |= POLLIN|POLLOUT;
176 if (what & POLLIN) {
177 res |= EV_READ;
178 r_ev = pop->event_r_back[i];
179 }
180 if (what & POLLOUT) {
181 res |= EV_WRITE;
182 w_ev = pop->event_w_back[i];
183 }
184 if (res == 0)
185 continue;
186
187 if (r_ev && (res & r_ev->ev_events)) {
188 event_active(r_ev, res & r_ev->ev_events, 1);
189 }
190 if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
191 event_active(w_ev, res & w_ev->ev_events, 1);
192 }
193 }
194
195 return (0);
196 }
197
198 static int
poll_add(void * arg,struct event * ev)199 poll_add(void *arg, struct event *ev)
200 {
201 struct pollop *pop = arg;
202 struct pollfd *pfd = NULL;
203 int i;
204
205 if (ev->ev_events & EV_SIGNAL)
206 return (evsignal_add(ev));
207 if (!(ev->ev_events & (EV_READ|EV_WRITE)))
208 return (0);
209
210 poll_check_ok(pop);
211 if (pop->nfds + 1 >= pop->event_count) {
212 struct pollfd *tmp_event_set;
213 struct event **tmp_event_r_back;
214 struct event **tmp_event_w_back;
215 int tmp_event_count;
216
217 if (pop->event_count < 32)
218 tmp_event_count = 32;
219 else
220 tmp_event_count = pop->event_count * 2;
221
222 /* We need more file descriptors */
223 tmp_event_set = reallocarray(pop->event_set,
224 tmp_event_count, sizeof(struct pollfd));
225 if (tmp_event_set == NULL) {
226 event_warn("realloc");
227 return (-1);
228 }
229 pop->event_set = tmp_event_set;
230
231 tmp_event_r_back = reallocarray(pop->event_r_back,
232 tmp_event_count, sizeof(struct event *));
233 if (tmp_event_r_back == NULL) {
234 /* event_set overallocated; that's okay. */
235 event_warn("realloc");
236 return (-1);
237 }
238 pop->event_r_back = tmp_event_r_back;
239
240 tmp_event_w_back = reallocarray(pop->event_w_back,
241 tmp_event_count, sizeof(struct event *));
242 if (tmp_event_w_back == NULL) {
243 /* event_set and event_r_back overallocated; that's
244 * okay. */
245 event_warn("realloc");
246 return (-1);
247 }
248 pop->event_w_back = tmp_event_w_back;
249
250 pop->event_count = tmp_event_count;
251 }
252 if (ev->ev_fd >= pop->fd_count) {
253 int *tmp_idxplus1_by_fd;
254 int new_count;
255 if (pop->fd_count < 32)
256 new_count = 32;
257 else
258 new_count = pop->fd_count * 2;
259 while (new_count <= ev->ev_fd)
260 new_count *= 2;
261 tmp_idxplus1_by_fd = reallocarray(pop->idxplus1_by_fd,
262 new_count, sizeof(int));
263 if (tmp_idxplus1_by_fd == NULL) {
264 event_warn("realloc");
265 return (-1);
266 }
267 pop->idxplus1_by_fd = tmp_idxplus1_by_fd;
268 memset(pop->idxplus1_by_fd + pop->fd_count,
269 0, sizeof(int)*(new_count - pop->fd_count));
270 pop->fd_count = new_count;
271 }
272
273 i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
274 if (i >= 0) {
275 pfd = &pop->event_set[i];
276 } else {
277 i = pop->nfds++;
278 pfd = &pop->event_set[i];
279 pfd->events = 0;
280 pfd->fd = ev->ev_fd;
281 pop->event_w_back[i] = pop->event_r_back[i] = NULL;
282 pop->idxplus1_by_fd[ev->ev_fd] = i + 1;
283 }
284
285 pfd->revents = 0;
286 if (ev->ev_events & EV_WRITE) {
287 pfd->events |= POLLOUT;
288 pop->event_w_back[i] = ev;
289 }
290 if (ev->ev_events & EV_READ) {
291 pfd->events |= POLLIN;
292 pop->event_r_back[i] = ev;
293 }
294 poll_check_ok(pop);
295
296 return (0);
297 }
298
299 /*
300 * Nothing to be done here.
301 */
302
303 static int
poll_del(void * arg,struct event * ev)304 poll_del(void *arg, struct event *ev)
305 {
306 struct pollop *pop = arg;
307 struct pollfd *pfd = NULL;
308 int i;
309
310 if (ev->ev_events & EV_SIGNAL)
311 return (evsignal_del(ev));
312
313 if (!(ev->ev_events & (EV_READ|EV_WRITE)))
314 return (0);
315
316 poll_check_ok(pop);
317 i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
318 if (i < 0)
319 return (-1);
320
321 /* Do we still want to read or write? */
322 pfd = &pop->event_set[i];
323 if (ev->ev_events & EV_READ) {
324 pfd->events &= ~POLLIN;
325 pop->event_r_back[i] = NULL;
326 }
327 if (ev->ev_events & EV_WRITE) {
328 pfd->events &= ~POLLOUT;
329 pop->event_w_back[i] = NULL;
330 }
331 poll_check_ok(pop);
332 if (pfd->events)
333 /* Another event cares about that fd. */
334 return (0);
335
336 /* Okay, so we aren't interested in that fd anymore. */
337 pop->idxplus1_by_fd[ev->ev_fd] = 0;
338
339 --pop->nfds;
340 if (i != pop->nfds) {
341 /*
342 * Shift the last pollfd down into the now-unoccupied
343 * position.
344 */
345 memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
346 sizeof(struct pollfd));
347 pop->event_r_back[i] = pop->event_r_back[pop->nfds];
348 pop->event_w_back[i] = pop->event_w_back[pop->nfds];
349 pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1;
350 }
351
352 poll_check_ok(pop);
353 return (0);
354 }
355
356 static void
poll_dealloc(struct event_base * base,void * arg)357 poll_dealloc(struct event_base *base, void *arg)
358 {
359 struct pollop *pop = arg;
360
361 evsignal_dealloc(base);
362 free(pop->event_set);
363 free(pop->event_r_back);
364 free(pop->event_w_back);
365 free(pop->idxplus1_by_fd);
366
367 memset(pop, 0, sizeof(struct pollop));
368 free(pop);
369 }
370