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