1*8698d772Snayden /* $OpenBSD: poll.c,v 1.22 2016/09/03 11:31:17 nayden Exp $ */
2378aa367Smarkus
3378aa367Smarkus /*
4378aa367Smarkus * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
5378aa367Smarkus * All rights reserved.
6378aa367Smarkus *
7378aa367Smarkus * Redistribution and use in source and binary forms, with or without
8378aa367Smarkus * modification, are permitted provided that the following conditions
9378aa367Smarkus * are met:
10378aa367Smarkus * 1. Redistributions of source code must retain the above copyright
11378aa367Smarkus * notice, this list of conditions and the following disclaimer.
12378aa367Smarkus * 2. Redistributions in binary form must reproduce the above copyright
13378aa367Smarkus * notice, this list of conditions and the following disclaimer in the
14378aa367Smarkus * documentation and/or other materials provided with the distribution.
15ff9272daSbrad * 3. The name of the author may not be used to endorse or promote products
16378aa367Smarkus * derived from this software without specific prior written permission.
17378aa367Smarkus *
18378aa367Smarkus * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19378aa367Smarkus * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20378aa367Smarkus * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21378aa367Smarkus * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22378aa367Smarkus * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23378aa367Smarkus * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24378aa367Smarkus * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25378aa367Smarkus * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26378aa367Smarkus * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27378aa367Smarkus * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28378aa367Smarkus */
29378aa367Smarkus
30378aa367Smarkus #include <sys/types.h>
31378aa367Smarkus #include <sys/time.h>
32378aa367Smarkus #include <sys/queue.h>
33defc4074Sbluhm
34378aa367Smarkus #include <poll.h>
35378aa367Smarkus #include <signal.h>
36378aa367Smarkus #include <stdio.h>
37378aa367Smarkus #include <stdlib.h>
38378aa367Smarkus #include <string.h>
39378aa367Smarkus #include <unistd.h>
40378aa367Smarkus #include <errno.h>
41348ce57bSbrad #ifdef CHECK_INVARIANTS
42348ce57bSbrad #include <assert.h>
43348ce57bSbrad #endif
44378aa367Smarkus
45378aa367Smarkus #include "event.h"
464643be29Sbrad #include "event-internal.h"
47378aa367Smarkus #include "evsignal.h"
484643be29Sbrad #include "log.h"
49378aa367Smarkus
50378aa367Smarkus struct pollop {
51378aa367Smarkus int event_count; /* Highest number alloc */
52348ce57bSbrad int nfds; /* Size of event_* */
531085edd8Sbrad int fd_count; /* Size of idxplus1_by_fd */
54378aa367Smarkus struct pollfd *event_set;
551085edd8Sbrad struct event **event_r_back;
561085edd8Sbrad struct event **event_w_back;
571085edd8Sbrad int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so
581085edd8Sbrad * that 0 (which is easy to memset) can mean
591085edd8Sbrad * "no entry." */
604643be29Sbrad };
61378aa367Smarkus
628ead113eSnicm static void *poll_init (struct event_base *);
638ead113eSnicm static int poll_add (void *, struct event *);
648ead113eSnicm static int poll_del (void *, struct event *);
658ead113eSnicm static int poll_dispatch (struct event_base *, void *, struct timeval *);
668ead113eSnicm static void poll_dealloc (struct event_base *, void *);
67378aa367Smarkus
68ae8df7afSderaadt const struct eventop pollops = {
69378aa367Smarkus "poll",
70378aa367Smarkus poll_init,
71378aa367Smarkus poll_add,
72378aa367Smarkus poll_del,
733ac1ba99Sbrad poll_dispatch,
748ead113eSnicm poll_dealloc,
758ead113eSnicm 0
76378aa367Smarkus };
77378aa367Smarkus
788ead113eSnicm static void *
poll_init(struct event_base * base)79bdce580dSbrad poll_init(struct event_base *base)
80378aa367Smarkus {
814643be29Sbrad struct pollop *pollop;
824643be29Sbrad
83110bbf6fSbrad /* Disable poll when this environment variable is set */
8484ea67e3Sbluhm if (!issetugid() && getenv("EVENT_NOPOLL"))
85378aa367Smarkus return (NULL);
86378aa367Smarkus
874643be29Sbrad if (!(pollop = calloc(1, sizeof(struct pollop))))
884643be29Sbrad return (NULL);
89378aa367Smarkus
90bdce580dSbrad evsignal_init(base);
91378aa367Smarkus
924643be29Sbrad return (pollop);
93378aa367Smarkus }
94378aa367Smarkus
95348ce57bSbrad #ifdef CHECK_INVARIANTS
96348ce57bSbrad static void
poll_check_ok(struct pollop * pop)97348ce57bSbrad poll_check_ok(struct pollop *pop)
98348ce57bSbrad {
99348ce57bSbrad int i, idx;
100348ce57bSbrad struct event *ev;
101348ce57bSbrad
102348ce57bSbrad for (i = 0; i < pop->fd_count; ++i) {
103348ce57bSbrad idx = pop->idxplus1_by_fd[i]-1;
104348ce57bSbrad if (idx < 0)
105348ce57bSbrad continue;
106348ce57bSbrad assert(pop->event_set[idx].fd == i);
107348ce57bSbrad if (pop->event_set[idx].events & POLLIN) {
108348ce57bSbrad ev = pop->event_r_back[idx];
109348ce57bSbrad assert(ev);
110348ce57bSbrad assert(ev->ev_events & EV_READ);
111348ce57bSbrad assert(ev->ev_fd == i);
112348ce57bSbrad }
113348ce57bSbrad if (pop->event_set[idx].events & POLLOUT) {
114348ce57bSbrad ev = pop->event_w_back[idx];
115348ce57bSbrad assert(ev);
116348ce57bSbrad assert(ev->ev_events & EV_WRITE);
117348ce57bSbrad assert(ev->ev_fd == i);
118348ce57bSbrad }
119348ce57bSbrad }
120348ce57bSbrad for (i = 0; i < pop->nfds; ++i) {
121348ce57bSbrad struct pollfd *pfd = &pop->event_set[i];
122348ce57bSbrad assert(pop->idxplus1_by_fd[pfd->fd] == i+1);
123348ce57bSbrad }
124348ce57bSbrad }
125348ce57bSbrad #else
126348ce57bSbrad #define poll_check_ok(pop)
127348ce57bSbrad #endif
128348ce57bSbrad
1298ead113eSnicm static int
poll_dispatch(struct event_base * base,void * arg,struct timeval * tv)1304643be29Sbrad poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
131378aa367Smarkus {
1328ead113eSnicm int res, i, j, msec = -1, nfds;
133378aa367Smarkus struct pollop *pop = arg;
134378aa367Smarkus
135348ce57bSbrad poll_check_ok(pop);
136378aa367Smarkus
137bdce580dSbrad if (tv != NULL)
138bdce580dSbrad msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
139bdce580dSbrad
140bdce580dSbrad nfds = pop->nfds;
141bdce580dSbrad res = poll(pop->event_set, nfds, msec);
142378aa367Smarkus
143378aa367Smarkus if (res == -1) {
144378aa367Smarkus if (errno != EINTR) {
1454643be29Sbrad event_warn("poll");
146378aa367Smarkus return (-1);
147378aa367Smarkus }
148378aa367Smarkus
149bdce580dSbrad evsignal_process(base);
150378aa367Smarkus return (0);
151bdce580dSbrad } else if (base->sig.evsignal_caught) {
152bdce580dSbrad evsignal_process(base);
153bdce580dSbrad }
154378aa367Smarkus
1554643be29Sbrad event_debug(("%s: poll reports %d", __func__, res));
156378aa367Smarkus
1578ead113eSnicm if (res == 0 || nfds == 0)
158378aa367Smarkus return (0);
159378aa367Smarkus
1609ce3ea75Sdlg i = arc4random_uniform(nfds);
1618ead113eSnicm for (j = 0; j < nfds; j++) {
1621085edd8Sbrad struct event *r_ev = NULL, *w_ev = NULL;
1638ead113eSnicm int what;
1648ead113eSnicm if (++i == nfds)
1658ead113eSnicm i = 0;
1668ead113eSnicm what = pop->event_set[i].revents;
167ff9272daSbrad
168348ce57bSbrad if (!what)
169348ce57bSbrad continue;
170348ce57bSbrad
171378aa367Smarkus res = 0;
172ff9272daSbrad
1735b97e645Smarkus /* If the file gets closed notify */
174348ce57bSbrad if (what & (POLLHUP|POLLERR))
175ff9272daSbrad what |= POLLIN|POLLOUT;
1761085edd8Sbrad if (what & POLLIN) {
1775b97e645Smarkus res |= EV_READ;
1781085edd8Sbrad r_ev = pop->event_r_back[i];
1791085edd8Sbrad }
1801085edd8Sbrad if (what & POLLOUT) {
1815b97e645Smarkus res |= EV_WRITE;
1821085edd8Sbrad w_ev = pop->event_w_back[i];
1831085edd8Sbrad }
184378aa367Smarkus if (res == 0)
185378aa367Smarkus continue;
186378aa367Smarkus
1871085edd8Sbrad if (r_ev && (res & r_ev->ev_events)) {
1881085edd8Sbrad event_active(r_ev, res & r_ev->ev_events, 1);
1891085edd8Sbrad }
1901085edd8Sbrad if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
1911085edd8Sbrad event_active(w_ev, res & w_ev->ev_events, 1);
192378aa367Smarkus }
193378aa367Smarkus }
194378aa367Smarkus
195378aa367Smarkus return (0);
196378aa367Smarkus }
197378aa367Smarkus
1988ead113eSnicm static int
poll_add(void * arg,struct event * ev)199378aa367Smarkus poll_add(void *arg, struct event *ev)
200378aa367Smarkus {
201378aa367Smarkus struct pollop *pop = arg;
202348ce57bSbrad struct pollfd *pfd = NULL;
203348ce57bSbrad int i;
204378aa367Smarkus
205378aa367Smarkus if (ev->ev_events & EV_SIGNAL)
206bdce580dSbrad return (evsignal_add(ev));
207348ce57bSbrad if (!(ev->ev_events & (EV_READ|EV_WRITE)))
208348ce57bSbrad return (0);
209348ce57bSbrad
210348ce57bSbrad poll_check_ok(pop);
211348ce57bSbrad if (pop->nfds + 1 >= pop->event_count) {
212a04202bdSbrad struct pollfd *tmp_event_set;
213a04202bdSbrad struct event **tmp_event_r_back;
214a04202bdSbrad struct event **tmp_event_w_back;
215a04202bdSbrad int tmp_event_count;
216a04202bdSbrad
217348ce57bSbrad if (pop->event_count < 32)
218a04202bdSbrad tmp_event_count = 32;
219348ce57bSbrad else
220a04202bdSbrad tmp_event_count = pop->event_count * 2;
221348ce57bSbrad
222348ce57bSbrad /* We need more file descriptors */
2232af34b00Sderaadt tmp_event_set = reallocarray(pop->event_set,
2242af34b00Sderaadt tmp_event_count, sizeof(struct pollfd));
225a04202bdSbrad if (tmp_event_set == NULL) {
226348ce57bSbrad event_warn("realloc");
227348ce57bSbrad return (-1);
228348ce57bSbrad }
229a04202bdSbrad pop->event_set = tmp_event_set;
230a04202bdSbrad
2312af34b00Sderaadt tmp_event_r_back = reallocarray(pop->event_r_back,
2322af34b00Sderaadt tmp_event_count, sizeof(struct event *));
233a04202bdSbrad if (tmp_event_r_back == NULL) {
234a04202bdSbrad /* event_set overallocated; that's okay. */
235348ce57bSbrad event_warn("realloc");
236348ce57bSbrad return (-1);
237348ce57bSbrad }
238a04202bdSbrad pop->event_r_back = tmp_event_r_back;
239a04202bdSbrad
2402af34b00Sderaadt tmp_event_w_back = reallocarray(pop->event_w_back,
2412af34b00Sderaadt tmp_event_count, sizeof(struct event *));
242a04202bdSbrad if (tmp_event_w_back == NULL) {
243a04202bdSbrad /* event_set and event_r_back overallocated; that's
244a04202bdSbrad * okay. */
245a04202bdSbrad event_warn("realloc");
246a04202bdSbrad return (-1);
247a04202bdSbrad }
248a04202bdSbrad pop->event_w_back = tmp_event_w_back;
249a04202bdSbrad
250a04202bdSbrad pop->event_count = tmp_event_count;
251348ce57bSbrad }
252348ce57bSbrad if (ev->ev_fd >= pop->fd_count) {
253a04202bdSbrad int *tmp_idxplus1_by_fd;
254348ce57bSbrad int new_count;
255348ce57bSbrad if (pop->fd_count < 32)
256348ce57bSbrad new_count = 32;
257348ce57bSbrad else
258348ce57bSbrad new_count = pop->fd_count * 2;
259348ce57bSbrad while (new_count <= ev->ev_fd)
260348ce57bSbrad new_count *= 2;
2612af34b00Sderaadt tmp_idxplus1_by_fd = reallocarray(pop->idxplus1_by_fd,
2622af34b00Sderaadt new_count, sizeof(int));
263a04202bdSbrad if (tmp_idxplus1_by_fd == NULL) {
264348ce57bSbrad event_warn("realloc");
265348ce57bSbrad return (-1);
266348ce57bSbrad }
267a04202bdSbrad pop->idxplus1_by_fd = tmp_idxplus1_by_fd;
268348ce57bSbrad memset(pop->idxplus1_by_fd + pop->fd_count,
269348ce57bSbrad 0, sizeof(int)*(new_count - pop->fd_count));
270348ce57bSbrad pop->fd_count = new_count;
271348ce57bSbrad }
272348ce57bSbrad
273348ce57bSbrad i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
274348ce57bSbrad if (i >= 0) {
275348ce57bSbrad pfd = &pop->event_set[i];
276348ce57bSbrad } else {
277348ce57bSbrad i = pop->nfds++;
278348ce57bSbrad pfd = &pop->event_set[i];
279348ce57bSbrad pfd->events = 0;
280348ce57bSbrad pfd->fd = ev->ev_fd;
281348ce57bSbrad pop->event_w_back[i] = pop->event_r_back[i] = NULL;
282348ce57bSbrad pop->idxplus1_by_fd[ev->ev_fd] = i + 1;
283348ce57bSbrad }
284348ce57bSbrad
285348ce57bSbrad pfd->revents = 0;
286348ce57bSbrad if (ev->ev_events & EV_WRITE) {
287348ce57bSbrad pfd->events |= POLLOUT;
288348ce57bSbrad pop->event_w_back[i] = ev;
289348ce57bSbrad }
290348ce57bSbrad if (ev->ev_events & EV_READ) {
291348ce57bSbrad pfd->events |= POLLIN;
292348ce57bSbrad pop->event_r_back[i] = ev;
293348ce57bSbrad }
294348ce57bSbrad poll_check_ok(pop);
295378aa367Smarkus
296378aa367Smarkus return (0);
297378aa367Smarkus }
298378aa367Smarkus
299378aa367Smarkus /*
300378aa367Smarkus * Nothing to be done here.
301378aa367Smarkus */
302378aa367Smarkus
3038ead113eSnicm static int
poll_del(void * arg,struct event * ev)304378aa367Smarkus poll_del(void *arg, struct event *ev)
305378aa367Smarkus {
306378aa367Smarkus struct pollop *pop = arg;
307348ce57bSbrad struct pollfd *pfd = NULL;
308348ce57bSbrad int i;
309378aa367Smarkus
310348ce57bSbrad if (ev->ev_events & EV_SIGNAL)
311bdce580dSbrad return (evsignal_del(ev));
312348ce57bSbrad
313348ce57bSbrad if (!(ev->ev_events & (EV_READ|EV_WRITE)))
314378aa367Smarkus return (0);
315378aa367Smarkus
316348ce57bSbrad poll_check_ok(pop);
317348ce57bSbrad i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
318348ce57bSbrad if (i < 0)
319348ce57bSbrad return (-1);
320348ce57bSbrad
321348ce57bSbrad /* Do we still want to read or write? */
322348ce57bSbrad pfd = &pop->event_set[i];
323348ce57bSbrad if (ev->ev_events & EV_READ) {
324348ce57bSbrad pfd->events &= ~POLLIN;
325348ce57bSbrad pop->event_r_back[i] = NULL;
326348ce57bSbrad }
327348ce57bSbrad if (ev->ev_events & EV_WRITE) {
328348ce57bSbrad pfd->events &= ~POLLOUT;
329348ce57bSbrad pop->event_w_back[i] = NULL;
330348ce57bSbrad }
331348ce57bSbrad poll_check_ok(pop);
332348ce57bSbrad if (pfd->events)
333348ce57bSbrad /* Another event cares about that fd. */
334348ce57bSbrad return (0);
335348ce57bSbrad
336348ce57bSbrad /* Okay, so we aren't interested in that fd anymore. */
337348ce57bSbrad pop->idxplus1_by_fd[ev->ev_fd] = 0;
338348ce57bSbrad
339348ce57bSbrad --pop->nfds;
340348ce57bSbrad if (i != pop->nfds) {
341348ce57bSbrad /*
342348ce57bSbrad * Shift the last pollfd down into the now-unoccupied
343348ce57bSbrad * position.
344348ce57bSbrad */
345348ce57bSbrad memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
346348ce57bSbrad sizeof(struct pollfd));
347348ce57bSbrad pop->event_r_back[i] = pop->event_r_back[pop->nfds];
348348ce57bSbrad pop->event_w_back[i] = pop->event_w_back[pop->nfds];
349348ce57bSbrad pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1;
350348ce57bSbrad }
351348ce57bSbrad
352348ce57bSbrad poll_check_ok(pop);
353348ce57bSbrad return (0);
354378aa367Smarkus }
3553ac1ba99Sbrad
3568ead113eSnicm static void
poll_dealloc(struct event_base * base,void * arg)357bdce580dSbrad poll_dealloc(struct event_base *base, void *arg)
3583ac1ba99Sbrad {
3593ac1ba99Sbrad struct pollop *pop = arg;
3603ac1ba99Sbrad
361bdce580dSbrad evsignal_dealloc(base);
3623ac1ba99Sbrad free(pop->event_set);
3633ac1ba99Sbrad free(pop->event_r_back);
3643ac1ba99Sbrad free(pop->event_w_back);
3653ac1ba99Sbrad free(pop->idxplus1_by_fd);
3663ac1ba99Sbrad
3673ac1ba99Sbrad memset(pop, 0, sizeof(struct pollop));
3683ac1ba99Sbrad free(pop);
3693ac1ba99Sbrad }
370