Skip to content

Commit

Permalink
Use /proc/self/fd to efficiently close all FDs on Linux
Browse files Browse the repository at this point in the history
Issue #1506.
  • Loading branch information
edolstra committed Aug 9, 2017
1 parent c6184de commit af765a8
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/libstore/build.cc
Expand Up @@ -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
Expand Down
18 changes: 16 additions & 2 deletions src/libutil/util.cc
Expand Up @@ -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)
Expand Down Expand Up @@ -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);

This comment has been minimized.

Copy link
@Mic92

Mic92 Aug 10, 2017

Member

Maybe include a readlink("/proc/self/fd/%d") in debug to find the root cause of the leak?

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 */
}

Expand Down
4 changes: 2 additions & 2 deletions src/libutil/util.hh
Expand Up @@ -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. */
Expand Down

2 comments on commit af765a8

@copumpkin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does ls /dev/fd work for this on Linux too? That works on Darwin, so it might be possible to make it a bit more portable.

@dtzWill
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, but it does on FreeBSD. The closefrom implementation in libbsd uses both in the same way.

Please sign in to comment.