Use /proc/self/fd to efficiently close all FDs on Linux

Issue #1506.
This commit is contained in:
Eelco Dolstra 2017-08-09 16:22:05 +02:00
parent c6184dec6c
commit af765a8eab
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
3 changed files with 19 additions and 5 deletions

View file

@ -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

View file

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

View file

@ -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. */