about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2017-08-09T14·22+0200
committerEelco Dolstra <edolstra@gmail.com>2017-08-09T14·22+0200
commitaf765a8eab288eb638100e027b97a1d15e4e3026 (patch)
tree0ab17da6aae1af4c83efef428deae6d0b534aa93
parentc6184dec6c208d39a329586d0503b7a51bc2ded1 (diff)
Use /proc/self/fd to efficiently close all FDs on Linux
Issue #1506.
-rw-r--r--src/libstore/build.cc2
-rw-r--r--src/libutil/util.cc18
-rw-r--r--src/libutil/util.hh4
3 files changed, 19 insertions, 5 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index db5f606fa883..9250a9b1778f 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2556,7 +2556,7 @@ void DerivationGoal::runChild()
             throw SysError(format("changing into '%1%'") % tmpDir);
 
         /* Close all other file descriptors. */
-        closeMostFDs(set<int>());
+        closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO});
 
 #if __linux__
         /* Change the personality to 32-bit if we're doing an
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 27f4fea187d2..55d3e1d16f6c 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -310,6 +310,7 @@ string readLine(int fd)
     while (1) {
         checkInterrupt();
         char ch;
+        // FIXME: inefficient
         ssize_t rd = read(fd, &ch, 1);
         if (rd == -1) {
             if (errno != EINTR)
@@ -962,11 +963,24 @@ string runProgram(Path program, bool searchPath, const Strings & args,
 
 void closeMostFDs(const set<int> & exceptions)
 {
+#if __linux__
+    try {
+        for (auto & s : readDirectory("/proc/self/fd")) {
+            auto fd = std::stoi(s.name);
+            if (!exceptions.count(fd)) {
+                debug("closing leaked FD %d", fd);
+                close(fd);
+            }
+        }
+        return;
+    } catch (SysError &) {
+    }
+#endif
+
     int maxFD = 0;
     maxFD = sysconf(_SC_OPEN_MAX);
     for (int fd = 0; fd < maxFD; ++fd)
-        if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO
-            && exceptions.find(fd) == exceptions.end())
+        if (!exceptions.count(fd))
             close(fd); /* ignore result */
 }
 
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 7ea32e8d9f14..f37f2c5d1be5 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -261,8 +261,8 @@ public:
    list of strings. */
 std::vector<char *> stringsToCharPtrs(const Strings & ss);
 
-/* Close all file descriptors except stdin, stdout, stderr, and those
-   listed in the given set.  Good practice in child processes. */
+/* Close all file descriptors except those listed in the given set.
+   Good practice in child processes. */
 void closeMostFDs(const set<int> & exceptions);
 
 /* Set the close-on-exec flag for the given file descriptor. */