--- a
+++ b/src/closefrom.cpp
@@ -0,0 +1,241 @@
+/* Copyright (C) 2009 J.F.Dockes
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/*
+ * Close all file descriptors above a given value.
+ *
+ * A Unix execXX() call used to execute another program does not close open
+ * file descriptors by default.
+ *
+ * The only descriptors closed are those on which the FD_CLOEXEC flag was
+ * set. FD_CLOEXEC is not easily usable on files opened by external
+ * libraries.
+ *
+ * There are many reasons for closing file descriptors before
+ * an exec (security, pipe control, the possibility that a bug will trigger
+ * an unwanted write, etc.)
+ *
+ * A process has currently no POSIX way to determine the set of open file
+ * descriptors or at least the highest value. Closing all files (except a few),
+ * thus implies performing a close() system call on each entry up to the
+ * maximum, which can be both relatively difficult to determine, and quite
+ * high (ie: several thousands), incurring a non-negligible cost.
+ *
+ * A number of systems have non-portable support for mitigating or solving
+ * this problem.
+ *
+ * This module supplies a portable interface to this functionality.
+ *
+ * The initial data on system interfaces was obtained from:
+ * http://stackoverflow.com/questions/899038/\
+ * getting-the-highest-allocated-file-descriptor
+ *
+ * System interfaces:
+ * FreeBSD:
+ * - Has a closefrom() system call as of release 7.x around Sep 2009
+ * - Has a /dev/fd, directory which shows the current process' open
+ * descriptors. Only descriptors 0, 1, 2 are shown except if
+ * fdescfs is mounted which it is not by default
+ *
+ * Interface:
+ * int libclf_closefrom(fd)
+ * @param fd All open file descriptors with equal or higher numeric
+ * values will be closed. fd needs not be a valid descriptor.
+ * @return 0 for success, -1 for error.
+ */
+#ifndef TEST_CLOSEFROM
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/param.h>
+
+/* #define DEBUG_CLOSEFROM*/
+#ifdef DEBUG_CLOSEFROM
+#define DPRINT(X) fprintf X
+#else
+#define DPRINT(X)
+#endif
+
+/* Note: sudo has a closefrom implementation, needs a lot of autoconfig, but
+ * we could use it instead. It's quite close to this though */
+
+/*************************************************************************/
+
+/* closefrom() exists on Solaris, netbsd and openbsd, but someone will
+ * have to provide me the appropriate macro to test */
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 702104)
+/* Use closefrom() system call */
+int libclf_closefrom(int fd0)
+{
+ DPRINT((stderr, "libclf_closefrom: using closefrom(2)\n"));
+ closefrom(fd0);
+ return 0;
+}
+
+/*************************************************************************/
+#elif defined(F_CLOSEM)
+
+/* Use fcntl(fd, F_CLOSEM) */
+
+int libclf_closefrom(int fd0)
+{
+ DPRINT((stderr, "libclf_closefrom: using fcntl(F_CLOSEM)\n"));
+ // We need a valid descriptor for this to work. Try to dup stdin, else
+ // go wild
+ if (fcntl(0, F_GETFL) != -1) {
+ if (fd0 != 0) {
+ dup2(0, fd0);
+ }
+ } else {
+ int fd = open("/etc/group", 0); // yes i am a unix man
+ if (fd >= 0 && fd != fd0) {
+ dup2(fd, fd0);
+ close(fd);
+ }
+ }
+ return fcntl(fd0, F_CLOSEM, 0);
+}
+
+/*************************************************************************/
+#elif (defined(linux) || defined(__linux))
+
+/* Use /proc/self/fd directory */
+#include <sys/types.h>
+#include <dirent.h>
+
+int libclf_closefrom(int fd0)
+{
+ DIR *dirp;
+ struct dirent *ent;
+
+ DPRINT((stderr, "libclf_closefrom: using /proc\n"));
+ dirp = opendir("/proc/self/fd");
+ if (dirp == 0) {
+ return -1;
+ }
+
+ while ((ent = readdir(dirp)) != 0) {
+ int fd;
+ if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+ continue;
+ }
+
+ if (sscanf(ent->d_name, "%d", &fd) == 1 && fd >= fd0 &&
+ fd != dirfd(dirp)) {
+ close(fd);
+ }
+ }
+ closedir(dirp);
+ return 0;
+}
+
+/*************************************************************************/
+#else
+/* System has no native support for this functionality whatsoever.
+ *
+ * Close all descriptors up to compiled/configured maximum.
+ * The caller will usually have an idea of a reasonable maximum, else
+ * we retrieve a value from the system.
+ */
+
+static int closefrom_maxfd = -1;
+
+void libclf_setmaxfd(int max)
+{
+ closefrom_maxfd = max;
+}
+
+#ifdef sun
+#include <limits.h>
+#endif
+int libclf_closefrom(int fd0)
+{
+ int i, maxfd = closefrom_maxfd;
+
+ if (maxfd < 0) {
+#ifdef _SC_OPEN_MAX
+ maxfd = sysconf(_SC_OPEN_MAX);
+ DPRINT((stderr, "Maxfd is %d after sysconf()\n", maxfd));
+#else
+ maxfd = getdtablesize();
+ DPRINT((stderr, "Maxfd is %d after getdtablesize()\n", maxfd));
+#endif
+ }
+ if (maxfd < 0) {
+ maxfd = OPEN_MAX;
+ }
+
+ DPRINT((stderr, "libclf_closefrom: using loop to %d\n", maxfd));
+
+ for (i = fd0; i < maxfd; i++) {
+ (void)close(i);
+ }
+ return 0;
+}
+#endif
+
+
+#else /* TEST_CLOSEFROM */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include "closefrom.h"
+
+int main(int argc, char **argv)
+{
+ int i;
+
+ int fd0 = open("/etc/group", 0);
+ if (fd0 < 0) {
+ perror("open /etc/group");
+ exit(1);
+ }
+
+ if (dup2(fd0, 11) < 0) {
+ perror("dup2->11");
+ exit(1);
+ }
+ if (dup2(fd0, 19) < 0) {
+ perror("dup2->19");
+ exit(1);
+ }
+ if (dup2(fd0, 99) < 0) {
+ perror("dup2->99 (ok)");
+ }
+ if (dup2(fd0, 999) < 0) {
+ perror("dup3->999 (ok)");
+ }
+
+ libclf_closefrom(11);
+ for (i = 0; i < 10000; i++) {
+ if (fcntl(i, F_GETFL) != -1) {
+ fprintf(stderr, "Descriptor %d is still open", i);
+ if (i < 11) {
+ fprintf(stderr, " (OK)\n");
+ } else {
+ fprintf(stderr, " (BAD)\n");
+ }
+ }
+ }
+ exit(0);
+}
+
+#endif
+