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