Index: include/stdlib.h =================================================================== --- include/stdlib.h +++ include/stdlib.h @@ -278,6 +278,7 @@ char *devname_r(__dev_t, __mode_t, char *, int); char *fdevname(int); char *fdevname_r(int, char *, int); +int fdwalk(int (*)(void *, int), void *); int getloadavg(double [], int); const char * getprogname(void); Index: lib/libc/stdlib/Makefile.inc =================================================================== --- lib/libc/stdlib/Makefile.inc +++ lib/libc/stdlib/Makefile.inc @@ -7,7 +7,7 @@ MISRCS+=C99_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \ bsearch.c \ cxa_thread_atexit.c cxa_thread_atexit_impl.c \ - div.c exit.c getenv.c getopt.c getopt_long.c \ + div.c exit.c fdwalk.c getenv.c getopt.c getopt_long.c \ getsubopt.c hcreate.c hcreate_r.c hdestroy_r.c heapsort.c heapsort_b.c \ hsearch_r.c imaxabs.c imaxdiv.c \ insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c \ @@ -32,7 +32,7 @@ MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 \ atoi.3 atol.3 at_quick_exit.3 bsearch.3 \ - div.3 exit.3 getenv.3 getopt.3 getopt_long.3 getsubopt.3 \ + div.3 exit.3 fdwalk.3 getenv.3 getopt.3 getopt_long.3 getsubopt.3 \ hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 llabs.3 lldiv.3 \ lsearch.3 memory.3 ptsname.3 qsort.3 \ quick_exit.3 \ Index: lib/libc/stdlib/Symbol.map =================================================================== --- lib/libc/stdlib/Symbol.map +++ lib/libc/stdlib/Symbol.map @@ -124,6 +124,10 @@ set_constraint_handler_s; }; +FBSD_1.6 { + fdwalk; +}; + FBSDprivate_1.0 { __system; _system; Index: lib/libc/stdlib/fdwalk.3 =================================================================== --- /dev/null +++ lib/libc/stdlib/fdwalk.3 @@ -0,0 +1,69 @@ +.\" Copyright (c) 2019 Justin Hibbits +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 9, 2019 +.Dt FDWALK 3 +.Os +.Sh NAME +.Nm fdwalk +.Nd iterate over open file descriptors +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In stdlib.h +.Ft int +.Fn fdwalk "int (*cb)(void *, int)" "void *cbd" +.Sh DESCRIPTION +The +.Nm +function executes a callback for each file descriptor open at the time of its +invocation. +The +.Nm +function passes the +.Ar cbd +argument into the callback, along with each file descriptor. +If +.Ar cbd +returns non-zero, iteration is terminated. +It is async-signal-safe. +.Sh RETURN VALUES +The +.Nm +function always returns 0. +.Sh SEE ALSO +.Xr closefrom 2 , +.Xr kinfo_getfile 3 +.Sh HISTORY +The +.Nm +function first appeared in SunOS. +.Sh BUGS +It only takes a snapshot, so any file descriptors created or removed +after the snapshot is taken, either in another thread or in the callback +function, are not handled. Index: lib/libc/stdlib/fdwalk.c =================================================================== --- /dev/null +++ lib/libc/stdlib/fdwalk.c @@ -0,0 +1,89 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Justin Hibbits + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#define FD(slot, bit) ((slot * sizeof(NDSLOTTYPE) * NBBY) + bit) + +int +fdwalk(int (*cb)(void *, int), void *cbd) +{ + int mib[4]; + size_t oldlen, newlen; + int error, i, j, len; + NDSLOTTYPE *buf, tmp; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_FDMAP; + mib[3] = 0; + + oldlen = 0; + for (;;) { + error = sysctl(mib, nitems(mib), NULL, &newlen, NULL, 0); + if (error == -1) + return (0); + if (oldlen < newlen) { + oldlen = newlen; + buf = alloca(newlen); + } + newlen = oldlen; + error = sysctl(mib, nitems(mib), buf, &newlen, NULL, 0); + if (error == 0) + break; + if (errno != ENOMEM) + return (0); + } + + /* + * Go through the full file list. The fdmap is an integral multiple of + * sizeof(NDSLOTTYPE). + */ + len = howmany(newlen, sizeof(NDSLOTTYPE)); + + for (i = 0; i < len; i++) { + /* + * Iterate over each bit in the slot, short-circuting when there + * are no more file descriptors in use in this slot. + */ + for (j = 0, tmp = buf[i]; + j < NBBY * sizeof(NDSLOTTYPE) && tmp != 0; + j++, tmp >>= 1) { + if (tmp & 1) { + error = cb(cbd, FD(i, j)); + if (error != 0) + return (error); + } + } + } + return (0); +} Index: lib/libc/sys/sigaction.2 =================================================================== --- lib/libc/sys/sigaction.2 +++ lib/libc/sys/sigaction.2 @@ -393,6 +393,7 @@ .Fn fchown , .Fn fchownat , .Fn fcntl , +.Fn fdwalk , .Fn fork , .Fn fstat , .Fn fstatat , Index: sys/kern/kern_descrip.c =================================================================== --- sys/kern/kern_descrip.c +++ sys/kern/kern_descrip.c @@ -144,6 +144,7 @@ #define NDSLOT(x) ((x) / NDENTRIES) #define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES)) #define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES) +#define NFD2MAPSIZE(x) (NDSLOTS(x) * NDSLOTSIZE) /* * SLIST entry used to keep track of ofiles which must be reclaimed when @@ -1680,7 +1681,7 @@ * entries than the table can hold. */ if (NDSLOTS(nnfiles) > NDSLOTS(onfiles)) { - nmap = malloc(NDSLOTS(nnfiles) * NDSLOTSIZE, M_FILEDESC, + nmap = malloc(NFD2MAPSIZE(nnfiles), M_FILEDESC, M_ZERO | M_WAITOK); /* copy over the old data and update the pointer */ memcpy(nmap, omap, NDSLOTS(onfiles) * sizeof(*omap)); @@ -3313,6 +3314,55 @@ CTLFLAG_RD|CTLFLAG_CAPRD|CTLFLAG_MPSAFE, sysctl_kern_proc_nfds, "Number of open file descriptors"); +static int +sysctl_kern_proc_fdmap(SYSCTL_HANDLER_ARGS) +{ + struct filedesc *fdp; + NDSLOTTYPE lmap[NDSLOTS(NDFILE) * 16]; + NDSLOTTYPE *map; + size_t bsize, csize; + int error; + + CTASSERT(sizeof(lmap) <= 128); + + if (*(int *)arg1 != 0) + return (EINVAL); + + fdp = curproc->p_fd; + + if (req->oldptr == NULL) + return (SYSCTL_OUT(req, NULL, NFD2MAPSIZE(fdp->fd_nfiles))); + + if (curproc->p_numthreads == 1 && fdp->fd_refcnt == 1) + /* No need to lock anything as only we can modify the map. */ + return (SYSCTL_OUT(req, fdp->fd_map, NFD2MAPSIZE(fdp->fd_nfiles))); + + /* Potential other threads modifying the map. */ + map = lmap; + bsize = sizeof(lmap); + for (;;) { + FILEDESC_SLOCK(fdp); + csize = NFD2MAPSIZE(fdp->fd_nfiles); + if (bsize >= csize) + break; + FILEDESC_SUNLOCK(fdp); + if (map != lmap) + free(map, M_TEMP); + bsize = csize; + map = malloc(bsize, M_TEMP, M_WAITOK); + } + memcpy(map, fdp->fd_map, csize); + FILEDESC_SUNLOCK(fdp); + error = SYSCTL_OUT(req, map, csize); + if (map != lmap) + free(map, M_TEMP); + return (error); +} + +static SYSCTL_NODE(_kern_proc, KERN_PROC_FDMAP, fdmap, + CTLFLAG_RD|CTLFLAG_CAPRD|CTLFLAG_MPSAFE, sysctl_kern_proc_fdmap, + "File descriptor map"); + /* * Get file structures globally. */ Index: sys/sys/sysctl.h =================================================================== --- sys/sys/sysctl.h +++ sys/sys/sysctl.h @@ -977,6 +977,7 @@ #define KERN_PROC_SIGTRAMP 41 /* signal trampoline location */ #define KERN_PROC_CWD 42 /* process current working directory */ #define KERN_PROC_NFDS 43 /* number of open file descriptors */ +#define KERN_PROC_FDMAP 44 /* file descriptor map */ /* * KERN_IPC identifiers