Home | History | Annotate | Line # | Download | only in rpc
      1 /*	$NetBSD: svc_fdset.c,v 1.17 2025/05/06 23:18:37 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Christos Zoulas.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __RCSID("$NetBSD: svc_fdset.c,v 1.17 2025/05/06 23:18:37 riastradh Exp $");
     34 
     35 
     36 #include "reentrant.h"
     37 
     38 #include <sys/fd_set.h>
     39 
     40 #include <rpc/rpc.h>
     41 
     42 #ifdef FDSET_DEBUG
     43 #include <stdio.h>
     44 #include <stdarg.h>
     45 #include <unistd.h>
     46 #include <lwp.h>
     47 #endif
     48 #include <stdlib.h>
     49 #include <string.h>
     50 #include <poll.h>
     51 
     52 #include "svc_fdset.h"
     53 
     54 #undef svc_fdset
     55 #undef svc_maxfd
     56 #ifdef _LIBC
     57 extern __fd_set_256 svc_fdset;
     58 #endif
     59 extern int svc_maxfd;
     60 int __svc_flags;
     61 
     62 struct svc_fdset {
     63 	/* select */
     64 	fd_set *fdset;
     65 	int	fdmax;
     66 	int	fdsize;
     67 	/* poll */
     68 	struct pollfd *fdp;
     69 	int	fdnum;
     70 	int	fdused;
     71 };
     72 
     73 /* The single threaded, one global fd_set version */
     74 static struct svc_fdset __svc_fdset;
     75 
     76 #ifdef _REENTRANT
     77 static thread_key_t fdsetkey = -2;
     78 #endif
     79 
     80 #ifdef FDSET_DEBUG
     81 
     82 static void  __printflike(3, 0)
     83 svc_header(const char *func, size_t line, const char *fmt, va_list ap)
     84 {
     85 	fprintf(stderr, "%s[%d.%d]: %s, %zu: ", getprogname(), (int)getpid(),
     86 	    (int)_lwp_self(), func, line);
     87 	vfprintf(stderr, fmt, ap);
     88 	va_end(ap);
     89 }
     90 
     91 static void __printflike(4, 5)
     92 svc_fdset_print(const char *func, size_t line, struct svc_fdset *fds,
     93     const char *fmt, ...)
     94 {
     95 	va_list ap;
     96 	const char *did = "";
     97 
     98 	va_start(ap, fmt);
     99 	svc_header(func, line, fmt, ap);
    100 	va_end(ap);
    101 
    102 	fprintf(stderr, "%p[%d] fd_set<", fds->fdset, fds->fdmax);
    103 	for (int i = 0; i <= fds->fdmax; i++) {
    104 		if (!FD_ISSET(i, fds->fdset))
    105 			continue;
    106 		fprintf(stderr, "%s%d", did, i);
    107 		did = ", ";
    108 	}
    109 	did = "";
    110 	fprintf(stderr, "> poll<");
    111 	for (int i = 0; i < fds->fdused; i++) {
    112 		int fd = fds->fdp[i].fd;
    113 		if (fd == -1)
    114 			continue;
    115 		fprintf(stderr, "%s%d", did, fd);
    116 		did = ", ";
    117 	}
    118 	fprintf(stderr, ">\n");
    119 }
    120 
    121 static void __printflike(3, 4)
    122 svc_print(const char *func, size_t line, const char *fmt, ...)
    123 {
    124 	va_list ap;
    125 
    126 	va_start(ap, fmt);
    127 	svc_header(func, line, fmt, ap);
    128 	va_end(ap);
    129 	fprintf(stderr, "\n");
    130 }
    131 
    132 #define DPRINTF(...)		svc_print(__func__, __LINE__, __VA_ARGS__)
    133 #define DPRINTF_FDSET(...)	svc_fdset_print(__func__, __LINE__, __VA_ARGS__)
    134 
    135 #else
    136 
    137 #define DPRINTF(...)
    138 #define DPRINTF_FDSET(...)
    139 
    140 #endif
    141 
    142 
    143 static inline void
    144 svc_fdset_sanitize(struct svc_fdset *fds)
    145 {
    146 	while (fds->fdmax >= 0 && !FD_ISSET(fds->fdmax, fds->fdset))
    147 		fds->fdmax--;
    148 #ifdef _LIBC
    149 	/* Compat update */
    150 	if (fds == &__svc_fdset) {
    151 		svc_fdset = *(__fd_set_256 *)(void *)__svc_fdset.fdset;
    152 		svc_maxfd = __svc_fdset.fdmax;
    153 	}
    154 #endif
    155 }
    156 
    157 #ifdef _REENTRANT
    158 static void
    159 svc_fdset_free(void *v)
    160 {
    161 	struct svc_fdset *fds = v;
    162 	DPRINTF_FDSET(fds, "free");
    163 
    164 	free(fds->fdp);
    165 	free(fds->fdset);
    166 	free(fds);
    167 }
    168 #endif
    169 
    170 static void
    171 svc_pollfd_init(struct pollfd *pfd, int nfd)
    172 {
    173 	for (int i = 0; i < nfd; i++) {
    174 		pfd[i].fd = -1;
    175 		pfd[i].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
    176 		pfd[i].revents = 0;
    177 	}
    178 }
    179 
    180 static struct pollfd *
    181 svc_pollfd_alloc(struct svc_fdset *fds)
    182 {
    183 	if (fds->fdp != NULL)
    184 		return fds->fdp;
    185 
    186 	fds->fdnum = FD_SETSIZE;
    187 	fds->fdp = calloc(fds->fdnum, sizeof(*fds->fdp));
    188 	if (fds->fdp == NULL)
    189 		return NULL;
    190 	svc_pollfd_init(fds->fdp, fds->fdnum);
    191 	return fds->fdp;
    192 }
    193 
    194 
    195 static struct svc_fdset *
    196 svc_pollfd_add(int fd, struct svc_fdset *fds)
    197 {
    198 	struct pollfd *pfd;
    199 
    200 	if ((pfd = svc_pollfd_alloc(fds)) == NULL)
    201 		return NULL;
    202 
    203 	for (int i = 0; i < fds->fdnum; i++)
    204 		if (pfd[i].fd == -1) {
    205 			if (i >= fds->fdused)
    206 				fds->fdused = i + 1;
    207 			DPRINTF("add fd=%d slot=%d fdused=%d",
    208 			    fd, i, fds->fdused);
    209 			pfd[i].fd = fd;
    210 			return fds;
    211 		}
    212 
    213 	pfd = realloc(fds->fdp, (fds->fdnum + FD_SETSIZE) * sizeof(*fds->fdp));
    214 	if (pfd == NULL)
    215 		return NULL;
    216 
    217 	svc_pollfd_init(pfd + fds->fdnum, FD_SETSIZE);
    218 	pfd[fds->fdnum].fd = fd;
    219 	fds->fdused = fds->fdnum + 1;
    220 	DPRINTF("add fd=%d slot=%d fdused=%d", fd, fds->fdnum, fds->fdused);
    221 	fds->fdnum += FD_SETSIZE;
    222 	fds->fdp = pfd;
    223 	return fds;
    224 }
    225 
    226 static struct svc_fdset *
    227 svc_pollfd_del(int fd, struct svc_fdset *fds)
    228 {
    229 	struct pollfd *pfd;
    230 
    231 	if ((pfd = svc_pollfd_alloc(fds)) == NULL)
    232 		return NULL;
    233 
    234 	for (int i = 0; i < fds->fdnum; i++) {
    235 		if (pfd[i].fd != fd)
    236 			continue;
    237 
    238 		pfd[i].fd = -1;
    239 		DPRINTF("del fd=%d slot=%d", fd, fds->fdused);
    240 		if (i != fds->fdused - 1)
    241 			return fds;
    242 
    243 		do
    244 			if (pfd[i].fd != -1)
    245 				break;
    246 		while (--i >= 0);
    247 
    248 		fds->fdused = i + 1;
    249 		DPRINTF("del fd=%d fdused=%d", fd, fds->fdused);
    250 		return fds;
    251 	}
    252 	DPRINTF("del fd=%d not found", fd);
    253 	return NULL;
    254 }
    255 
    256 static struct svc_fdset *
    257 svc_fdset_resize(int fd, struct svc_fdset *fds)
    258 {
    259 	if (fds->fdset && fd < fds->fdsize) {
    260 		DPRINTF_FDSET(fds, "keeping %d < %d", fd, fds->fdsize);
    261 		return fds;
    262 	}
    263 
    264 	fd += FD_SETSIZE;
    265 
    266 	char *newfdset = realloc(fds->fdset, __NFD_BYTES(fd));
    267 	if (newfdset == NULL)
    268 		return NULL;
    269 
    270 	memset(newfdset + __NFD_BYTES(fds->fdsize), 0,
    271 	    __NFD_BYTES(fd) - __NFD_BYTES(fds->fdsize));
    272 
    273 
    274 	fds->fdset = (void *)newfdset;
    275 	DPRINTF_FDSET(fds, "resize %d > %d", fd, fds->fdsize);
    276 	fds->fdsize = fd;
    277 
    278 	return fds;
    279 }
    280 
    281 static struct svc_fdset *
    282 svc_fdset_alloc(int fd)
    283 {
    284 #ifdef _REENTRANT
    285 	struct svc_fdset *fds;
    286 
    287 	if (!__isthreaded || fdsetkey == -2)
    288 		return svc_fdset_resize(fd, &__svc_fdset);
    289 
    290 	if (fdsetkey == -1)
    291 		thr_keycreate(&fdsetkey, svc_fdset_free);
    292 
    293 	if ((fds = thr_getspecific(fdsetkey)) == NULL) {
    294 
    295 		fds = calloc(1, sizeof(*fds));
    296 		if (fds == NULL)
    297 			return NULL;
    298 
    299 		(void)thr_setspecific(fdsetkey, fds);
    300 
    301 		if (__svc_fdset.fdsize != 0) {
    302 			*fds = __svc_fdset;
    303 			DPRINTF("switching to %p", fds->fdset);
    304 		} else {
    305 			DPRINTF("first thread time %p", fds->fdset);
    306 		}
    307 	} else {
    308 		DPRINTF("again for %p", fds->fdset);
    309 		if (fd < fds->fdsize)
    310 			return fds;
    311 	}
    312 
    313 	return svc_fdset_resize(fd, fds);
    314 #else
    315 	return svc_fdset_resize(fd, &__svc_fdset);
    316 #endif
    317 }
    318 
    319 /* allow each thread to have their own copy */
    320 void
    321 svc_fdset_init(int flags)
    322 {
    323 	DPRINTF("%x", flags);
    324 	__svc_flags = flags;
    325 #ifdef _REENTRANT
    326 	if ((flags & SVC_FDSET_MT) && fdsetkey == -2)
    327 		fdsetkey = -1;
    328 #endif
    329 }
    330 
    331 void
    332 svc_fdset_zero(void)
    333 {
    334 	DPRINTF("zero");
    335 
    336 	struct svc_fdset *fds = svc_fdset_alloc(0);
    337 	if (fds == NULL)
    338 		return;
    339 	memset(fds->fdset, 0, fds->fdsize);
    340 	fds->fdmax = -1;
    341 
    342 	free(fds->fdp);
    343 	fds->fdp = NULL;
    344 	fds->fdnum = fds->fdused = 0;
    345 }
    346 
    347 int
    348 svc_fdset_set(int fd)
    349 {
    350 	struct svc_fdset *fds = svc_fdset_alloc(fd);
    351 
    352 	if (fds == NULL)
    353 		return -1;
    354 
    355 	FD_SET(fd, fds->fdset);
    356 	if (fd > fds->fdmax)
    357 		fds->fdmax = fd;
    358 
    359 	int rv = svc_pollfd_add(fd, fds) ? 0 : -1;
    360 	DPRINTF_FDSET(fds, "%d", fd);
    361 
    362 	svc_fdset_sanitize(fds);
    363 	return rv;
    364 }
    365 
    366 int
    367 svc_fdset_isset(int fd)
    368 {
    369 	struct svc_fdset *fds = svc_fdset_alloc(fd);
    370 
    371 	if (fds == NULL)
    372 		return -1;
    373 
    374 	DPRINTF_FDSET(fds, "%d", fd);
    375 
    376 	return FD_ISSET(fd, fds->fdset) != 0;
    377 }
    378 
    379 int
    380 svc_fdset_clr(int fd)
    381 {
    382 	struct svc_fdset *fds = svc_fdset_alloc(fd);
    383 
    384 	if (fds == NULL)
    385 		return -1;
    386 
    387 	FD_CLR(fd, fds->fdset);
    388 
    389 	int rv = svc_pollfd_del(fd, fds) ? 0 : -1;
    390 	DPRINTF_FDSET(fds, "%d", fd);
    391 
    392 	svc_fdset_sanitize(fds);
    393 	return rv;
    394 }
    395 
    396 fd_set *
    397 svc_fdset_copy(const fd_set *orig)
    398 {
    399 	int size = svc_fdset_getsize(0);
    400 	if (size == -1)
    401 		return NULL;
    402 	fd_set *copy = calloc(1, __NFD_BYTES(size));
    403 	if (copy == NULL)
    404 		return NULL;
    405 	if (orig)
    406 		memcpy(copy, orig, __NFD_BYTES(size));
    407 	return copy;
    408 }
    409 
    410 fd_set *
    411 svc_fdset_get(void)
    412 {
    413 	struct svc_fdset *fds = svc_fdset_alloc(0);
    414 
    415 	if (fds == NULL)
    416 		return NULL;
    417 
    418 	DPRINTF_FDSET(fds, "get");
    419 	svc_fdset_sanitize(fds);
    420 	return fds->fdset;
    421 }
    422 
    423 int *
    424 svc_fdset_getmax(void)
    425 {
    426 	struct svc_fdset *fds = svc_fdset_alloc(0);
    427 
    428 	if (fds == NULL)
    429 		return NULL;
    430 
    431 	DPRINTF_FDSET(fds, "getmax");
    432 	svc_fdset_sanitize(fds);
    433 	return &fds->fdmax;
    434 }
    435 
    436 int
    437 svc_fdset_getsize(int fd)
    438 {
    439 	struct svc_fdset *fds = svc_fdset_alloc(fd);
    440 
    441 	if (fds == NULL)
    442 		return -1;
    443 
    444 	DPRINTF_FDSET(fds, "getsize");
    445 	return fds->fdsize;
    446 }
    447 
    448 struct pollfd *
    449 svc_pollfd_copy(const struct pollfd *orig)
    450 {
    451 	int size = svc_fdset_getsize(0);
    452 	if (size == -1)
    453 		return NULL;
    454 	struct pollfd *copy = calloc(size, sizeof(*orig));
    455 	if (copy == NULL)
    456 		return NULL;
    457 	if (orig)
    458 		memcpy(copy, orig, size * sizeof(*orig));
    459 	return copy;
    460 }
    461 
    462 struct pollfd *
    463 svc_pollfd_get(void)
    464 {
    465 	struct svc_fdset *fds = svc_fdset_alloc(0);
    466 
    467 	if (fds == NULL)
    468 		return NULL;
    469 
    470 	DPRINTF_FDSET(fds, "getpoll");
    471 	return fds->fdp;
    472 }
    473 
    474 int *
    475 svc_pollfd_getmax(void)
    476 {
    477 	struct svc_fdset *fds = svc_fdset_alloc(0);
    478 
    479 	if (fds == NULL)
    480 		return NULL;
    481 	return &fds->fdused;
    482 }
    483 
    484 int
    485 svc_pollfd_getsize(int fd)
    486 {
    487 	struct svc_fdset *fds = svc_fdset_alloc(fd);
    488 
    489 	if (fds == NULL)
    490 		return -1;
    491 
    492 	DPRINTF_FDSET(fds, "getsize");
    493 	return fds->fdnum;
    494 }
    495