xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/select.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: select.c,v 1.6 2024/08/18 20:47:21 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*	$OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $	*/
48585484eSchristos 
58585484eSchristos /*
68585484eSchristos  * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
78585484eSchristos  * Copyright 2007-2012 Niels Provos and Nick Mathewson
88585484eSchristos  *
98585484eSchristos  * Redistribution and use in source and binary forms, with or without
108585484eSchristos  * modification, are permitted provided that the following conditions
118585484eSchristos  * are met:
128585484eSchristos  * 1. Redistributions of source code must retain the above copyright
138585484eSchristos  *    notice, this list of conditions and the following disclaimer.
148585484eSchristos  * 2. Redistributions in binary form must reproduce the above copyright
158585484eSchristos  *    notice, this list of conditions and the following disclaimer in the
168585484eSchristos  *    documentation and/or other materials provided with the distribution.
178585484eSchristos  * 3. The name of the author may not be used to endorse or promote products
188585484eSchristos  *    derived from this software without specific prior written permission.
198585484eSchristos  *
208585484eSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
218585484eSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
228585484eSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
238585484eSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
248585484eSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
258585484eSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
268585484eSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
278585484eSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
288585484eSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
298585484eSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
308585484eSchristos  */
318585484eSchristos #include "event2/event-config.h"
328585484eSchristos #include "evconfig-private.h"
338585484eSchristos 
348585484eSchristos #ifdef EVENT__HAVE_SELECT
358585484eSchristos 
368585484eSchristos #ifdef __APPLE__
378585484eSchristos /* Apple wants us to define this if we might ever pass more than
388585484eSchristos  * FD_SETSIZE bits to select(). */
398585484eSchristos #define _DARWIN_UNLIMITED_SELECT
408585484eSchristos #endif
418585484eSchristos 
428585484eSchristos #include <sys/types.h>
438585484eSchristos #ifdef EVENT__HAVE_SYS_TIME_H
448585484eSchristos #include <sys/time.h>
458585484eSchristos #endif
468585484eSchristos #ifdef EVENT__HAVE_SYS_SELECT_H
478585484eSchristos #include <sys/select.h>
488585484eSchristos #endif
498585484eSchristos #include <sys/queue.h>
508585484eSchristos #include <signal.h>
518585484eSchristos #include <stdio.h>
528585484eSchristos #include <stdlib.h>
538585484eSchristos #include <string.h>
548585484eSchristos #include <unistd.h>
558585484eSchristos #include <errno.h>
568585484eSchristos 
578585484eSchristos #include "event-internal.h"
588585484eSchristos #include "evsignal-internal.h"
598585484eSchristos #include "event2/thread.h"
608585484eSchristos #include "evthread-internal.h"
618585484eSchristos #include "log-internal.h"
628585484eSchristos #include "evmap-internal.h"
638585484eSchristos 
648585484eSchristos #ifndef EVENT__HAVE_FD_MASK
658585484eSchristos /* This type is mandatory, but Android doesn't define it. */
668585484eSchristos typedef unsigned long fd_mask;
678585484eSchristos #endif
688585484eSchristos 
698585484eSchristos #ifndef NFDBITS
708585484eSchristos #define NFDBITS (sizeof(fd_mask)*8)
718585484eSchristos #endif
728585484eSchristos 
738585484eSchristos /* Divide positive x by y, rounding up. */
748585484eSchristos #define DIV_ROUNDUP(x, y)   (((x)+((y)-1))/(y))
758585484eSchristos 
768585484eSchristos /* How many bytes to allocate for N fds? */
778585484eSchristos #define SELECT_ALLOC_SIZE(n) \
788585484eSchristos 	(DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask))
798585484eSchristos 
808585484eSchristos struct selectop {
818585484eSchristos 	int event_fds;		/* Highest fd in fd set */
828585484eSchristos 	int event_fdsz;
838585484eSchristos 	int resize_out_sets;
848585484eSchristos 	fd_set *event_readset_in;
858585484eSchristos 	fd_set *event_writeset_in;
868585484eSchristos 	fd_set *event_readset_out;
878585484eSchristos 	fd_set *event_writeset_out;
888585484eSchristos };
898585484eSchristos 
908585484eSchristos static void *select_init(struct event_base *);
918585484eSchristos static int select_add(struct event_base *, int, short old, short events, void*);
928585484eSchristos static int select_del(struct event_base *, int, short old, short events, void*);
938585484eSchristos static int select_dispatch(struct event_base *, struct timeval *);
948585484eSchristos static void select_dealloc(struct event_base *);
958585484eSchristos 
968585484eSchristos const struct eventop selectops = {
978585484eSchristos 	"select",
988585484eSchristos 	select_init,
998585484eSchristos 	select_add,
1008585484eSchristos 	select_del,
1018585484eSchristos 	select_dispatch,
1028585484eSchristos 	select_dealloc,
103*eabc0478Schristos 	1, /* need_reinit. */
1048585484eSchristos 	EV_FEATURE_FDS,
1058585484eSchristos 	0,
1068585484eSchristos };
1078585484eSchristos 
1088585484eSchristos static int select_resize(struct selectop *sop, int fdsz);
1098585484eSchristos static void select_free_selectop(struct selectop *sop);
1108585484eSchristos 
1118585484eSchristos static void *
1128585484eSchristos select_init(struct event_base *base)
1138585484eSchristos {
1148585484eSchristos 	struct selectop *sop;
1158585484eSchristos 
1168585484eSchristos 	if (!(sop = mm_calloc(1, sizeof(struct selectop))))
1178585484eSchristos 		return (NULL);
1188585484eSchristos 
1198585484eSchristos 	if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
1208585484eSchristos 		select_free_selectop(sop);
1218585484eSchristos 		return (NULL);
1228585484eSchristos 	}
1238585484eSchristos 
1248585484eSchristos 	evsig_init_(base);
1258585484eSchristos 
1268585484eSchristos 	evutil_weakrand_seed_(&base->weakrand_seed, 0);
1278585484eSchristos 
1288585484eSchristos 	return (sop);
1298585484eSchristos }
1308585484eSchristos 
1318585484eSchristos #ifdef CHECK_INVARIANTS
1328585484eSchristos static void
1338585484eSchristos check_selectop(struct selectop *sop)
1348585484eSchristos {
1358585484eSchristos 	/* nothing to be done here */
1368585484eSchristos }
1378585484eSchristos #else
1388585484eSchristos #define check_selectop(sop) do { (void) sop; } while (0)
1398585484eSchristos #endif
1408585484eSchristos 
1418585484eSchristos static int
1428585484eSchristos select_dispatch(struct event_base *base, struct timeval *tv)
1438585484eSchristos {
1448585484eSchristos 	int res=0, i, j, nfds;
1458585484eSchristos 	struct selectop *sop = base->evbase;
1468585484eSchristos 
1478585484eSchristos 	check_selectop(sop);
1488585484eSchristos 	if (sop->resize_out_sets) {
1498585484eSchristos 		fd_set *readset_out=NULL, *writeset_out=NULL;
1508585484eSchristos 		size_t sz = sop->event_fdsz;
1518585484eSchristos 		if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
1528585484eSchristos 			return (-1);
1538585484eSchristos 		sop->event_readset_out = readset_out;
1548585484eSchristos 		if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
1558585484eSchristos 			/* We don't free readset_out here, since it was
1568585484eSchristos 			 * already successfully reallocated. The next time
1578585484eSchristos 			 * we call select_dispatch, the realloc will be a
1588585484eSchristos 			 * no-op. */
1598585484eSchristos 			return (-1);
1608585484eSchristos 		}
1618585484eSchristos 		sop->event_writeset_out = writeset_out;
1628585484eSchristos 		sop->resize_out_sets = 0;
1638585484eSchristos 	}
1648585484eSchristos 
1658585484eSchristos 	memcpy(sop->event_readset_out, sop->event_readset_in,
1668585484eSchristos 	       sop->event_fdsz);
1678585484eSchristos 	memcpy(sop->event_writeset_out, sop->event_writeset_in,
1688585484eSchristos 	       sop->event_fdsz);
1698585484eSchristos 
1708585484eSchristos 	nfds = sop->event_fds+1;
1718585484eSchristos 
1728585484eSchristos 	EVBASE_RELEASE_LOCK(base, th_base_lock);
1738585484eSchristos 
1748585484eSchristos 	res = select(nfds, sop->event_readset_out,
1758585484eSchristos 	    sop->event_writeset_out, NULL, tv);
1768585484eSchristos 
1778585484eSchristos 	EVBASE_ACQUIRE_LOCK(base, th_base_lock);
1788585484eSchristos 
1798585484eSchristos 	check_selectop(sop);
1808585484eSchristos 
1818585484eSchristos 	if (res == -1) {
1828585484eSchristos 		if (errno != EINTR) {
1838585484eSchristos 			event_warn("select");
1848585484eSchristos 			return (-1);
1858585484eSchristos 		}
1868585484eSchristos 
1878585484eSchristos 		return (0);
1888585484eSchristos 	}
1898585484eSchristos 
1908585484eSchristos 	event_debug(("%s: select reports %d", __func__, res));
1918585484eSchristos 
1928585484eSchristos 	check_selectop(sop);
1938585484eSchristos 	i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
1948585484eSchristos 	for (j = 0; j < nfds; ++j) {
1958585484eSchristos 		if (++i >= nfds)
1968585484eSchristos 			i = 0;
1978585484eSchristos 		res = 0;
1988585484eSchristos 		if (FD_ISSET(i, sop->event_readset_out))
1998585484eSchristos 			res |= EV_READ;
2008585484eSchristos 		if (FD_ISSET(i, sop->event_writeset_out))
2018585484eSchristos 			res |= EV_WRITE;
2028585484eSchristos 
2038585484eSchristos 		if (res == 0)
2048585484eSchristos 			continue;
2058585484eSchristos 
2068585484eSchristos 		evmap_io_active_(base, i, res);
2078585484eSchristos 	}
2088585484eSchristos 	check_selectop(sop);
2098585484eSchristos 
2108585484eSchristos 	return (0);
2118585484eSchristos }
2128585484eSchristos 
2138585484eSchristos static int
2148585484eSchristos select_resize(struct selectop *sop, int fdsz)
2158585484eSchristos {
2168585484eSchristos 	fd_set *readset_in = NULL;
2178585484eSchristos 	fd_set *writeset_in = NULL;
2188585484eSchristos 
2198585484eSchristos 	if (sop->event_readset_in)
2208585484eSchristos 		check_selectop(sop);
2218585484eSchristos 
2228585484eSchristos 	if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL)
2238585484eSchristos 		goto error;
2248585484eSchristos 	sop->event_readset_in = readset_in;
2258585484eSchristos 	if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) {
2268585484eSchristos 		/* Note that this will leave event_readset_in expanded.
2278585484eSchristos 		 * That's okay; we wouldn't want to free it, since that would
2288585484eSchristos 		 * change the semantics of select_resize from "expand the
2298585484eSchristos 		 * readset_in and writeset_in, or return -1" to "expand the
2308585484eSchristos 		 * *set_in members, or trash them and return -1."
2318585484eSchristos 		 */
2328585484eSchristos 		goto error;
2338585484eSchristos 	}
2348585484eSchristos 	sop->event_writeset_in = writeset_in;
2358585484eSchristos 	sop->resize_out_sets = 1;
2368585484eSchristos 
2378585484eSchristos 	memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
2388585484eSchristos 	    fdsz - sop->event_fdsz);
2398585484eSchristos 	memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
2408585484eSchristos 	    fdsz - sop->event_fdsz);
2418585484eSchristos 
2428585484eSchristos 	sop->event_fdsz = fdsz;
2438585484eSchristos 	check_selectop(sop);
2448585484eSchristos 
2458585484eSchristos 	return (0);
2468585484eSchristos 
2478585484eSchristos  error:
2488585484eSchristos 	event_warn("malloc");
2498585484eSchristos 	return (-1);
2508585484eSchristos }
2518585484eSchristos 
2528585484eSchristos 
2538585484eSchristos static int
2548585484eSchristos select_add(struct event_base *base, int fd, short old, short events, void *p)
2558585484eSchristos {
2568585484eSchristos 	struct selectop *sop = base->evbase;
2578585484eSchristos 	(void) p;
2588585484eSchristos 
2598585484eSchristos 	EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
2608585484eSchristos 	check_selectop(sop);
2618585484eSchristos 	/*
2628585484eSchristos 	 * Keep track of the highest fd, so that we can calculate the size
2638585484eSchristos 	 * of the fd_sets for select(2)
2648585484eSchristos 	 */
2658585484eSchristos 	if (sop->event_fds < fd) {
2668585484eSchristos 		int fdsz = sop->event_fdsz;
2678585484eSchristos 
2688585484eSchristos 		if (fdsz < (int)sizeof(fd_mask))
2698585484eSchristos 			fdsz = (int)sizeof(fd_mask);
2708585484eSchristos 
2718585484eSchristos 		/* In theory we should worry about overflow here.  In
2728585484eSchristos 		 * reality, though, the highest fd on a unixy system will
2738585484eSchristos 		 * not overflow here. XXXX */
2748585484eSchristos 		while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
2758585484eSchristos 			fdsz *= 2;
2768585484eSchristos 
2778585484eSchristos 		if (fdsz != sop->event_fdsz) {
2788585484eSchristos 			if (select_resize(sop, fdsz)) {
2798585484eSchristos 				check_selectop(sop);
2808585484eSchristos 				return (-1);
2818585484eSchristos 			}
2828585484eSchristos 		}
2838585484eSchristos 
2848585484eSchristos 		sop->event_fds = fd;
2858585484eSchristos 	}
2868585484eSchristos 
2878585484eSchristos 	if (events & EV_READ)
2888585484eSchristos 		FD_SET(fd, sop->event_readset_in);
2898585484eSchristos 	if (events & EV_WRITE)
2908585484eSchristos 		FD_SET(fd, sop->event_writeset_in);
2918585484eSchristos 	check_selectop(sop);
2928585484eSchristos 
2938585484eSchristos 	return (0);
2948585484eSchristos }
2958585484eSchristos 
2968585484eSchristos /*
2978585484eSchristos  * Nothing to be done here.
2988585484eSchristos  */
2998585484eSchristos 
3008585484eSchristos static int
3018585484eSchristos select_del(struct event_base *base, int fd, short old, short events, void *p)
3028585484eSchristos {
3038585484eSchristos 	struct selectop *sop = base->evbase;
3048585484eSchristos 	(void)p;
3058585484eSchristos 
3068585484eSchristos 	EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
3078585484eSchristos 	check_selectop(sop);
3088585484eSchristos 
3098585484eSchristos 	if (sop->event_fds < fd) {
3108585484eSchristos 		check_selectop(sop);
3118585484eSchristos 		return (0);
3128585484eSchristos 	}
3138585484eSchristos 
3148585484eSchristos 	if (events & EV_READ)
3158585484eSchristos 		FD_CLR(fd, sop->event_readset_in);
3168585484eSchristos 
3178585484eSchristos 	if (events & EV_WRITE)
3188585484eSchristos 		FD_CLR(fd, sop->event_writeset_in);
3198585484eSchristos 
3208585484eSchristos 	check_selectop(sop);
3218585484eSchristos 	return (0);
3228585484eSchristos }
3238585484eSchristos 
3248585484eSchristos static void
3258585484eSchristos select_free_selectop(struct selectop *sop)
3268585484eSchristos {
3278585484eSchristos 	if (sop->event_readset_in)
3288585484eSchristos 		mm_free(sop->event_readset_in);
3298585484eSchristos 	if (sop->event_writeset_in)
3308585484eSchristos 		mm_free(sop->event_writeset_in);
3318585484eSchristos 	if (sop->event_readset_out)
3328585484eSchristos 		mm_free(sop->event_readset_out);
3338585484eSchristos 	if (sop->event_writeset_out)
3348585484eSchristos 		mm_free(sop->event_writeset_out);
3358585484eSchristos 
3368585484eSchristos 	memset(sop, 0, sizeof(struct selectop));
3378585484eSchristos 	mm_free(sop);
3388585484eSchristos }
3398585484eSchristos 
3408585484eSchristos static void
3418585484eSchristos select_dealloc(struct event_base *base)
3428585484eSchristos {
3438585484eSchristos 	evsig_dealloc_(base);
3448585484eSchristos 
3458585484eSchristos 	select_free_selectop(base->evbase);
3468585484eSchristos }
3478585484eSchristos 
3488585484eSchristos #endif /* EVENT__HAVE_SELECT */
349