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