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,87 @@ +.\" 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 walks the list of currently open file descriptors and the +.Ar cb +callback on each descriptor. +The +.Nm +function passes the +.Ar cbd +argument into the callback, along with each file descriptor. +If +.Ar cbd +returns non-zero, iteration over the list is terminated and the return value +from +.Ar cbd +is returned up to the caller as the return value of +.Nm . +.Sh RETURN VALUES +The +.Nm +function will return 0 on success, or the return value of the last call to the +callback. +No errors are defined for it. +All errors are passed through from +.Xr sysctl 3 , +.Xr malloc 3 +and the callback. +.Sh SEE ALSO +.Xr closefrom 2 , +.Xr kinfo_getfile 3 +.Sh HISTORY +The +.Nm +function first appeared in SunOS. +.Sh BUGS +The +.Nm +function is potentially not thread safe. +It uses +.Xr malloc 3 +and +.Xr sysctl 3 +behind the scenes. +It also 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,107 @@ +/*- + * 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]; + int error; + size_t len, i, j; + NDSLOTTYPE *buf, tmp; + int retries = 5; /* Arbitrary retry count. */ + + len = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_FDMAP; + mib[3] = getpid(); + + buf = NULL; + + /* + * Try a few times to get a stable buffer. The buffer size may change + * if file descriptors are being created in other threads. + */ + for (; retries > 0; --retries) { + error = sysctl(mib, nitems(mib), NULL, &len, NULL, 0); + if (error == -1) + return (-1); + /* + * Add some headroom in case more descriptors are added before + * the next call. + */ + len = len * 4 / 3; + buf = reallocf(buf, len); + if (buf == NULL) + return (-1); + error = sysctl(mib, nitems(mib), buf, &len, NULL, 0); + if (error == 0) + break; + if (errno != ENOMEM) { + free(buf); + return (-1); + } + } + if (retries == 0) { + free(buf); + return (-1); + } + /* + * Go through the full file list. The fdmap is an integral multiple of + * sizeof(NDSLOTTYPE). + */ + len = howmany(len, 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) + goto done; + } + } + } +done: + free(buf); + + return (error); +} Index: sys/kern/kern_descrip.c =================================================================== --- sys/kern/kern_descrip.c +++ sys/kern/kern_descrip.c @@ -3821,6 +3821,36 @@ CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_kern_proc_filedesc, "Process filedesc entries"); +static int +sysctl_kern_proc_fdmap(SYSCTL_HANDLER_ARGS) +{ + struct filedesc *fdp; + struct proc *p; + int error, *name; + + name = (int *)arg1; + + error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); + if (error != 0) { + return (error); + } + + fdp = fdhold(p); + PROC_UNLOCK(p); + if (fdp == NULL) + return (ENOENT); + FILEDESC_SLOCK(fdp); + error = SYSCTL_OUT(req, fdp->fd_map, + roundup2(fdp->fd_nfiles, NDENTRIES)/NBBY); + FILEDESC_SUNLOCK(fdp); + fddrop(fdp); + return (error); +} + +static SYSCTL_NODE(_kern_proc, KERN_PROC_FDMAP, fdmap, + CTLFLAG_RD|CTLFLAG_MPSAFE, sysctl_kern_proc_fdmap, + "Process file descriptor map"); + /* * Store a process current working directory information to sbuf. * 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