svc_fdset.c revision 1.17 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