xref: /netbsd-src/lib/libc/rpc/svc_fdset.c (revision 76218c7768f18bbdd7e7aefa5ce0bb5fc6d3a3ca)
1 /*	$NetBSD: svc_fdset.c,v 1.3 2015/11/06 23:05:09 joerg Exp $	*/
2 
3 #include <sys/cdefs.h>
4 __RCSID("$NetBSD: svc_fdset.c,v 1.3 2015/11/06 23:05:09 joerg Exp $");
5 
6 
7 #include "reentrant.h"
8 
9 #include <sys/fd_set.h>
10 
11 #include <rpc/rpc.h>
12 
13 #include <stdlib.h>
14 #include <string.h>
15 
16 struct my_svc_fdset {
17 	fd_set *fdset;
18 	int	fdmax;
19 	int	fdsize;
20 };
21 
22 
23 /* The single threaded, one global fd_set version */
24 static fd_set *__svc_fdset;
25 static int svc_fdsize = 0;
26 
27 /*
28  * Update the old global svc_fdset if needed for binary compatibility
29  */
30 #define COMPAT_UPDATE(a)				\
31 	do						\
32 		if ((a) == __svc_fdset)			\
33 			svc_fdset = *__svc_fdset;	\
34 	while (/*CONSTCOND*/0)
35 
36 static thread_key_t fdsetkey = -2;
37 
38 #ifdef FDSET_DEBUG
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <unistd.h>
42 #include <lwp.h>
43 
44 static void  __printflike(3, 0)
45 svc_header(const char *func, size_t line, const char *fmt, va_list ap)
46 {
47 	fprintf(stderr, "%s[%d.%d]: %s, %zu: ", getprogname(), (int)getpid(),
48 	    (int)_lwp_self(), func, line);
49 	vfprintf(stderr, fmt, ap);
50 	va_end(ap);
51 }
52 
53 static void __printflike(5, 6)
54 svc_fdset_print(const char *func, size_t line, const fd_set *fds, int fdmax,
55     const char *fmt, ...)
56 {
57 	va_list ap;
58 	const char *did = "";
59 
60 	va_start(ap, fmt);
61 	svc_header(func, line, fmt, ap);
62 	va_end(ap);
63 
64 	if (fdmax == 0)
65 		fdmax = FD_SETSIZE;
66 
67 	fprintf(stderr, "%p[%d] <", fds, fdmax);
68 	for (int i = 0; i <= fdmax; i++) {
69 		if (!FD_ISSET(i, fds))
70 			continue;
71 		fprintf(stderr, "%s%d", did, i);
72 		did = ", ";
73 	}
74 	fprintf(stderr, ">\n");
75 }
76 
77 static void __printflike(3, 4)
78 svc_print(const char *func, size_t line, const char *fmt, ...)
79 {
80 	va_list ap;
81 
82 	va_start(ap, fmt);
83 	svc_header(func, line, fmt, ap);
84 	va_end(ap);
85 	fprintf(stderr, "\n");
86 }
87 
88 #define DPRINTF_FDSET(...)	svc_fdset_print(__func__, __LINE__, __VA_ARGS__)
89 #define DPRINTF(...)		svc_print(__func__, __LINE__, __VA_ARGS__)
90 #else
91 #define DPRINTF_FDSET(...)
92 #define DPRINTF(...)
93 #endif
94 
95 
96 static void
97 svc_fdset_free(void *v)
98 {
99 	struct my_svc_fdset *rv = v;
100 	DPRINTF_FDSET(rv->fdset, 0, "free");
101 
102 	free(rv->fdset);
103 	free(rv);
104 }
105 
106 static fd_set *
107 svc_fdset_resize(int fd, fd_set **fdset, int *fdsize)
108 {
109 	if (*fdset && fd < *fdsize) {
110 		DPRINTF_FDSET(*fdset, 0, "keeping %d < %d",
111 		    fd, *fdsize);
112 		return *fdset;
113 	}
114 
115 	fd += FD_SETSIZE;
116 	if (fd == 517)
117 		abort();
118 
119 	char *newfdset = realloc(*fdset, __NFD_BYTES(fd));
120 	if (newfdset == NULL)
121 		return NULL;
122 
123 	memset(newfdset + __NFD_BYTES(*fdsize), 0,
124 	    __NFD_BYTES(fd) - __NFD_BYTES(*fdsize));
125 
126 
127 	*fdset = (void *)newfdset;
128 	DPRINTF_FDSET(*fdset, 0, "resize %d > %d", fd, *fdsize);
129 	*fdsize = fd;
130 
131 	COMPAT_UPDATE(*fdset);
132 
133 	return *fdset;
134 }
135 
136 static struct my_svc_fdset *
137 svc_fdset_alloc(int fd)
138 {
139 	struct my_svc_fdset *rv;
140 
141 	if (fdsetkey == -1)
142 		thr_keycreate(&fdsetkey, svc_fdset_free);
143 
144 	if ((rv = thr_getspecific(fdsetkey)) == NULL) {
145 
146 		rv = calloc(1, sizeof(*rv));
147 		if (rv == NULL)
148 			return NULL;
149 
150 		(void)thr_setspecific(fdsetkey, rv);
151 
152 		if (svc_fdsize != 0) {
153 			rv->fdset = __svc_fdset;
154 			DPRINTF("switching to %p", rv->fdset);
155 			rv->fdmax = svc_maxfd;
156 			rv->fdsize = svc_fdsize;
157 
158 			svc_fdsize = 0;
159 		} else {
160 			DPRINTF("first thread time %p", rv->fdset);
161 		}
162 	} else {
163 		DPRINTF("again for %p", rv->fdset);
164 		if (fd < rv->fdsize)
165 			return rv;
166 	}
167 	if (svc_fdset_resize(fd, &rv->fdset, &rv->fdsize) == NULL)
168 		return NULL;
169 	return rv;
170 }
171 
172 static fd_set *
173 svc_fdset_get_internal(int fd)
174 {
175 	struct my_svc_fdset *rv;
176 
177 	if (!__isthreaded || fdsetkey == -2)
178 		return svc_fdset_resize(fd, &__svc_fdset, &svc_fdsize);
179 
180 	rv = svc_fdset_alloc(fd);
181 	if (rv == NULL)
182 		return NULL;
183 	return rv->fdset;
184 }
185 
186 
187 /* allow each thread to have their own copy */
188 void
189 svc_fdset_init(int flags)
190 {
191 	DPRINTF("%x", flags);
192 	if ((flags & SVC_FDSET_MT) && fdsetkey == -2)
193 		fdsetkey = -1;
194 }
195 
196 void
197 svc_fdset_zero(void)
198 {
199 	DPRINTF("zero");
200 	fd_set *fds = svc_fdset_get_internal(0);
201 	int size = svc_fdset_getsize(0);
202 	memset(fds, 0, __NFD_BYTES(size));
203 	*svc_fdset_getmax() = 0;
204 
205 	COMPAT_UPDATE(fds);
206 
207 }
208 
209 void
210 svc_fdset_set(int fd)
211 {
212 	fd_set *fds = svc_fdset_get_internal(fd);
213 	int *fdmax = svc_fdset_getmax();
214 	FD_SET(fd, fds);
215 	if (fd > *fdmax)
216 		*fdmax = fd;
217 	DPRINTF_FDSET(fds, *fdmax, "%d", fd);
218 
219 	COMPAT_UPDATE(fds);
220 }
221 
222 int
223 svc_fdset_isset(int fd)
224 {
225 	fd_set *fds = svc_fdset_get_internal(fd);
226 	DPRINTF_FDSET(fds, 0, "%d", fd);
227 	return FD_ISSET(fd, fds);
228 }
229 
230 void
231 svc_fdset_clr(int fd)
232 {
233 	fd_set *fds = svc_fdset_get_internal(fd);
234 	FD_CLR(fd, fds);
235 	/* XXX: update fdmax? */
236 	DPRINTF_FDSET(fds, 0, "%d", fd);
237 
238 	COMPAT_UPDATE(fds);
239 }
240 
241 fd_set *
242 svc_fdset_copy(const fd_set *orig)
243 {
244 	int len, fdmax;
245 	fd_set *fds;
246 
247 	len = 0;
248 	fds = 0;
249 	fdmax = *svc_fdset_getmax();
250 
251 	DPRINTF_FDSET(orig, 0, "[orig]");
252 	fds = svc_fdset_resize(fdmax, &fds, &len);
253 	if (fds == NULL)
254 		return NULL;
255 
256 	if (orig)
257 		memcpy(fds, orig, __NFD_BYTES(fdmax));
258 	DPRINTF_FDSET(fds, 0, "[copy]");
259 	return fds;
260 }
261 
262 fd_set *
263 svc_fdset_get(void)
264 {
265 	fd_set *fds = svc_fdset_get_internal(0);
266 	DPRINTF_FDSET(fds, 0, "get");
267 	return fds;
268 }
269 
270 int *
271 svc_fdset_getmax(void)
272 {
273 	struct my_svc_fdset *rv;
274 
275 	if (!__isthreaded || fdsetkey == -2)
276 		return &svc_maxfd;
277 
278 	rv = svc_fdset_alloc(0);
279 	if (rv == NULL)
280 		return NULL;
281 	return &rv->fdmax;
282 }
283 
284 int
285 svc_fdset_getsize(int fd)
286 {
287 	struct my_svc_fdset *rv;
288 
289 	if (!__isthreaded || fdsetkey == -2) {
290 		if (svc_fdset_resize(fd, &__svc_fdset, &svc_fdsize) == NULL)
291 			return -1;
292 		else
293 			return svc_fdsize;
294 	}
295 
296 	rv = svc_fdset_alloc(fd);
297 	if (rv == NULL)
298 		return -1;
299 	return rv->fdsize;
300 }
301