summaryrefslogtreecommitdiff
path: root/base/posix
diff options
context:
space:
mode:
authorDaniel Erat <derat@google.com>2015-07-06 13:18:13 -0600
committerDaniel Erat <derat@google.com>2015-07-07 08:49:02 -0600
commitb8cf94937c52feb53b55c39e3f82094d27de464c (patch)
tree20cd8f9867dfe9c0c2510899346c744605425b75 /base/posix
parent5bb0a2e04cf1346d5a2819f277381c0415549600 (diff)
downloadlibchrome-b8cf94937c52feb53b55c39e3f82094d27de464c.tar.gz
Add upstream code as of Chromium r334380.
Copy the unchanged source from https://chromium.googlesource.com/chromium/src/base/ as of r334380 (really r334285 a.k.a. 23911a0c in the base/ subtree). Also add MODULE_LICENSE_BSD and copy Chromium's current LICENSE file to NOTICE. Bug: 22317122 Change-Id: I89863bfeca67b3a1ff05e6078f2f9ee4e31c5c99
Diffstat (limited to 'base/posix')
-rw-r--r--base/posix/eintr_wrapper.h67
-rw-r--r--base/posix/file_descriptor_shuffle.cc100
-rw-r--r--base/posix/file_descriptor_shuffle.h87
-rw-r--r--base/posix/file_descriptor_shuffle_unittest.cc281
-rw-r--r--base/posix/global_descriptors.cc85
-rw-r--r--base/posix/global_descriptors.h92
-rw-r--r--base/posix/safe_strerror.cc124
-rw-r--r--base/posix/safe_strerror.h42
-rw-r--r--base/posix/unix_domain_socket_linux.cc245
-rw-r--r--base/posix/unix_domain_socket_linux.h103
-rw-r--r--base/posix/unix_domain_socket_linux_unittest.cc161
11 files changed, 1387 insertions, 0 deletions
diff --git a/base/posix/eintr_wrapper.h b/base/posix/eintr_wrapper.h
new file mode 100644
index 0000000000..5a5dc758a9
--- /dev/null
+++ b/base/posix/eintr_wrapper.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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.
+
+// This provides a wrapper around system calls which may be interrupted by a
+// signal and return EINTR. See man 7 signal.
+// To prevent long-lasting loops (which would likely be a bug, such as a signal
+// that should be masked) to go unnoticed, there is a limit after which the
+// caller will nonetheless see an EINTR in Debug builds.
+//
+// On Windows, this wrapper macro does nothing.
+//
+// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return
+// value of close is significant. See http://crbug.com/269623.
+
+#ifndef BASE_POSIX_EINTR_WRAPPER_H_
+#define BASE_POSIX_EINTR_WRAPPER_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+
+#include <errno.h>
+
+#if defined(NDEBUG)
+
+#define HANDLE_EINTR(x) ({ \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR); \
+ eintr_wrapper_result; \
+})
+
+#else
+
+#define HANDLE_EINTR(x) ({ \
+ int eintr_wrapper_counter = 0; \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR && \
+ eintr_wrapper_counter++ < 100); \
+ eintr_wrapper_result; \
+})
+
+#endif // NDEBUG
+
+#define IGNORE_EINTR(x) ({ \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ if (eintr_wrapper_result == -1 && errno == EINTR) { \
+ eintr_wrapper_result = 0; \
+ } \
+ } while (0); \
+ eintr_wrapper_result; \
+})
+
+#else
+
+#define HANDLE_EINTR(x) (x)
+#define IGNORE_EINTR(x) (x)
+
+#endif // OS_POSIX
+
+#endif // BASE_POSIX_EINTR_WRAPPER_H_
diff --git a/base/posix/file_descriptor_shuffle.cc b/base/posix/file_descriptor_shuffle.cc
new file mode 100644
index 0000000000..d2fd39a95a
--- /dev/null
+++ b/base/posix/file_descriptor_shuffle.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2011 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/posix/file_descriptor_shuffle.h"
+
+#include <unistd.h>
+#include <stddef.h>
+#include <ostream>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/logging.h"
+
+namespace base {
+
+bool PerformInjectiveMultimapDestructive(
+ InjectiveMultimap* m, InjectionDelegate* delegate) {
+ static const size_t kMaxExtraFDs = 16;
+ int extra_fds[kMaxExtraFDs];
+ unsigned next_extra_fd = 0;
+
+ // DANGER: this function must not allocate or lock.
+ // Cannot use STL iterators here, since debug iterators use locks.
+
+ for (size_t i_index = 0; i_index < m->size(); ++i_index) {
+ InjectiveMultimap::value_type* i = &(*m)[i_index];
+ int temp_fd = -1;
+
+ // We DCHECK the injectiveness of the mapping.
+ for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
+ InjectiveMultimap::value_type* j = &(*m)[j_index];
+ DCHECK(i->dest != j->dest) << "Both fd " << i->source
+ << " and " << j->source << " map to " << i->dest;
+ }
+
+ const bool is_identity = i->source == i->dest;
+
+ for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
+ InjectiveMultimap::value_type* j = &(*m)[j_index];
+ if (!is_identity && i->dest == j->source) {
+ if (temp_fd == -1) {
+ if (!delegate->Duplicate(&temp_fd, i->dest))
+ return false;
+ if (next_extra_fd < kMaxExtraFDs) {
+ extra_fds[next_extra_fd++] = temp_fd;
+ } else {
+ RAW_LOG(ERROR, "PerformInjectiveMultimapDestructive overflowed "
+ "extra_fds. Leaking file descriptors!");
+ }
+ }
+
+ 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 (unsigned i = 0; i < next_extra_fd; i++)
+ delegate->Close(extra_fds[i]);
+
+ return true;
+}
+
+bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
+ InjectionDelegate* delegate) {
+ InjectiveMultimap m(m_in);
+ return PerformInjectiveMultimapDestructive(&m, delegate);
+}
+
+bool FileDescriptorTableInjection::Duplicate(int* result, int fd) {
+ *result = HANDLE_EINTR(dup(fd));
+ return *result >= 0;
+}
+
+bool FileDescriptorTableInjection::Move(int src, int dest) {
+ return HANDLE_EINTR(dup2(src, dest)) != -1;
+}
+
+void FileDescriptorTableInjection::Close(int fd) {
+ int ret = IGNORE_EINTR(close(fd));
+ DPCHECK(ret == 0);
+}
+
+} // namespace base
diff --git a/base/posix/file_descriptor_shuffle.h b/base/posix/file_descriptor_shuffle.h
new file mode 100644
index 0000000000..78e3a7d493
--- /dev/null
+++ b/base/posix/file_descriptor_shuffle.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 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_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
+#define BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
+
+// This code exists to shuffle file descriptors, which is commonly needed when
+// forking subprocesses. The naive approach (just call dup2 to set up the
+// desired descriptors) is very simple, but wrong: it won't handle 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>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+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;
+
+ protected:
+ virtual ~InjectionDelegate() {}
+};
+
+// An implementation of the InjectionDelegate interface using the file
+// descriptor table of the current process as the domain.
+class BASE_EXPORT FileDescriptorTableInjection : public InjectionDelegate {
+ bool Duplicate(int* result, int fd) override;
+ bool Move(int src, int dest) override;
+ void Close(int fd) override;
+};
+
+// 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;
+
+BASE_EXPORT bool PerformInjectiveMultimap(const InjectiveMultimap& map,
+ InjectionDelegate* delegate);
+
+BASE_EXPORT bool PerformInjectiveMultimapDestructive(
+ InjectiveMultimap* map,
+ InjectionDelegate* delegate);
+
+// This function will not call malloc but will mutate |map|
+static inline bool ShuffleFileDescriptors(InjectiveMultimap* map) {
+ FileDescriptorTableInjection delegate;
+ return PerformInjectiveMultimapDestructive(map, &delegate);
+}
+
+} // namespace base
+
+#endif // BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
diff --git a/base/posix/file_descriptor_shuffle_unittest.cc b/base/posix/file_descriptor_shuffle_unittest.cc
new file mode 100644
index 0000000000..3dfbf7e246
--- /dev/null
+++ b/base/posix/file_descriptor_shuffle_unittest.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2012 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/posix/file_descriptor_shuffle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// 'Duplicated' file descriptors start at this number
+const int kDuplicateBase = 1000;
+
+} // namespace
+
+namespace base {
+
+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) override {
+ *result = next_duplicate_++;
+ actions_.push_back(Action(Action::DUPLICATE, *result, fd));
+ return true;
+ }
+
+ bool Move(int src, int dest) override {
+ actions_.push_back(Action(Action::MOVE, src, dest));
+ return true;
+ }
+
+ void Close(int fd) override { 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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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));
+ ASSERT_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) override { return false; }
+
+ bool Move(int src, int dest) override { return false; }
+
+ void Close(int fd) override {}
+};
+
+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));
+}
+
+} // namespace base
diff --git a/base/posix/global_descriptors.cc b/base/posix/global_descriptors.cc
new file mode 100644
index 0000000000..6c187838ad
--- /dev/null
+++ b/base/posix/global_descriptors.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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/posix/global_descriptors.h"
+
+#include <vector>
+#include <utility>
+
+#include "base/logging.h"
+
+namespace base {
+
+GlobalDescriptors::Descriptor::Descriptor(Key key, int fd)
+ : key(key), fd(fd), region(base::MemoryMappedFile::Region::kWholeFile) {
+}
+
+GlobalDescriptors::Descriptor::Descriptor(Key key,
+ int fd,
+ base::MemoryMappedFile::Region region)
+ : key(key), fd(fd), region(region) {
+}
+
+// static
+GlobalDescriptors* GlobalDescriptors::GetInstance() {
+ typedef Singleton<base::GlobalDescriptors,
+ LeakySingletonTraits<base::GlobalDescriptors> >
+ GlobalDescriptorsSingleton;
+ return GlobalDescriptorsSingleton::get();
+}
+
+int GlobalDescriptors::Get(Key key) const {
+ const int ret = MaybeGet(key);
+
+ if (ret == -1)
+ DLOG(FATAL) << "Unknown global descriptor: " << key;
+ return ret;
+}
+
+int GlobalDescriptors::MaybeGet(Key key) const {
+ for (Mapping::const_iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ if (i->key == key)
+ return i->fd;
+ }
+
+ return -1;
+}
+
+void GlobalDescriptors::Set(Key key, int fd) {
+ Set(key, fd, base::MemoryMappedFile::Region::kWholeFile);
+}
+
+void GlobalDescriptors::Set(Key key,
+ int fd,
+ base::MemoryMappedFile::Region region) {
+ for (auto& i : descriptors_) {
+ if (i.key == key) {
+ i.fd = fd;
+ i.region = region;
+ return;
+ }
+ }
+
+ descriptors_.push_back(Descriptor(key, fd, region));
+}
+
+base::MemoryMappedFile::Region GlobalDescriptors::GetRegion(Key key) const {
+ for (const auto& i : descriptors_) {
+ if (i.key == key)
+ return i.region;
+ }
+ DLOG(FATAL) << "Unknown global descriptor: " << key;
+ return base::MemoryMappedFile::Region::kWholeFile;
+}
+
+void GlobalDescriptors::Reset(const Mapping& mapping) {
+ descriptors_ = mapping;
+}
+
+GlobalDescriptors::GlobalDescriptors() {}
+
+GlobalDescriptors::~GlobalDescriptors() {}
+
+} // namespace base
diff --git a/base/posix/global_descriptors.h b/base/posix/global_descriptors.h
new file mode 100644
index 0000000000..c774634f59
--- /dev/null
+++ b/base/posix/global_descriptors.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 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_POSIX_GLOBAL_DESCRIPTORS_H_
+#define BASE_POSIX_GLOBAL_DESCRIPTORS_H_
+
+#include "build/build_config.h"
+
+#include <vector>
+#include <utility>
+
+#include <stdint.h>
+
+#include "base/files/memory_mapped_file.h"
+#include "base/memory/singleton.h"
+
+namespace base {
+
+// It's common practice to install file descriptors into well known slot
+// numbers before execing a child; stdin, stdout and stderr are ubiqutous
+// examples.
+//
+// However, when using a zygote model, this becomes troublesome. Since the
+// descriptors which need to be in these slots generally aren't known, any code
+// could open a resource and take one of the reserved descriptors. Simply
+// overwriting the slot isn't a viable solution.
+//
+// We could try to fill the reserved slots as soon as possible, but this is a
+// fragile solution since global constructors etc are able to open files.
+//
+// Instead, we retreat from the idea of installing descriptors in specific
+// slots and add a layer of indirection in the form of this singleton object.
+// It maps from an abstract key to a descriptor. If independent modules each
+// need to define keys, then values should be chosen randomly so as not to
+// collide.
+class BASE_EXPORT GlobalDescriptors {
+ public:
+ typedef uint32_t Key;
+ struct Descriptor {
+ Descriptor(Key key, int fd);
+ Descriptor(Key key, int fd, base::MemoryMappedFile::Region region);
+
+ // Globally unique key.
+ Key key;
+ // Actual FD.
+ int fd;
+ // Optional region, defaults to kWholeFile.
+ base::MemoryMappedFile::Region region;
+ };
+ typedef std::vector<Descriptor> Mapping;
+
+ // Often we want a canonical descriptor for a given Key. In this case, we add
+ // the following constant to the key value:
+#if !defined(OS_ANDROID)
+ static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken.
+#else
+ static const int kBaseDescriptor = 4; // 3 used by __android_log_write().
+#endif
+
+ // Return the singleton instance of GlobalDescriptors.
+ static GlobalDescriptors* GetInstance();
+
+ // Get a descriptor given a key. It is a fatal error if the key is not known.
+ int Get(Key key) const;
+
+ // Get a descriptor given a key. Returns -1 on error.
+ int MaybeGet(Key key) const;
+
+ // Get a region given a key. It is a fatal error if the key is not known.
+ base::MemoryMappedFile::Region GetRegion(Key key) const;
+
+ // Set the descriptor for the given |key|. This sets the region associated
+ // with |key| to kWholeFile.
+ void Set(Key key, int fd);
+
+ // Set the descriptor and |region| for the given |key|.
+ void Set(Key key, int fd, base::MemoryMappedFile::Region region);
+
+ void Reset(const Mapping& mapping);
+
+ private:
+ friend struct DefaultSingletonTraits<GlobalDescriptors>;
+ GlobalDescriptors();
+ ~GlobalDescriptors();
+
+ Mapping descriptors_;
+};
+
+} // namespace base
+
+#endif // BASE_POSIX_GLOBAL_DESCRIPTORS_H_
diff --git a/base/posix/safe_strerror.cc b/base/posix/safe_strerror.cc
new file mode 100644
index 0000000000..e80e8f8bd9
--- /dev/null
+++ b/base/posix/safe_strerror.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2006-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.
+
+#if defined(__ANDROID__)
+// Post-L versions of bionic define the GNU-specific strerror_r if _GNU_SOURCE
+// is defined, but the symbol is renamed to __gnu_strerror_r which only exists
+// on those later versions. To preserve ABI compatibility with older versions,
+// undefine _GNU_SOURCE and use the POSIX version.
+#undef _GNU_SOURCE
+#endif
+
+#include "base/posix/safe_strerror.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "build/build_config.h"
+
+namespace base {
+
+#define USE_HISTORICAL_STRERRO_R (defined(__GLIBC__) || defined(OS_NACL))
+
+#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
+// GCC will complain about the unused second wrap function unless we tell it
+// that we meant for them to be potentially unused, which is exactly what this
+// attribute is for.
+#define POSSIBLY_UNUSED __attribute__((unused))
+#else
+#define POSSIBLY_UNUSED
+#endif
+
+#if USE_HISTORICAL_STRERRO_R
+// glibc has two strerror_r functions: a historical GNU-specific one that
+// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
+// that returns int. This wraps the GNU-specific one.
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(
+ char *(*strerror_r_ptr)(int, char *, size_t),
+ int err,
+ char *buf,
+ size_t len) {
+ // GNU version.
+ char *rc = (*strerror_r_ptr)(err, buf, len);
+ if (rc != buf) {
+ // glibc did not use buf and returned a static string instead. Copy it
+ // into buf.
+ buf[0] = '\0';
+ strncat(buf, rc, len - 1);
+ }
+ // The GNU version never fails. Unknown errors get an "unknown error" message.
+ // The result is always null terminated.
+}
+#endif // USE_HISTORICAL_STRERRO_R
+
+// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
+// does not define the behaviour for some of the edge cases, so we wrap it to
+// guarantee that they are handled. This is compiled on all POSIX platforms, but
+// it will only be used on Linux if the POSIX strerror_r implementation is
+// being used (see below).
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(
+ int (*strerror_r_ptr)(int, char *, size_t),
+ int err,
+ char *buf,
+ size_t len) {
+ int old_errno = errno;
+ // Have to cast since otherwise we get an error if this is the GNU version
+ // (but in such a scenario this function is never called). Sadly we can't use
+ // C++-style casts because the appropriate one is reinterpret_cast but it's
+ // considered illegal to reinterpret_cast a type to itself, so we get an
+ // error in the opposite case.
+ int result = (*strerror_r_ptr)(err, buf, len);
+ if (result == 0) {
+ // POSIX is vague about whether the string will be terminated, although
+ // it indirectly implies that typically ERANGE will be returned, instead
+ // of truncating the string. We play it safe by always terminating the
+ // string explicitly.
+ buf[len - 1] = '\0';
+ } else {
+ // Error. POSIX is vague about whether the return value is itself a system
+ // error code or something else. On Linux currently it is -1 and errno is
+ // set. On BSD-derived systems it is a system error and errno is unchanged.
+ // We try and detect which case it is so as to put as much useful info as
+ // we can into our message.
+ int strerror_error; // The error encountered in strerror
+ int new_errno = errno;
+ if (new_errno != old_errno) {
+ // errno was changed, so probably the return value is just -1 or something
+ // else that doesn't provide any info, and errno is the error.
+ strerror_error = new_errno;
+ } else {
+ // Either the error from strerror_r was the same as the previous value, or
+ // errno wasn't used. Assume the latter.
+ strerror_error = result;
+ }
+ // snprintf truncates and always null-terminates.
+ snprintf(buf,
+ len,
+ "Error %d while retrieving error %d",
+ strerror_error,
+ err);
+ }
+ errno = old_errno;
+}
+
+void safe_strerror_r(int err, char *buf, size_t len) {
+ if (buf == NULL || len <= 0) {
+ return;
+ }
+ // If using glibc (i.e., Linux), the compiler will automatically select the
+ // appropriate overloaded function based on the function type of strerror_r.
+ // The other one will be elided from the translation unit since both are
+ // static.
+ wrap_posix_strerror_r(&strerror_r, err, buf, len);
+}
+
+std::string safe_strerror(int err) {
+ const int buffer_size = 256;
+ char buf[buffer_size];
+ safe_strerror_r(err, buf, sizeof(buf));
+ return std::string(buf);
+}
+
+} // namespace base
diff --git a/base/posix/safe_strerror.h b/base/posix/safe_strerror.h
new file mode 100644
index 0000000000..862a75066c
--- /dev/null
+++ b/base/posix/safe_strerror.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 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_POSIX_SAFE_STRERROR_H_
+#define BASE_POSIX_SAFE_STRERROR_H_
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+
+// BEFORE using anything from this file, first look at PLOG and friends in
+// logging.h and use them instead if applicable.
+//
+// This file declares safe, portable alternatives to the POSIX strerror()
+// function. strerror() is inherently unsafe in multi-threaded apps and should
+// never be used. Doing so can cause crashes. Additionally, the thread-safe
+// alternative strerror_r varies in semantics across platforms. Use these
+// functions instead.
+
+// Thread-safe strerror function with dependable semantics that never fails.
+// It will write the string form of error "err" to buffer buf of length len.
+// If there is an error calling the OS's strerror_r() function then a message to
+// that effect will be printed into buf, truncating if necessary. The final
+// result is always null-terminated. The value of errno is never changed.
+//
+// Use this instead of strerror_r().
+BASE_EXPORT void safe_strerror_r(int err, char *buf, size_t len);
+
+// Calls safe_strerror_r with a buffer of suitable size and returns the result
+// in a C++ string.
+//
+// Use this instead of strerror(). Note though that safe_strerror_r will be
+// more robust in the case of heap corruption errors, since it doesn't need to
+// allocate a string.
+BASE_EXPORT std::string safe_strerror(int err);
+
+} // namespace base
+
+#endif // BASE_POSIX_SAFE_STRERROR_H_
diff --git a/base/posix/unix_domain_socket_linux.cc b/base/posix/unix_domain_socket_linux.cc
new file mode 100644
index 0000000000..62c930fdee
--- /dev/null
+++ b/base/posix/unix_domain_socket_linux.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2011 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/posix/unix_domain_socket_linux.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
+
+#if !defined(OS_NACL_NONSFI)
+#include <sys/uio.h>
+#endif
+
+namespace base {
+
+const size_t UnixDomainSocket::kMaxFileDescriptors = 16;
+
+#if !defined(OS_NACL_NONSFI)
+// Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes
+// ownership of the newly allocated file descriptors to |one| and |two|.
+// Returns true on success.
+static bool CreateSocketPair(ScopedFD* one, ScopedFD* two) {
+ int raw_socks[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks) == -1)
+ return false;
+ one->reset(raw_socks[0]);
+ two->reset(raw_socks[1]);
+ return true;
+}
+
+// static
+bool UnixDomainSocket::EnableReceiveProcessId(int fd) {
+ const int enable = 1;
+ return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0;
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+// static
+bool UnixDomainSocket::SendMsg(int fd,
+ const void* buf,
+ size_t length,
+ const std::vector<int>& fds) {
+ struct msghdr msg = {};
+ struct iovec iov = { const_cast<void*>(buf), length };
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char* control_buffer = NULL;
+ if (fds.size()) {
+ const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size());
+ control_buffer = new char[control_len];
+
+ struct cmsghdr* cmsg;
+ msg.msg_control = control_buffer;
+ msg.msg_controllen = control_len;
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+ memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size());
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+
+ // Avoid a SIGPIPE if the other end breaks the connection.
+ // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't
+ // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by
+ // POSIX.
+ const int flags = MSG_NOSIGNAL;
+ const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags));
+ const bool ret = static_cast<ssize_t>(length) == r;
+ delete[] control_buffer;
+ return ret;
+}
+
+// static
+ssize_t UnixDomainSocket::RecvMsg(int fd,
+ void* buf,
+ size_t length,
+ ScopedVector<ScopedFD>* fds) {
+ return UnixDomainSocket::RecvMsgWithPid(fd, buf, length, fds, NULL);
+}
+
+// static
+ssize_t UnixDomainSocket::RecvMsgWithPid(int fd,
+ void* buf,
+ size_t length,
+ ScopedVector<ScopedFD>* fds,
+ ProcessId* pid) {
+ return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds, pid);
+}
+
+// static
+ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
+ void* buf,
+ size_t length,
+ int flags,
+ ScopedVector<ScopedFD>* fds,
+ ProcessId* out_pid) {
+ fds->clear();
+
+ struct msghdr msg = {};
+ struct iovec iov = { buf, length };
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ const size_t kControlBufferSize =
+ CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)
+#if !defined(OS_NACL_NONSFI)
+ // The PNaCl toolchain for Non-SFI binary build does not support ucred.
+ + CMSG_SPACE(sizeof(struct ucred))
+#endif
+ ;
+ char control_buffer[kControlBufferSize];
+ msg.msg_control = control_buffer;
+ msg.msg_controllen = sizeof(control_buffer);
+
+ const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags));
+ if (r == -1)
+ return -1;
+
+ int* wire_fds = NULL;
+ unsigned wire_fds_len = 0;
+ ProcessId pid = -1;
+
+ if (msg.msg_controllen > 0) {
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ DCHECK_EQ(payload_len % sizeof(int), 0u);
+ DCHECK_EQ(wire_fds, static_cast<void*>(nullptr));
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ wire_fds_len = payload_len / sizeof(int);
+ }
+#if !defined(OS_NACL_NONSFI)
+ // The PNaCl toolchain for Non-SFI binary build does not support
+ // SCM_CREDENTIALS.
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS) {
+ DCHECK_EQ(payload_len, sizeof(struct ucred));
+ DCHECK_EQ(pid, -1);
+ pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid;
+ }
+#endif
+ }
+ }
+
+ if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
+ for (unsigned i = 0; i < wire_fds_len; ++i)
+ close(wire_fds[i]);
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ if (wire_fds) {
+ for (unsigned i = 0; i < wire_fds_len; ++i)
+ fds->push_back(new ScopedFD(wire_fds[i]));
+ }
+
+ if (out_pid) {
+ // |pid| will legitimately be -1 if we read EOF, so only DCHECK if we
+ // actually received a message. Unfortunately, Linux allows sending zero
+ // length messages, which are indistinguishable from EOF, so this check
+ // has false negatives.
+ if (r > 0 || msg.msg_controllen > 0)
+ DCHECK_GE(pid, 0);
+
+ *out_pid = pid;
+ }
+
+ return r;
+}
+
+#if !defined(OS_NACL_NONSFI)
+// static
+ssize_t UnixDomainSocket::SendRecvMsg(int fd,
+ uint8_t* reply,
+ unsigned max_reply_len,
+ int* result_fd,
+ const Pickle& request) {
+ return UnixDomainSocket::SendRecvMsgWithFlags(fd, reply, max_reply_len,
+ 0, /* recvmsg_flags */
+ result_fd, request);
+}
+
+// static
+ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd,
+ uint8_t* reply,
+ unsigned max_reply_len,
+ int recvmsg_flags,
+ int* result_fd,
+ const Pickle& request) {
+ // This socketpair is only used for the IPC and is cleaned up before
+ // returning.
+ ScopedFD recv_sock, send_sock;
+ if (!CreateSocketPair(&recv_sock, &send_sock))
+ return -1;
+
+ {
+ std::vector<int> send_fds;
+ send_fds.push_back(send_sock.get());
+ if (!SendMsg(fd, request.data(), request.size(), send_fds))
+ return -1;
+ }
+
+ // Close the sending end of the socket right away so that if our peer closes
+ // it before sending a response (e.g., from exiting), RecvMsgWithFlags() will
+ // return EOF instead of hanging.
+ send_sock.reset();
+
+ ScopedVector<ScopedFD> recv_fds;
+ // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the
+ // sender might get a SIGPIPE.
+ const ssize_t reply_len = RecvMsgWithFlags(
+ recv_sock.get(), reply, max_reply_len, recvmsg_flags, &recv_fds, NULL);
+ recv_sock.reset();
+ if (reply_len == -1)
+ return -1;
+
+ // If we received more file descriptors than caller expected, then we treat
+ // that as an error.
+ if (recv_fds.size() > (result_fd != NULL ? 1 : 0)) {
+ NOTREACHED();
+ return -1;
+ }
+
+ if (result_fd)
+ *result_fd = recv_fds.empty() ? -1 : recv_fds[0]->release();
+
+ return reply_len;
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+} // namespace base
diff --git a/base/posix/unix_domain_socket_linux.h b/base/posix/unix_domain_socket_linux.h
new file mode 100644
index 0000000000..94da4b4f91
--- /dev/null
+++ b/base/posix/unix_domain_socket_linux.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2011 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_POSIX_UNIX_DOMAIN_SOCKET_LINUX_H_
+#define BASE_POSIX_UNIX_DOMAIN_SOCKET_LINUX_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/files/scoped_file.h"
+#include "base/memory/scoped_vector.h"
+#include "base/process/process_handle.h"
+
+namespace base {
+
+class Pickle;
+
+class BASE_EXPORT UnixDomainSocket {
+ public:
+ // Maximum number of file descriptors that can be read by RecvMsg().
+ static const size_t kMaxFileDescriptors;
+
+#if !defined(OS_NACL_NONSFI)
+ // Use to enable receiving process IDs in RecvMsgWithPid. Should be called on
+ // the receiving socket (i.e., the socket passed to RecvMsgWithPid). Returns
+ // true if successful.
+ static bool EnableReceiveProcessId(int fd);
+#endif // !defined(OS_NACL_NONSFI)
+
+ // Use sendmsg to write the given msg and include a vector of file
+ // descriptors. Returns true if successful.
+ static bool SendMsg(int fd,
+ const void* msg,
+ size_t length,
+ const std::vector<int>& fds);
+
+ // Use recvmsg to read a message and an array of file descriptors. Returns
+ // -1 on failure. Note: will read, at most, |kMaxFileDescriptors| descriptors.
+ static ssize_t RecvMsg(int fd,
+ void* msg,
+ size_t length,
+ ScopedVector<ScopedFD>* fds);
+
+ // Same as RecvMsg above, but also returns the sender's process ID (as seen
+ // from the caller's namespace). However, before using this function to
+ // receive process IDs, EnableReceiveProcessId() should be called on the
+ // receiving socket.
+ static ssize_t RecvMsgWithPid(int fd,
+ void* msg,
+ size_t length,
+ ScopedVector<ScopedFD>* fds,
+ ProcessId* pid);
+
+#if !defined(OS_NACL_NONSFI)
+ // Perform a sendmsg/recvmsg pair.
+ // 1. This process creates a UNIX SEQPACKET socketpair. Using
+ // connection-oriented sockets (SEQPACKET or STREAM) is critical here,
+ // because if one of the ends closes the other one must be notified.
+ // 2. This process writes a request to |fd| with an SCM_RIGHTS control
+ // message containing on end of the fresh socket pair.
+ // 3. This process blocks reading from the other end of the fresh
+ // socketpair.
+ // 4. The target process receives the request, processes it and writes the
+ // reply to the end of the socketpair contained in the request.
+ // 5. This process wakes up and continues.
+ //
+ // fd: descriptor to send the request on
+ // reply: buffer for the reply
+ // reply_len: size of |reply|
+ // result_fd: (may be NULL) the file descriptor returned in the reply
+ // (if any)
+ // request: the bytes to send in the request
+ static ssize_t SendRecvMsg(int fd,
+ uint8_t* reply,
+ unsigned reply_len,
+ int* result_fd,
+ const Pickle& request);
+
+ // Similar to SendRecvMsg(), but |recvmsg_flags| allows to control the flags
+ // of the recvmsg(2) call.
+ static ssize_t SendRecvMsgWithFlags(int fd,
+ uint8_t* reply,
+ unsigned reply_len,
+ int recvmsg_flags,
+ int* result_fd,
+ const Pickle& request);
+#endif // !defined(OS_NACL_NONSFI)
+ private:
+ // Similar to RecvMsg, but allows to specify |flags| for recvmsg(2).
+ static ssize_t RecvMsgWithFlags(int fd,
+ void* msg,
+ size_t length,
+ int flags,
+ ScopedVector<ScopedFD>* fds,
+ ProcessId* pid);
+};
+
+} // namespace base
+
+#endif // BASE_POSIX_UNIX_DOMAIN_SOCKET_LINUX_H_
diff --git a/base/posix/unix_domain_socket_linux_unittest.cc b/base/posix/unix_domain_socket_linux_unittest.cc
new file mode 100644
index 0000000000..175ec52f08
--- /dev/null
+++ b/base/posix/unix_domain_socket_linux_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2013 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 <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/location.h"
+#include "base/memory/scoped_vector.h"
+#include "base/pickle.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) {
+ Thread message_thread("UnixDomainSocketTest");
+ ASSERT_TRUE(message_thread.Start());
+
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ ScopedFD scoped_fd0(fds[0]);
+ ScopedFD scoped_fd1(fds[1]);
+
+ // Have the thread send a synchronous message via the socket.
+ Pickle request;
+ message_thread.task_runner()->PostTask(
+ FROM_HERE,
+ Bind(IgnoreResult(&UnixDomainSocket::SendRecvMsg), fds[1],
+ static_cast<uint8_t*>(NULL), 0U, static_cast<int*>(NULL), request));
+
+ // Receive the message.
+ ScopedVector<ScopedFD> message_fds;
+ uint8_t buffer[16];
+ ASSERT_EQ(static_cast<int>(request.size()),
+ UnixDomainSocket::RecvMsg(fds[0], buffer, sizeof(buffer),
+ &message_fds));
+ ASSERT_EQ(1U, message_fds.size());
+
+ // Close the reply FD.
+ message_fds.clear();
+
+ // Check that the thread didn't get blocked.
+ WaitableEvent event(false, false);
+ message_thread.task_runner()->PostTask(
+ FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&event)));
+ ASSERT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(5000)));
+}
+
+TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) {
+ // Make sure SIGPIPE isn't being ignored.
+ struct sigaction act = {}, oldact;
+ act.sa_handler = SIG_DFL;
+ ASSERT_EQ(0, sigaction(SIGPIPE, &act, &oldact));
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ ScopedFD scoped_fd1(fds[1]);
+ ASSERT_EQ(0, IGNORE_EINTR(close(fds[0])));
+
+ // Have the thread send a synchronous message via the socket. Unless the
+ // message is sent with MSG_NOSIGNAL, this shall result in SIGPIPE.
+ Pickle request;
+ ASSERT_EQ(-1,
+ UnixDomainSocket::SendRecvMsg(fds[1], static_cast<uint8_t*>(NULL),
+ 0U, static_cast<int*>(NULL), request));
+ ASSERT_EQ(EPIPE, errno);
+ // Restore the SIGPIPE handler.
+ ASSERT_EQ(0, sigaction(SIGPIPE, &oldact, NULL));
+}
+
+// Simple sanity check within a single process that receiving PIDs works.
+TEST(UnixDomainSocketTest, RecvPid) {
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ ScopedFD recv_sock(fds[0]);
+ ScopedFD send_sock(fds[1]);
+
+ ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ static const char kHello[] = "hello";
+ ASSERT_TRUE(UnixDomainSocket::SendMsg(
+ send_sock.get(), kHello, sizeof(kHello), std::vector<int>()));
+
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ ProcessId sender_pid;
+ ScopedVector<ScopedFD> fd_vec;
+ const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
+ recv_sock.get(), buf, sizeof(buf), &fd_vec, &sender_pid);
+ ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread));
+ ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ ASSERT_EQ(0U, fd_vec.size());
+
+ ASSERT_EQ(getpid(), sender_pid);
+}
+
+// Same as above, but send the max number of file descriptors too.
+TEST(UnixDomainSocketTest, RecvPidWithMaxDescriptors) {
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ ScopedFD recv_sock(fds[0]);
+ ScopedFD send_sock(fds[1]);
+
+ ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ static const char kHello[] = "hello";
+ std::vector<int> send_fds(UnixDomainSocket::kMaxFileDescriptors,
+ send_sock.get());
+ ASSERT_TRUE(UnixDomainSocket::SendMsg(
+ send_sock.get(), kHello, sizeof(kHello), send_fds));
+
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ ProcessId sender_pid;
+ ScopedVector<ScopedFD> recv_fds;
+ const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
+ recv_sock.get(), buf, sizeof(buf), &recv_fds, &sender_pid);
+ ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread));
+ ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ ASSERT_EQ(UnixDomainSocket::kMaxFileDescriptors, recv_fds.size());
+
+ ASSERT_EQ(getpid(), sender_pid);
+}
+
+// Check that RecvMsgWithPid doesn't DCHECK fail when reading EOF from a
+// disconnected socket.
+TEST(UnixDomianSocketTest, RecvPidDisconnectedSocket) {
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ ScopedFD recv_sock(fds[0]);
+ ScopedFD send_sock(fds[1]);
+
+ ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ send_sock.reset();
+
+ char ch;
+ ProcessId sender_pid;
+ ScopedVector<ScopedFD> recv_fds;
+ const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
+ recv_sock.get(), &ch, sizeof(ch), &recv_fds, &sender_pid);
+ ASSERT_EQ(0, nread);
+ ASSERT_EQ(-1, sender_pid);
+ ASSERT_EQ(0U, recv_fds.size());
+}
+
+} // namespace
+
+} // namespace base