summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rw-r--r--base/base.gyp5
-rw-r--r--base/file_descriptor_shuffle.cc92
-rw-r--r--base/file_descriptor_shuffle.h76
-rw-r--r--base/file_descriptor_shuffle_unittest.cc289
-rw-r--r--base/process_util.h9
-rw-r--r--base/process_util_linux.cc26
-rw-r--r--base/process_util_posix.cc97
7 files changed, 564 insertions, 30 deletions
diff --git a/base/base.gyp b/base/base.gyp
index eef97fb..1ad079b 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -95,6 +95,8 @@
'event_recorder_stubs.cc',
'field_trial.cc',
'field_trial.h',
+ 'file_descriptor_shuffle.cc',
+ 'file_descriptor_shuffle.h',
'file_path.cc',
'file_path.h',
'file_util.cc',
@@ -446,6 +448,7 @@
'sources!': [
'data_pack.cc',
'event_recorder_stubs.cc',
+ 'file_descriptor_shuffle.cc',
'message_pump_libevent.cc',
'string16.cc',
],
@@ -558,6 +561,7 @@
'debug_util_unittest.cc',
'directory_watcher_unittest.cc',
'field_trial_unittest.cc',
+ 'file_descriptor_shuffle_unittest.cc',
'file_path_unittest.cc',
'file_util_unittest.cc',
'file_version_info_unittest.cc',
@@ -655,6 +659,7 @@
],
'sources!': [
'data_pack_unittest.cc',
+ 'file_descriptor_shuffle_unittest.cc',
],
}, { # OS != "win"
'sources!': [
diff --git a/base/file_descriptor_shuffle.cc b/base/file_descriptor_shuffle.cc
new file mode 100644
index 0000000..1426155
--- /dev/null
+++ b/base/file_descriptor_shuffle.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_descriptor_shuffle.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
+ InjectionDelegate* delegate) {
+ InjectiveMultimap m(m_in);
+ std::vector<int> extra_fds;
+
+ for (InjectiveMultimap::iterator i = m.begin(); i != m.end(); ++i) {
+ int temp_fd = -1;
+
+ // We DCHECK the injectiveness of the mapping.
+ for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j)
+ DCHECK(i->dest != j->dest);
+
+ const bool is_identity = i->source == i->dest;
+
+ for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j) {
+ if (!is_identity && i->dest == j->source) {
+ if (temp_fd == -1) {
+ if (!delegate->Duplicate(&temp_fd, i->dest))
+ return false;
+ extra_fds.push_back(temp_fd);
+ }
+
+ j->source = temp_fd;
+ j->close = false;
+ }
+
+ if (i->close && i->source == j->dest)
+ i->close = false;
+
+ if (i->close && i->source == j->source) {
+ i->close = false;
+ j->close = true;
+ }
+ }
+
+ if (!is_identity) {
+ if (!delegate->Move(i->source, i->dest))
+ return false;
+ }
+
+ if (!is_identity && i->close)
+ delegate->Close(i->source);
+ }
+
+ for (std::vector<int>::const_iterator
+ i = extra_fds.begin(); i != extra_fds.end(); ++i) {
+ delegate->Close(*i);
+ }
+
+ return true;
+}
+
+bool FileDescriptorTableInjection::Duplicate(int* result, int fd) {
+ do {
+ *result = dup(fd);
+ } while(*result == -1 && errno == EINTR);
+
+ return *result >= 0;
+}
+
+bool FileDescriptorTableInjection::Move(int src, int dest) {
+ int result;
+
+ do {
+ result = dup2(src, dest);
+ } while (result == -1 && errno == EINTR);
+
+ return result != -1;
+}
+
+void FileDescriptorTableInjection::Close(int fd) {
+ int result;
+
+ do {
+ result = close(fd);
+ } while (result == -1 && errno == EINTR);
+}
+
+} // namespace base
diff --git a/base/file_descriptor_shuffle.h b/base/file_descriptor_shuffle.h
new file mode 100644
index 0000000..8ee77409
--- /dev/null
+++ b/base/file_descriptor_shuffle.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILE_DESCRIPTOR_SHUFFLE_H_
+#define BASE_FILE_DESCRIPTOR_SHUFFLE_H_
+
+// This code exists to perform the shuffling of file descriptors which is
+// commonly needed when forking subprocesses. The naive approve is very simple,
+// just call dup2 to setup the desired descriptors, but wrong. It's tough to
+// handle the edge cases (like mapping 0 -> 1, 1 -> 0) correctly.
+//
+// In order to unittest this code, it's broken into the abstract action (an
+// injective multimap) and the concrete code for dealing with file descriptors.
+// Users should use the code like this:
+// base::InjectiveMultimap file_descriptor_map;
+// file_descriptor_map.push_back(base::InjectionArc(devnull, 0, true));
+// file_descriptor_map.push_back(base::InjectionArc(devnull, 2, true));
+// file_descriptor_map.push_back(base::InjectionArc(pipe[1], 1, true));
+// base::ShuffleFileDescriptors(file_descriptor_map);
+//
+// and trust the the Right Thing will get done.
+
+#include <vector>
+
+namespace base {
+
+// A Delegate which performs the actions required to perform an injective
+// multimapping in place.
+class InjectionDelegate {
+ public:
+ // Duplicate |fd|, an element of the domain, and write a fresh element of the
+ // domain into |result|. Returns true iff successful.
+ virtual bool Duplicate(int* result, int fd) = 0;
+ // Destructively move |src| to |dest|, overwriting |dest|. Returns true iff
+ // successful.
+ virtual bool Move(int src, int dest) = 0;
+ // Delete an element of the domain.
+ virtual void Close(int fd) = 0;
+};
+
+// An implementation of the InjectionDelegate interface using the file
+// descriptor table of the current process as the domain.
+class FileDescriptorTableInjection : public InjectionDelegate {
+ bool Duplicate(int* result, int fd);
+ bool Move(int src, int dest);
+ void Close(int fd);
+};
+
+// A single arc of the directed graph which describes an injective multimapping.
+struct InjectionArc {
+ InjectionArc(int in_source, int in_dest, bool in_close)
+ : source(in_source),
+ dest(in_dest),
+ close(in_close) {
+ }
+
+ int source;
+ int dest;
+ bool close; // if true, delete the source element after performing the
+ // mapping.
+};
+
+typedef std::vector<InjectionArc> InjectiveMultimap;
+
+bool PerformInjectiveMultimap(const InjectiveMultimap& map,
+ InjectionDelegate* delegate);
+
+static inline bool ShuffleFileDescriptors(const InjectiveMultimap& map) {
+ FileDescriptorTableInjection delegate;
+ return PerformInjectiveMultimap(map, &delegate);
+}
+
+} // namespace base
+
+#endif // !BASE_FILE_DESCRIPTOR_SHUFFLE_H_
diff --git a/base/file_descriptor_shuffle_unittest.cc b/base/file_descriptor_shuffle_unittest.cc
new file mode 100644
index 0000000..981eed9
--- /dev/null
+++ b/base/file_descriptor_shuffle_unittest.cc
@@ -0,0 +1,289 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_descriptor_shuffle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::InjectiveMultimap;
+using base::InjectionArc;
+using base::PerformInjectiveMultimap;
+using base::InjectionDelegate;
+
+namespace {
+typedef testing::Test FileDescriptorShuffleTest;
+}
+
+// 'Duplicated' file descriptors start at this number
+static const int kDuplicateBase = 1000;
+
+struct Action {
+ enum Type {
+ CLOSE,
+ MOVE,
+ DUPLICATE,
+ };
+
+ Action(Type in_type, int in_fd1, int in_fd2 = -1)
+ : type(in_type),
+ fd1(in_fd1),
+ fd2(in_fd2) {
+ }
+
+ bool operator==(const Action& other) const {
+ return other.type == type &&
+ other.fd1 == fd1 &&
+ other.fd2 == fd2;
+ }
+
+ Type type;
+ int fd1;
+ int fd2;
+};
+
+class InjectionTracer : public InjectionDelegate {
+ public:
+ InjectionTracer()
+ : next_duplicate_(kDuplicateBase) {
+ }
+
+ bool Duplicate(int* result, int fd) {
+ *result = next_duplicate_++;
+ actions_.push_back(Action(Action::DUPLICATE, *result, fd));
+ return true;
+ }
+
+ bool Move(int src, int dest) {
+ actions_.push_back(Action(Action::MOVE, src, dest));
+ return true;
+ }
+
+ void Close(int fd) {
+ actions_.push_back(Action(Action::CLOSE, fd));
+ }
+
+ const std::vector<Action>& actions() const { return actions_; }
+
+ private:
+ int next_duplicate_;
+ std::vector<Action> actions_;
+};
+
+TEST(FileDescriptorShuffleTest, Empty) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(0u, tracer.actions().size());
+}
+
+TEST(FileDescriptorShuffleTest, Noop) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(0u, tracer.actions().size());
+}
+
+TEST(FileDescriptorShuffleTest, NoopAndClose) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 0, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(0u, tracer.actions().size());
+}
+
+TEST(FileDescriptorShuffleTest, Simple1) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(1u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+}
+
+TEST(FileDescriptorShuffleTest, Simple2) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(2, 3, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 2, 3));
+}
+
+TEST(FileDescriptorShuffleTest, Simple3) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 0));
+}
+
+TEST(FileDescriptorShuffleTest, Simple4) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(10, 0, true));
+ map.push_back(InjectionArc(1, 1, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 10, 0));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 10));
+}
+
+TEST(FileDescriptorShuffleTest, Cycle) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(1, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, CycleAndClose1) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(1, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, CycleAndClose2) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(1, 0, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, CycleAndClose3) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(1, 0, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, Fanout) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(0, 2, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+}
+
+TEST(FileDescriptorShuffleTest, FanoutAndClose1) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(0, 2, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(3u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
+}
+
+TEST(FileDescriptorShuffleTest, FanoutAndClose2) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(0, 2, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(3u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
+}
+
+TEST(FileDescriptorShuffleTest, FanoutAndClose3) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(0, 2, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(3u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
+}
+
+class FailingDelegate : public InjectionDelegate {
+ public:
+ bool Duplicate(int* result, int fd) {
+ return false;
+ }
+
+ bool Move(int src, int dest) {
+ return false;
+ }
+
+ void Close(int fd) {
+ }
+};
+
+TEST(FileDescriptorShuffleTest, EmptyWithFailure) {
+ InjectiveMultimap map;
+ FailingDelegate failing;
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &failing));
+}
+
+TEST(FileDescriptorShuffleTest, NoopWithFailure) {
+ InjectiveMultimap map;
+ FailingDelegate failing;
+ map.push_back(InjectionArc(0, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &failing));
+}
+
+TEST(FileDescriptorShuffleTest, Simple1WithFailure) {
+ InjectiveMultimap map;
+ FailingDelegate failing;
+ map.push_back(InjectionArc(0, 1, false));
+
+ EXPECT_FALSE(PerformInjectiveMultimap(map, &failing));
+}
diff --git a/base/process_util.h b/base/process_util.h
index a459ed4..fe1b4d2 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -44,6 +44,8 @@ struct IoCounters {
unsigned long long WriteTransferCount;
unsigned long long OtherTransferCount;
};
+
+#include "base/file_descriptor_shuffle.h"
#endif
#if defined(OS_MACOSX)
@@ -88,7 +90,14 @@ ProcessId GetProcId(ProcessHandle process);
#if defined(OS_POSIX)
// Sets all file descriptors to close on exec except for stdin, stdout
// and stderr.
+// TODO(agl): remove this function
+// WARNING: do not use. It's inherently race-prone in the face of
+// multi-threading.
void SetAllFDsToCloseOnExec();
+// Close all file descriptors, expect those which are a destination in the
+// given multimap. Only call this function in a child process where you know
+// that there aren't any other threads.
+void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map);
#endif
#if defined(OS_WIN)
diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc
index 686b04b..f8e8a04 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -31,30 +31,22 @@ namespace base {
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle) {
- // Make sure we don't leak any FDs to the child process by marking all FDs
- // as close-on-exec.
- SetAllFDsToCloseOnExec();
-
pid_t pid = fork();
if (pid < 0)
return false;
if (pid == 0) {
- for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
- it != fds_to_remap.end();
- ++it) {
- int src_fd = it->first;
- int dest_fd = it->second;
- if (src_fd == dest_fd) {
- int flags = fcntl(src_fd, F_GETFD);
- if (flags != -1) {
- fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
- }
- } else {
- dup2(src_fd, dest_fd);
- }
+ InjectiveMultimap fd_shuffle;
+ for (file_handle_mapping_vector::const_iterator
+ it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
+ fd_shuffle.push_back(InjectionArc(it->first, it->second, false));
}
+ if (!ShuffleFileDescriptors(fd_shuffle))
+ exit(127);
+
+ CloseSuperfluousFds(fd_shuffle);
+
scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
for (size_t i = 0; i < argv.size(); i++)
argv_cstr[i] = const_cast<char*>(argv[i].c_str());
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index 859dfc3..c0e792b 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -14,6 +14,7 @@
#include <unistd.h>
#include <limits>
+#include <set>
#include "base/basictypes.h"
#include "base/logging.h"
@@ -95,8 +96,82 @@ class ScopedDIRClose {
};
typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR;
+void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
+ std::set<int> saved_fds;
+
+ // Don't close stdin, stdout and stderr
+ saved_fds.insert(STDIN_FILENO);
+ saved_fds.insert(STDOUT_FILENO);
+ saved_fds.insert(STDERR_FILENO);
+
+ for (base::InjectiveMultimap::const_iterator
+ i = saved_mapping.begin(); i != saved_mapping.end(); ++i) {
+ saved_fds.insert(i->dest);
+ }
+
+#if defined(OS_LINUX)
+ static const char fd_dir[] = "/proc/self/fd";
+#elif defined(OS_MACOSX)
+ static const char fd_dir[] = "/dev/fd";
+#endif
+
+ ScopedDIR dir_closer(opendir(fd_dir));
+ DIR *dir = dir_closer.get();
+ if (NULL == dir) {
+ DLOG(ERROR) << "Unable to open " << fd_dir;
+
+ // Fallback case
+ struct rlimit nofile;
+ rlim_t num_fds;
+ if (getrlimit(RLIMIT_NOFILE, &nofile)) {
+ // getrlimit failed. Take a best guess.
+ num_fds = 8192;
+ DLOG(ERROR) << "getrlimit(RLIMIT_NOFILE) failed: " << errno;
+ } else {
+ num_fds = nofile.rlim_cur;
+ }
+
+ if (num_fds > INT_MAX)
+ num_fds = INT_MAX;
+
+ for (rlim_t i = 0; i < num_fds; ++i) {
+ const int fd = static_cast<int>(i);
+ if (saved_fds.find(fd) != saved_fds.end())
+ continue;
+
+ int result;
+ do {
+ result = close(fd);
+ } while (result == -1 && errno == EINTR);
+ }
+ return;
+ }
+
+ struct dirent *ent;
+ while ((ent = readdir(dir))) {
+ // Skip . and .. entries.
+ if (ent->d_name[0] == '.')
+ continue;
+
+ char *endptr;
+ errno = 0;
+ const long int fd = strtol(ent->d_name, &endptr, 10);
+ if (ent->d_name[0] == 0 || *endptr || fd < 0 || fd >= INT_MAX || errno)
+ continue;
+ if (saved_fds.find(fd) != saved_fds.end())
+ continue;
+
+ int result;
+ do {
+ result = close(fd);
+ } while (result == -1 && errno == EINTR);
+ }
+}
+
// Sets all file descriptors to close on exec except for stdin, stdout
// and stderr.
+// TODO(agl): Remove this function. It's fundamentally broken for multithreaded
+// apps.
void SetAllFDsToCloseOnExec() {
#if defined(OS_LINUX)
const char fd_dir[] = "/proc/self/fd";
@@ -354,19 +429,15 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) {
if (dev_null < 0)
exit(127);
- int rv;
- do {
- rv = dup2(pipe_fd[1], STDOUT_FILENO);
- } while (rv == -1 && errno == EINTR);
- do {
- rv = dup2(dev_null, STDERR_FILENO);
- } while (rv == -1 && errno == EINTR);
- if (pipe_fd[0] != STDOUT_FILENO && pipe_fd[0] != STDERR_FILENO)
- close(pipe_fd[0]);
- if (pipe_fd[1] != STDOUT_FILENO && pipe_fd[1] != STDERR_FILENO)
- close(pipe_fd[1]);
- if (dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO)
- close(dev_null);
+ InjectiveMultimap fd_shuffle;
+ fd_shuffle.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
+ fd_shuffle.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
+ fd_shuffle.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
+
+ if (!ShuffleFileDescriptors(fd_shuffle))
+ exit(127);
+
+ CloseSuperfluousFds(fd_shuffle);
const std::vector<std::string> argv = cl.argv();
scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);