From 48755ebf093543113de96747a7f5f78e0640b333 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 2 Mar 2020 17:42:51 +0000 Subject: [PATCH] Optimise closing file descriptors. Dnsmasq needs to close all the file descriptors it inherits, for security reasons. This is traditionally done by calling close() on every possible file descriptor (most of which won't be open.) On big servers where "every possible file descriptor" is a rather large set, this gets rather slow, so we use the /proc//fd directory to get a list of the fds which are acually open. This only works on Linux. On other platforms, and on Linux systems without a /proc filesystem, we fall back to the old way. --- src/util.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/util.c b/src/util.c index 91b52df..f058c92 100644 --- a/src/util.c +++ b/src/util.c @@ -708,6 +708,38 @@ int read_write(int fd, unsigned char *packet, int size, int rw) /* close all fds except STDIN, STDOUT and STDERR, spare1, spare2 and spare3 */ void close_fds(long max_fd, int spare1, int spare2, int spare3) { + /* On Linux, use the /proc/ filesystem to find which files + are actually open, rather than iterate over the whole space, + for efficiency reasons. If this fails we drop back to the dumb code. */ +#ifdef HAVE_LINUX_NETWORK + DIR *d; + + if ((d = opendir("/proc/self/fd"))) + { + struct dirent *de; + + while ((de = readdir(d))) + { + long fd; + char *e = NULL; + + errno = 0; + fd = strtol(de->d_name, &e, 10); + + if (errno != 0 || !e || *e || fd == dirfd(d) || + fd == STDOUT_FILENO || fd == STDERR_FILENO || fd == STDIN_FILENO || + fd == spare1 || fd == spare2 || fd == spare3) + continue; + + close(fd); + } + + closedir(d); + return; + } +#endif + + /* fallback, dumb code. */ for (max_fd--; max_fd >= 0; max_fd--) if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO && max_fd != spare1 && max_fd != spare2 && max_fd != spare3)