1*8698d772Snayden /* $OpenBSD: select.c,v 1.25 2016/09/03 11:31:17 nayden Exp $ */
234fc9cdeSmickey
3fd332320Sprovos /*
4fd332320Sprovos * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
5fd332320Sprovos * All rights reserved.
6fd332320Sprovos *
7fd332320Sprovos * Redistribution and use in source and binary forms, with or without
8fd332320Sprovos * modification, are permitted provided that the following conditions
9fd332320Sprovos * are met:
10fd332320Sprovos * 1. Redistributions of source code must retain the above copyright
11fd332320Sprovos * notice, this list of conditions and the following disclaimer.
12fd332320Sprovos * 2. Redistributions in binary form must reproduce the above copyright
13fd332320Sprovos * notice, this list of conditions and the following disclaimer in the
14fd332320Sprovos * 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
16fd332320Sprovos * derived from this software without specific prior written permission.
17fd332320Sprovos *
18fd332320Sprovos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19fd332320Sprovos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20fd332320Sprovos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21fd332320Sprovos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22fd332320Sprovos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23fd332320Sprovos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24fd332320Sprovos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25fd332320Sprovos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26fd332320Sprovos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27fd332320Sprovos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28fd332320Sprovos */
29fd332320Sprovos
30fd332320Sprovos #include <sys/types.h>
31fd332320Sprovos #include <sys/time.h>
32bdce580dSbrad #include <sys/select.h>
33fd332320Sprovos #include <sys/queue.h>
34defc4074Sbluhm
35fd332320Sprovos #include <signal.h>
36fd332320Sprovos #include <stdio.h>
37fd332320Sprovos #include <stdlib.h>
38fd332320Sprovos #include <string.h>
39fd332320Sprovos #include <unistd.h>
40fd332320Sprovos #include <errno.h>
41348ce57bSbrad #ifdef CHECK_INVARIANTS
42348ce57bSbrad #include <assert.h>
43348ce57bSbrad #endif
44fd332320Sprovos
45fd332320Sprovos #include "event.h"
464643be29Sbrad #include "event-internal.h"
471770acb2Smarkus #include "evsignal.h"
484643be29Sbrad #include "log.h"
49fd332320Sprovos
50fd332320Sprovos struct selectop {
51fd332320Sprovos int event_fds; /* Highest fd in fd set */
521b100c43Sbluhm size_t event_fdsz;
53348ce57bSbrad fd_set *event_readset_in;
54348ce57bSbrad fd_set *event_writeset_in;
55348ce57bSbrad fd_set *event_readset_out;
56348ce57bSbrad fd_set *event_writeset_out;
57348ce57bSbrad struct event **event_r_by_fd;
58348ce57bSbrad struct event **event_w_by_fd;
594643be29Sbrad };
60fd332320Sprovos
618ead113eSnicm static void *select_init (struct event_base *);
628ead113eSnicm static int select_add (void *, struct event *);
638ead113eSnicm static int select_del (void *, struct event *);
648ead113eSnicm static int select_dispatch (struct event_base *, void *, struct timeval *);
658ead113eSnicm static void select_dealloc (struct event_base *, void *);
66fd332320Sprovos
67759b8817Smickey const struct eventop selectops = {
68fd332320Sprovos "select",
69fd332320Sprovos select_init,
70fd332320Sprovos select_add,
71fd332320Sprovos select_del,
723ac1ba99Sbrad select_dispatch,
738ead113eSnicm select_dealloc,
748ead113eSnicm 0
75fd332320Sprovos };
76fd332320Sprovos
771b100c43Sbluhm static int select_resize(struct selectop *sop, size_t fdsz);
78348ce57bSbrad
798ead113eSnicm static void *
select_init(struct event_base * base)80bdce580dSbrad select_init(struct event_base *base)
81fd332320Sprovos {
824643be29Sbrad struct selectop *sop;
834643be29Sbrad
84110bbf6fSbrad /* Disable select when this environment variable is set */
8584ea67e3Sbluhm if (!issetugid() && getenv("EVENT_NOSELECT"))
861770acb2Smarkus return (NULL);
871770acb2Smarkus
884643be29Sbrad if (!(sop = calloc(1, sizeof(struct selectop))))
894643be29Sbrad return (NULL);
90fd332320Sprovos
91348ce57bSbrad select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask));
92348ce57bSbrad
93bdce580dSbrad evsignal_init(base);
94fd332320Sprovos
954643be29Sbrad return (sop);
96fd332320Sprovos }
97fd332320Sprovos
98348ce57bSbrad #ifdef CHECK_INVARIANTS
99348ce57bSbrad static void
check_selectop(struct selectop * sop)100348ce57bSbrad check_selectop(struct selectop *sop)
101348ce57bSbrad {
102348ce57bSbrad int i;
103348ce57bSbrad for (i = 0; i <= sop->event_fds; ++i) {
104348ce57bSbrad if (FD_ISSET(i, sop->event_readset_in)) {
105348ce57bSbrad assert(sop->event_r_by_fd[i]);
106348ce57bSbrad assert(sop->event_r_by_fd[i]->ev_events & EV_READ);
107348ce57bSbrad assert(sop->event_r_by_fd[i]->ev_fd == i);
108348ce57bSbrad } else {
109348ce57bSbrad assert(! sop->event_r_by_fd[i]);
110348ce57bSbrad }
111348ce57bSbrad if (FD_ISSET(i, sop->event_writeset_in)) {
112348ce57bSbrad assert(sop->event_w_by_fd[i]);
113348ce57bSbrad assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE);
114348ce57bSbrad assert(sop->event_w_by_fd[i]->ev_fd == i);
115348ce57bSbrad } else {
116348ce57bSbrad assert(! sop->event_w_by_fd[i]);
117348ce57bSbrad }
118348ce57bSbrad }
119348ce57bSbrad
120348ce57bSbrad }
121348ce57bSbrad #else
1227567a386Smillert #define check_selectop(sop) do { (void) sop; } while (0)
123348ce57bSbrad #endif
124348ce57bSbrad
1258ead113eSnicm static int
select_dispatch(struct event_base * base,void * arg,struct timeval * tv)1264643be29Sbrad select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
127fd332320Sprovos {
1288ead113eSnicm int res, i, j;
129fd332320Sprovos struct selectop *sop = arg;
130fd332320Sprovos
131348ce57bSbrad check_selectop(sop);
132fd332320Sprovos
133348ce57bSbrad memcpy(sop->event_readset_out, sop->event_readset_in,
134348ce57bSbrad sop->event_fdsz);
135348ce57bSbrad memcpy(sop->event_writeset_out, sop->event_writeset_in,
136348ce57bSbrad sop->event_fdsz);
137fd332320Sprovos
138348ce57bSbrad res = select(sop->event_fds + 1, sop->event_readset_out,
139348ce57bSbrad sop->event_writeset_out, NULL, tv);
140fd332320Sprovos
141348ce57bSbrad check_selectop(sop);
142fd332320Sprovos
143fd332320Sprovos if (res == -1) {
144fd332320Sprovos if (errno != EINTR) {
1454643be29Sbrad event_warn("select");
146fd332320Sprovos return (-1);
147fd332320Sprovos }
148fd332320Sprovos
149bdce580dSbrad evsignal_process(base);
150fd332320Sprovos return (0);
151bdce580dSbrad } else if (base->sig.evsignal_caught) {
152bdce580dSbrad evsignal_process(base);
153bdce580dSbrad }
154fd332320Sprovos
1554643be29Sbrad event_debug(("%s: select reports %d", __func__, res));
156fd332320Sprovos
157348ce57bSbrad check_selectop(sop);
1589ce3ea75Sdlg i = arc4random_uniform(sop->event_fds + 1);
1598ead113eSnicm for (j = 0; j <= sop->event_fds; ++j) {
160348ce57bSbrad struct event *r_ev = NULL, *w_ev = NULL;
1618ead113eSnicm if (++i >= sop->event_fds+1)
1628ead113eSnicm i = 0;
1638ead113eSnicm
164fd332320Sprovos res = 0;
165348ce57bSbrad if (FD_ISSET(i, sop->event_readset_out)) {
166348ce57bSbrad r_ev = sop->event_r_by_fd[i];
167fd332320Sprovos res |= EV_READ;
168fd332320Sprovos }
169348ce57bSbrad if (FD_ISSET(i, sop->event_writeset_out)) {
170348ce57bSbrad w_ev = sop->event_w_by_fd[i];
171348ce57bSbrad res |= EV_WRITE;
172348ce57bSbrad }
173348ce57bSbrad if (r_ev && (res & r_ev->ev_events)) {
174348ce57bSbrad event_active(r_ev, res & r_ev->ev_events, 1);
175348ce57bSbrad }
176348ce57bSbrad if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
177348ce57bSbrad event_active(w_ev, res & w_ev->ev_events, 1);
178348ce57bSbrad }
179348ce57bSbrad }
180348ce57bSbrad check_selectop(sop);
181fd332320Sprovos
182fd332320Sprovos return (0);
183fd332320Sprovos }
184fd332320Sprovos
185348ce57bSbrad
186348ce57bSbrad static int
select_resize(struct selectop * sop,size_t fdsz)1871b100c43Sbluhm select_resize(struct selectop *sop, size_t fdsz)
188348ce57bSbrad {
1891b100c43Sbluhm size_t n_events, n_events_old;
190348ce57bSbrad
191348ce57bSbrad fd_set *readset_in = NULL;
192348ce57bSbrad fd_set *writeset_in = NULL;
193348ce57bSbrad fd_set *readset_out = NULL;
194348ce57bSbrad fd_set *writeset_out = NULL;
195348ce57bSbrad struct event **r_by_fd = NULL;
196348ce57bSbrad struct event **w_by_fd = NULL;
197348ce57bSbrad
198348ce57bSbrad n_events = (fdsz/sizeof(fd_mask)) * NFDBITS;
199348ce57bSbrad n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS;
200348ce57bSbrad
201348ce57bSbrad if (sop->event_readset_in)
202348ce57bSbrad check_selectop(sop);
203348ce57bSbrad
204348ce57bSbrad if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL)
205348ce57bSbrad goto error;
206348ce57bSbrad sop->event_readset_in = readset_in;
207348ce57bSbrad if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL)
208348ce57bSbrad goto error;
209348ce57bSbrad sop->event_readset_out = readset_out;
210348ce57bSbrad if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL)
211348ce57bSbrad goto error;
212348ce57bSbrad sop->event_writeset_in = writeset_in;
213348ce57bSbrad if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL)
214348ce57bSbrad goto error;
215348ce57bSbrad sop->event_writeset_out = writeset_out;
216aa682362Sdoug if ((r_by_fd = reallocarray(sop->event_r_by_fd, n_events,
217aa682362Sdoug sizeof(struct event *))) == NULL)
218348ce57bSbrad goto error;
219348ce57bSbrad sop->event_r_by_fd = r_by_fd;
220aa682362Sdoug if ((w_by_fd = reallocarray(sop->event_w_by_fd, n_events,
221aa682362Sdoug sizeof(struct event *))) == NULL)
222348ce57bSbrad goto error;
223348ce57bSbrad sop->event_w_by_fd = w_by_fd;
224348ce57bSbrad
225348ce57bSbrad memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
226348ce57bSbrad fdsz - sop->event_fdsz);
227348ce57bSbrad memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
228348ce57bSbrad fdsz - sop->event_fdsz);
229348ce57bSbrad memset(sop->event_r_by_fd + n_events_old, 0,
230348ce57bSbrad (n_events-n_events_old) * sizeof(struct event*));
231348ce57bSbrad memset(sop->event_w_by_fd + n_events_old, 0,
232348ce57bSbrad (n_events-n_events_old) * sizeof(struct event*));
233348ce57bSbrad
234348ce57bSbrad sop->event_fdsz = fdsz;
235348ce57bSbrad check_selectop(sop);
236348ce57bSbrad
237348ce57bSbrad return (0);
238348ce57bSbrad
239348ce57bSbrad error:
240348ce57bSbrad event_warn("malloc");
241348ce57bSbrad return (-1);
242348ce57bSbrad }
243348ce57bSbrad
244348ce57bSbrad
2458ead113eSnicm static int
select_add(void * arg,struct event * ev)246fd332320Sprovos select_add(void *arg, struct event *ev)
247fd332320Sprovos {
248fd332320Sprovos struct selectop *sop = arg;
249fd332320Sprovos
2501770acb2Smarkus if (ev->ev_events & EV_SIGNAL)
251bdce580dSbrad return (evsignal_add(ev));
252fd332320Sprovos
253348ce57bSbrad check_selectop(sop);
254fd332320Sprovos /*
255fd332320Sprovos * Keep track of the highest fd, so that we can calculate the size
256fd332320Sprovos * of the fd_sets for select(2)
257fd332320Sprovos */
258348ce57bSbrad if (sop->event_fds < ev->ev_fd) {
2591b100c43Sbluhm size_t fdsz = sop->event_fdsz;
260348ce57bSbrad
261348ce57bSbrad if (fdsz < sizeof(fd_mask))
262348ce57bSbrad fdsz = sizeof(fd_mask);
263348ce57bSbrad
264348ce57bSbrad while (fdsz <
265348ce57bSbrad (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask)))
266348ce57bSbrad fdsz *= 2;
267348ce57bSbrad
268348ce57bSbrad if (fdsz != sop->event_fdsz) {
269348ce57bSbrad if (select_resize(sop, fdsz)) {
270348ce57bSbrad check_selectop(sop);
271348ce57bSbrad return (-1);
272348ce57bSbrad }
273348ce57bSbrad }
274348ce57bSbrad
275fd332320Sprovos sop->event_fds = ev->ev_fd;
276348ce57bSbrad }
277348ce57bSbrad
278348ce57bSbrad if (ev->ev_events & EV_READ) {
279348ce57bSbrad FD_SET(ev->ev_fd, sop->event_readset_in);
280348ce57bSbrad sop->event_r_by_fd[ev->ev_fd] = ev;
281348ce57bSbrad }
282348ce57bSbrad if (ev->ev_events & EV_WRITE) {
283348ce57bSbrad FD_SET(ev->ev_fd, sop->event_writeset_in);
284348ce57bSbrad sop->event_w_by_fd[ev->ev_fd] = ev;
285348ce57bSbrad }
286348ce57bSbrad check_selectop(sop);
287fd332320Sprovos
288fd332320Sprovos return (0);
289fd332320Sprovos }
290fd332320Sprovos
291fd332320Sprovos /*
292fd332320Sprovos * Nothing to be done here.
293fd332320Sprovos */
294fd332320Sprovos
2958ead113eSnicm static int
select_del(void * arg,struct event * ev)296fd332320Sprovos select_del(void *arg, struct event *ev)
297fd332320Sprovos {
298fd332320Sprovos struct selectop *sop = arg;
299fd332320Sprovos
300348ce57bSbrad check_selectop(sop);
301348ce57bSbrad if (ev->ev_events & EV_SIGNAL)
302bdce580dSbrad return (evsignal_del(ev));
303348ce57bSbrad
304348ce57bSbrad if (sop->event_fds < ev->ev_fd) {
305348ce57bSbrad check_selectop(sop);
306348ce57bSbrad return (0);
307348ce57bSbrad }
308348ce57bSbrad
309348ce57bSbrad if (ev->ev_events & EV_READ) {
310348ce57bSbrad FD_CLR(ev->ev_fd, sop->event_readset_in);
311348ce57bSbrad sop->event_r_by_fd[ev->ev_fd] = NULL;
312348ce57bSbrad }
313348ce57bSbrad
314348ce57bSbrad if (ev->ev_events & EV_WRITE) {
315348ce57bSbrad FD_CLR(ev->ev_fd, sop->event_writeset_in);
316348ce57bSbrad sop->event_w_by_fd[ev->ev_fd] = NULL;
317348ce57bSbrad }
318348ce57bSbrad
319348ce57bSbrad check_selectop(sop);
320348ce57bSbrad return (0);
321fd332320Sprovos }
3223ac1ba99Sbrad
3238ead113eSnicm static void
select_dealloc(struct event_base * base,void * arg)324bdce580dSbrad select_dealloc(struct event_base *base, void *arg)
3253ac1ba99Sbrad {
3263ac1ba99Sbrad struct selectop *sop = arg;
3273ac1ba99Sbrad
328bdce580dSbrad evsignal_dealloc(base);
3293ac1ba99Sbrad free(sop->event_readset_in);
3303ac1ba99Sbrad free(sop->event_writeset_in);
3313ac1ba99Sbrad free(sop->event_readset_out);
3323ac1ba99Sbrad free(sop->event_writeset_out);
3333ac1ba99Sbrad free(sop->event_r_by_fd);
3343ac1ba99Sbrad free(sop->event_w_by_fd);
3353ac1ba99Sbrad
3363ac1ba99Sbrad memset(sop, 0, sizeof(struct selectop));
3373ac1ba99Sbrad free(sop);
3383ac1ba99Sbrad }
339