summaryrefslogtreecommitdiffstats
path: root/base/file_descriptor_shuffle.cc
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-30 19:40:03 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-30 19:40:03 +0000
commit3f04f2b1152b3c2b1db720792ed43c7600919953 (patch)
treeeb9e8606323e0cf51fdde717f2a351f3685f0c95 /base/file_descriptor_shuffle.cc
parente611fd66e64564d17865ed19658dc0b56e1ee746 (diff)
downloadchromium_src-3f04f2b1152b3c2b1db720792ed43c7600919953.zip
chromium_src-3f04f2b1152b3c2b1db720792ed43c7600919953.tar.gz
chromium_src-3f04f2b1152b3c2b1db720792ed43c7600919953.tar.bz2
POSIX: Add code for shuffling file descriptors.
When forking a child process, one often wants to move existing file descriptors to well known locations (stdout, stderr etc). However, this is a process bedeviled with corner cases. Consider the case where you open two file descriptors, get assigned fds 1 and 0 for them and wish to shuffle them so that they end up in slots 0 and 1. Our current code fails in this case. We also have a problem where we're currently trying to mark file descriptors as close-on-exec rather than closing them in the child process. This is inherently broken in a multithreaded process where another thread can open a file descriptor and race the loop which is trying to mark them. Thus, on Linux we switch to close-after-fork where we known that no other threads exist in the process. On Mac, the code is sufficiently different that this simple fix isn't applicable and one of the Mac folks will need to take a look. http://codereview.chromium.org/100127 BUG=11174 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14978 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/file_descriptor_shuffle.cc')
-rw-r--r--base/file_descriptor_shuffle.cc92
1 files changed, 92 insertions, 0 deletions
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