xref: /openbsd/lib/libevent/select.c (revision 8698d772)
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