summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gyp9
-rw-r--r--base/command_line.cc6
-rw-r--r--base/command_line.h3
-rw-r--r--base/file_util_posix.cc16
-rw-r--r--base/global_descriptors_posix.cc46
-rw-r--r--base/global_descriptors_posix.h66
-rw-r--r--base/logging.cc18
-rw-r--r--base/process_util.h8
-rw-r--r--base/process_util_linux.cc17
-rw-r--r--base/reserved_file_descriptors.h36
-rw-r--r--base/unix_domain_socket_posix.cc98
-rw-r--r--base/unix_domain_socket_posix.h22
-rw-r--r--base/zygote_manager.cc832
-rw-r--r--base/zygote_manager.h143
-rw-r--r--chrome/app/breakpad_linux.cc8
-rw-r--r--chrome/app/chrome_dll_main.cc39
-rw-r--r--chrome/app/chrome_main_uitest.cc5
-rw-r--r--chrome/browser/plugin_process_host.cc12
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc75
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.h3
-rw-r--r--chrome/browser/zygote_host_linux.cc90
-rw-r--r--chrome/browser/zygote_host_linux.h41
-rw-r--r--chrome/browser/zygote_main_linux.cc168
-rw-r--r--chrome/chrome.gyp2
-rw-r--r--chrome/common/chrome_descriptors.h15
-rw-r--r--chrome/common/chrome_switches.cc6
-rw-r--r--chrome/common/chrome_switches.h3
-rw-r--r--chrome/common/ipc_channel.h7
-rw-r--r--chrome/common/ipc_channel_posix.cc25
-rw-r--r--chrome/common/ipc_channel_posix.h2
-rw-r--r--chrome/common/ipc_channel_proxy.cc5
-rw-r--r--chrome/common/ipc_channel_proxy.h2
-rw-r--r--chrome/common/ipc_tests.cc21
-rw-r--r--chrome/common/process_watcher_posix.cc15
-rw-r--r--chrome/plugin/plugin_main.cc3
-rw-r--r--chrome/renderer/renderer_main_unittest.cc8
-rw-r--r--chrome/test/automation/automation_proxy.cc8
-rw-r--r--chrome/test/chrome_process_util.cc76
38 files changed, 1219 insertions, 740 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 96deb53..5d09098 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -133,8 +133,6 @@
'fix_wp64.h',
'float_util.h',
'foundation_utils_mac.h',
- 'global_descriptors_posix.h',
- 'global_descriptors_posix.cc',
'hash_tables.h',
'histogram.cc',
'histogram.h',
@@ -326,7 +324,6 @@
'tracked_objects.cc',
'tracked_objects.h',
'tuple.h',
- 'unix_domain_socket_posix.cc',
'values.cc',
'values.h',
'version.cc',
@@ -421,6 +418,9 @@
# so use idle_timer_none.cc instead.
'idle_timer.cc',
],
+ 'sources': [
+ 'zygote_manager.cc',
+ ],
'dependencies': [
'../build/util/build_util.gyp:lastchange',
'../build/linux/system.gyp:gtk',
@@ -685,6 +685,9 @@
],
'conditions': [
['OS == "linux"', {
+ 'sources': [
+ 'zygote_manager_unittest.cc',
+ ],
'sources!': [
'file_version_info_unittest.cc',
# Linux has an implementation of idle_timer, but it's unclear
diff --git a/base/command_line.cc b/base/command_line.cc
index 582c8b8..735620d 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -168,12 +168,6 @@ bool CommandLine::IsSwitch(const StringType& parameter_string,
}
// static
-void CommandLine::Reset() {
- delete current_process_commandline_;
- current_process_commandline_ = NULL;
-}
-
-// static
void CommandLine::Init(int argc, const char* const* argv) {
#if defined(OS_WIN)
current_process_commandline_ = new CommandLine;
diff --git a/base/command_line.h b/base/command_line.h
index d128d49..c895b81 100644
--- a/base/command_line.h
+++ b/base/command_line.h
@@ -45,9 +45,6 @@ class CommandLine {
// TODO(port): should be a FilePath.
explicit CommandLine(const std::wstring& program);
- // Uninit and free the current process's command line.
- static void Reset();
-
// Initialize the current process CommandLine singleton. On Windows,
// ignores its arguments (we instead parse GetCommandLineW()
// directly) because we don't trust the CRT's parsing of the command
diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc
index aca0ff9..92b9d00 100644
--- a/base/file_util_posix.cc
+++ b/base/file_util_posix.cc
@@ -27,6 +27,7 @@
#include "base/logging.h"
#include "base/string_util.h"
#include "base/time.h"
+#include "base/zygote_manager.h"
namespace file_util {
@@ -645,8 +646,19 @@ MemoryMappedFile::MemoryMappedFile()
}
bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
- file_ = open(file_name.value().c_str(), O_RDONLY);
-
+ file_ = -1;
+#if defined(OS_LINUX)
+ base::ZygoteManager* zm = base::ZygoteManager::Get();
+ if (zm) {
+ file_ = zm->OpenFile(file_name.value().c_str());
+ if (file_ == -1) {
+ LOG(INFO) << "Zygote manager can't open " << file_name.value()
+ << ", retrying locally. (OK at start of ui_tests.)";
+ }
+ }
+#endif // defined(OS_LINUX)
+ if (file_ == -1)
+ file_ = open(file_name.value().c_str(), O_RDONLY);
if (file_ == -1) {
LOG(ERROR) << "Couldn't open " << file_name.value();
return false;
diff --git a/base/global_descriptors_posix.cc b/base/global_descriptors_posix.cc
deleted file mode 100644
index 869d196..0000000
--- a/base/global_descriptors_posix.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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/global_descriptors_posix.h"
-
-#include <vector>
-#include <utility>
-
-#include "base/logging.h"
-
-namespace base {
-
-int GlobalDescriptors::MaybeGet(Key key) const {
- for (Mapping::const_iterator
- i = descriptors_.begin(); i != descriptors_.end(); ++i) {
- if (i->first == key)
- return i->second;
- }
-
- // In order to make unittests pass, we define a default mapping from keys to
- // descriptors by adding a fixed offset:
- return kBaseDescriptor + key;
-}
-
-int GlobalDescriptors::Get(Key key) const {
- const int ret = MaybeGet(key);
-
- if (ret == -1)
- LOG(FATAL) << "Unknown global descriptor: " << key;
- return ret;
-}
-
-void GlobalDescriptors::Set(Key key, int fd) {
- for (Mapping::iterator
- i = descriptors_.begin(); i != descriptors_.end(); ++i) {
- if (i->first == key) {
- i->second = fd;
- return;
- }
- }
-
- descriptors_.push_back(std::make_pair(key, fd));
-}
-
-} // namespace base
diff --git a/base/global_descriptors_posix.h b/base/global_descriptors_posix.h
deleted file mode 100644
index f606a82..0000000
--- a/base/global_descriptors_posix.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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_GLOBAL_DESCRIPTORS_POSIX_H_
-#define BASE_GLOBAL_DESCRIPTORS_POSIX_H_
-
-#include "build/build_config.h"
-
-#include <vector>
-#include <utility>
-
-#include <stdint.h>
-
-#include "base/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 GlobalDescriptors {
- public:
- typedef uint32_t Key;
- // Often we want a canonical descriptor for a given Key. In this case, we add
- // the following constant to the key value:
- static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken.
-
- // 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 give a key. Returns -1 on error.
- int MaybeGet(Key key) const;
-
- typedef std::vector<std::pair<Key, int> > Mapping;
-
- // Set the descriptor for the given key.
- void Set(Key key, int fd);
-
- void Reset(const Mapping& mapping) {
- descriptors_ = mapping;
- }
-
- private:
- GlobalDescriptors() { }
- friend struct DefaultSingletonTraits<GlobalDescriptors>;
-
- Mapping descriptors_;
-};
-
-} // namespace base
-
-#endif // BASE_GLOBAL_DESCRIPTORS_POSIX_H_
diff --git a/base/logging.cc b/base/logging.cc
index b5af671..77e9583 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -19,9 +19,12 @@ typedef HANDLE MutexHandle;
#endif
#if defined(OS_POSIX)
+#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
#define MAX_PATH PATH_MAX
typedef FILE* FileHandle;
@@ -37,6 +40,7 @@ typedef pthread_mutex_t* MutexHandle;
#include "base/command_line.h"
#include "base/debug_util.h"
#include "base/lock_impl.h"
+#include "base/reserved_file_descriptors.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
@@ -204,6 +208,20 @@ bool InitializeLogFileHandle() {
}
}
SetFilePointer(log_file, 0, 0, FILE_END);
+#elif defined(OS_LINUX)
+ // Reserve global fd slots.
+ int reserved_fds[kReservedFds];
+ for (int i=0; i < kReservedFds; i++)
+ reserved_fds[i] = open("/dev/null", O_RDONLY, 0);
+
+ log_file = fopen(log_file_name->c_str(), "a");
+
+ // Release the reserved fds.
+ for (int i=0; i < kReservedFds; i++)
+ close(reserved_fds[i]);
+
+ if (log_file == NULL)
+ return false;
#elif defined(OS_POSIX)
log_file = fopen(log_file_name->c_str(), "a");
if (log_file == NULL)
diff --git a/base/process_util.h b/base/process_util.h
index 41d0470..4fe081f 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -141,6 +141,14 @@ typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle);
+
+#if defined(OS_LINUX)
+// Like LaunchApp, but if zygote manager is enabled,
+// forks the zygote instead of forking and exec'ing.
+bool ForkApp(const std::vector<std::string>& argv,
+ const file_handle_mapping_vector& fds_to_remap,
+ ProcessHandle* process_handle);
+#endif
#endif
// Executes the application specified by cl. This function delegates to one
diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc
index 7560257..ba2d8cd 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -17,6 +17,7 @@
#include "base/logging.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
+#include "base/zygote_manager.h"
namespace {
@@ -85,6 +86,22 @@ FilePath GetProcessExecutablePath(ProcessHandle process) {
return FilePath(std::string(exename, len));
}
+bool ForkApp(const std::vector<std::string>& argv,
+ const file_handle_mapping_vector& fds_to_remap,
+ ProcessHandle* process_handle) {
+ ZygoteManager* zm = ZygoteManager::Get();
+ if (!zm)
+ return LaunchApp(argv, fds_to_remap, false, process_handle);
+
+ pid_t pid = zm->LongFork(argv, fds_to_remap);
+ if (pid < 0)
+ return false;
+
+ if (process_handle)
+ *process_handle = pid;
+ return true;
+}
+
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle) {
diff --git a/base/reserved_file_descriptors.h b/base/reserved_file_descriptors.h
new file mode 100644
index 0000000..5236a08
--- /dev/null
+++ b/base/reserved_file_descriptors.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef BASE_RESERVED_FILE_DESCRIPTORS_H_
+#define BASE_RESERVED_FILE_DESCRIPTORS_H_
+
+#if defined(OS_POSIX)
+
+// Chrome uses predefined file descriptors to communicate with child processes.
+// Normally this is a private contract between code that does fork/exec and the
+// code it invokes, but in zygote mode, things get a little more interesting.
+// It's a huge layering violation for this to be in base, but
+// logging and ZygoteManager need kReservedFileDescriptors, so there.
+
+enum GlobalReservedFds {
+ // Classic unix file descriptors.
+ // Let's leave them alone even if we don't use them.
+ kStdinFd = 0,
+ kStdoutFd = 1,
+ kStderrFd = 2,
+
+ // See chrome/common/ipc_channel_posix.cc
+ kClientChannelFd = 3,
+
+ // See chrome/app/breakpad_linux.cc and
+ // chrome/browser/renderer_host/browser_render_process_host.cc
+ kMagicCrashSignalFd = 4,
+
+ // One plus highest fd mentioned in this enum.
+ kReservedFds = 5
+};
+
+#endif
+
+#endif
diff --git a/base/unix_domain_socket_posix.cc b/base/unix_domain_socket_posix.cc
deleted file mode 100644
index 8d53d2a..0000000
--- a/base/unix_domain_socket_posix.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// 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/unix_domain_socket_posix.h"
-
-#include <unistd.h>
-#include <sys/uio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include "base/eintr_wrapper.h"
-#include "base/logging.h"
-
-namespace base {
-
-bool SendMsg(int fd, const void* buf, size_t length, std::vector<int>& fds) {
- struct msghdr msg;
- memset(&msg, 0, sizeof(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];
- if (!control_buffer)
- return false;
-
- 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;
- }
-
- const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, 0));
- const bool ret = static_cast<ssize_t>(length) == r;
- delete[] control_buffer;
- return ret;
-}
-
-ssize_t RecvMsg(int fd, void* buf, size_t length, std::vector<int>* fds) {
- static const unsigned kMaxDescriptors = 16;
-
- fds->clear();
-
- struct msghdr msg;
- memset(&msg, 0, sizeof(msg));
- struct iovec iov = {buf, length};
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- char control_buffer[CMSG_SPACE(sizeof(int) * kMaxDescriptors)];
- msg.msg_control = control_buffer;
- msg.msg_controllen = sizeof(control_buffer);
-
- const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, 0));
- if (r == -1)
- return -1;
-
- int* wire_fds = NULL;
- unsigned wire_fds_len = 0;
-
- if (msg.msg_controllen > 0) {
- struct cmsghdr* cmsg;
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS) {
- const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
- DCHECK(payload_len % sizeof(int) == 0);
- wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
- wire_fds_len = payload_len / sizeof(int);
- break;
- }
- }
- }
-
- 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;
- }
-
- fds->resize(wire_fds_len);
- memcpy(&(*fds)[0], wire_fds, sizeof(int) * wire_fds_len);
-
- return r;
-}
-
-} // namespace base
diff --git a/base/unix_domain_socket_posix.h b/base/unix_domain_socket_posix.h
deleted file mode 100644
index 46c473a..0000000
--- a/base/unix_domain_socket_posix.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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_UNIX_DOMAIN_SOCKET_POSIX_H_
-#define BASE_UNIX_DOMAIN_SOCKET_POSIX_H_
-
-#include <vector>
-
-namespace base {
-
-// Use sendmsg to write the given msg and include a vector
-// of file descriptors. Returns true iff successful.
-bool SendMsg(int fd, const void* msg, size_t length,
- 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, 16 descriptors.
-ssize_t RecvMsg(int fd, void* msg, size_t length, std::vector<int>* fds);
-
-} // namespace base
-
-#endif // BASE_UNIX_DOMAIN_SOCKET_POSIX_H_
diff --git a/base/zygote_manager.cc b/base/zygote_manager.cc
new file mode 100644
index 0000000..7b2c026
--- /dev/null
+++ b/base/zygote_manager.cc
@@ -0,0 +1,832 @@
+// 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.
+
+#include "base/zygote_manager.h"
+
+#if defined(OS_LINUX)
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <sys/file.h> // for flock()
+#include <sys/stat.h>
+#include <sys/uio.h> // for struct iovec
+#include <sys/wait.h>
+#include <unistd.h> // for ssize_t
+
+#include <string>
+
+#include "base/eintr_wrapper.h"
+#include "base/file_descriptor_shuffle.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/reserved_file_descriptors.h"
+#include "base/singleton.h"
+
+using file_util::Delete;
+
+// See comment below, where sigaction is called.
+static void SIGCHLDHandler(int signal) {
+}
+
+namespace base {
+
+const char ZygoteManager::kZMagic[] = "zygo";
+
+ZygoteManager::~ZygoteManager() {
+ if (server_fd_ != -1) {
+ close(server_fd_);
+ server_fd_ = -1;
+ }
+ if (client_fd_ != -1) {
+ close(client_fd_);
+ client_fd_ = -1;
+ }
+ if (lockfd_ != -1) {
+ close(lockfd_);
+ lockfd_ = -1;
+ }
+ if (canary_fd_ != -1) {
+ // Wake up the poll() in ReadAndHandleMessage()
+ close(canary_fd_);
+ canary_fd_ = -1;
+ }
+#ifndef OFFICIAL_BUILD
+ // Closing the canary kills the server,
+ // so after this it's ok for e.g. unit tests
+ // to start a new zygote server.
+ (void) unsetenv("ZYGOTE_MANAGER_STARTED");
+#endif
+}
+
+// Runs in client process
+ZygoteManager* ZygoteManager::Get() {
+ static bool checked = false;
+ static bool enabled = false;
+ if (!checked) {
+ enabled = (getenv("ENABLE_ZYGOTE_MANAGER") != NULL);
+ checked = true;
+ }
+ if (!enabled)
+ return NULL;
+ return Singleton<ZygoteManager>::get();
+}
+
+// Runs in zygote manager process
+int ZygoteManager::UnpickleHeader(const Pickle& reply, void** iter) {
+ std::string magic;
+ if (!reply.ReadString(iter, &magic) || magic != std::string(kZMagic)) {
+ LOG(ERROR) << "reply didn't start with " << kZMagic;
+ return ZMBAD;
+ }
+ pid_t clientpid = (pid_t) -1;
+ if (!reply.ReadInt(iter, &clientpid)) {
+ LOG(ERROR) << "Can't read client pid";
+ return ZMBAD;
+ }
+ if (clientpid != getpid()) {
+ LOG(ERROR) << "got client pid " << clientpid << ", expected " << getpid();
+ DCHECK(clientpid == getpid());
+ return ZMBAD;
+ }
+ int kind;
+ if (!reply.ReadInt(iter, &kind)) {
+ LOG(ERROR) << "can't read kind";
+ return ZMBAD;
+ }
+ return kind;
+}
+
+// Runs in client process (only used in unit test)
+bool ZygoteManager::Ping(base::TimeDelta* delta) {
+ if (client_fd_ == -1)
+ return false;
+
+ Pickle pickle;
+ pickle.WriteString(kZMagic);
+ pickle.WriteInt(getpid());
+ pickle.WriteInt(ZMPING);
+
+ int bytes_sent;
+ int bytes_read = -1;
+
+ TimeTicks time_sent = TimeTicks::HighResNow();
+
+ // Lock fork server, send the pickle, wait for the reply, unlock
+ if (flock(lockfd_, LOCK_EX))
+ LOG(ERROR) << "flock failed, errno " << errno;
+ bytes_sent = HANDLE_EINTR(write(client_fd_,
+ const_cast<void *>(pickle.data()), pickle.size()));
+ if (bytes_sent > 0) {
+ bytes_read = HANDLE_EINTR(read(client_fd_, msg_buf_, kMAX_MSG_LEN));
+ }
+ if (flock(lockfd_, LOCK_UN))
+ LOG(ERROR) << "flock failed, errno " << errno;
+
+ TimeTicks time_received = TimeTicks::HighResNow();
+
+ if (bytes_sent < 1) {
+ LOG(ERROR) << "Can't send to zm, errno " << errno;
+ return false;
+ }
+ if (bytes_read < 1) {
+ LOG(ERROR) << "Can't get from zm, errno " << errno;
+ return false;
+ }
+
+ // Unpickle the reply
+ Pickle reply(msg_buf_, bytes_read);
+ void* iter = NULL;
+ int kind = UnpickleHeader(reply, &iter);
+ if (kind != ZMPINGED) {
+ LOG(ERROR) << "reply wrong kind " << kind;
+ return false;
+ }
+ *delta = TimeTicks::HighResNow() - time_sent;
+ LOG(INFO) << "Round trip time in microseconds: " << delta->InMicroseconds();
+ return true;
+}
+
+// Runs in zygote manager process
+void ZygoteManager::PingHandler(const Pickle& request, void* iter,
+ Pickle* reply, std::vector<std::string>** newargv) {
+ reply->WriteInt(ZMPINGED);
+}
+
+// Runs in browser process, called only by base::ForkApp()
+pid_t ZygoteManager::LongFork(const std::vector<std::string>& argv,
+ const file_handle_mapping_vector& fds_to_remap) {
+ if (client_fd_ == -1)
+ return -1;
+
+ Pickle pickle;
+
+ // Encode the arguments and the desired remote fd numbers in the pickle,
+ // and the fds in a separate buffer
+ pickle.WriteString(kZMagic);
+ pickle.WriteInt(getpid());
+ pickle.WriteInt(ZMFORK);
+ pickle.WriteInt(argv.size());
+ std::vector<std::string>::const_iterator argi;
+ for (argi = argv.begin(); argi != argv.end(); ++argi)
+ pickle.WriteString(*argi);
+ pickle.WriteInt(fds_to_remap.size());
+
+ // Wrap the pickle and the fds together in a msghdr
+ ::msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ struct iovec iov;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_buf_;
+ msg.msg_controllen = CMSG_LEN(sizeof(int) * fds_to_remap.size());
+ struct cmsghdr* cmsg;
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = msg.msg_controllen;
+ int* wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ for (size_t i = 0; i < fds_to_remap.size(); i++) {
+ pickle.WriteInt(fds_to_remap[i].second);
+ wire_fds[i] = fds_to_remap[i].first;
+ }
+ iov.iov_base = const_cast<void *>(pickle.data());
+ iov.iov_len = pickle.size();
+
+ int bytes_sent;
+ int bytes_read = -1;
+
+ // Lock fork server, send the pickle, wait for the reply, unlock
+ if (flock(lockfd_, LOCK_EX))
+ LOG(ERROR) << "flock failed, errno " << errno;
+ bytes_sent = HANDLE_EINTR(sendmsg(client_fd_, &msg, MSG_WAITALL));
+ if (bytes_sent > 0) {
+ bytes_read = HANDLE_EINTR(read(client_fd_, msg_buf_, kMAX_MSG_LEN));
+ }
+ if (flock(lockfd_, LOCK_UN))
+ LOG(ERROR) << "flock failed, errno " << errno;
+
+ if (bytes_sent < 1) {
+ LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_;
+ return (pid_t) -1;
+ }
+ if (bytes_read < 1) {
+ LOG(ERROR) << "Can't get from zm, errno " << errno;
+ return (pid_t) -1;
+ }
+
+ // Unpickle the reply
+ Pickle reply(msg_buf_, bytes_read);
+ void* iter = NULL;
+ int kind = UnpickleHeader(reply, &iter);
+ if (kind != ZMFORKED) {
+ LOG(ERROR) << "reply wrong kind " << kind;
+ return (pid_t) -1;
+ }
+ pid_t newpid = (pid_t) -1;
+ int pid_errno;
+ if (!reply.ReadInt(&iter, &newpid) || !reply.ReadInt(&iter, &pid_errno)) {
+ LOG(ERROR) << "fork failed, can't read pid/errno";
+ return (pid_t) -1;
+ }
+ if ((newpid == (pid_t) -1) || pid_errno != 0) {
+ LOG(ERROR) << "fork failed, pid " << newpid << ", errno " << pid_errno;
+ return (pid_t) -1;
+ }
+ return newpid;
+}
+
+// Runs in zygote manager process
+bool ZygoteManager::LongForkHandler(const Pickle& request, void* iter,
+ Pickle* reply, std::vector<std::string>** newargv,
+ const int wire_fds[], int num_wire_fds) {
+ file_handle_mapping_vector fds_to_remap;
+ pid_t childpid;
+
+ reply->WriteInt(ZMFORKED);
+
+ // Unpickle commandline for new child
+ std::vector<std::string>* argv = new std::vector<std::string>;
+ int argc;
+ request.ReadInt(&iter, &argc);
+ for (int i = 0; i < argc; i++) {
+ std::string arg;
+ if (!request.ReadString(&iter, &arg)) {
+ LOG(ERROR) << "can't read arg?";
+ goto error_reply;
+ }
+ argv->push_back(arg);
+ }
+ // Unpickle file descriptor map for new child
+ int numfds;
+ request.ReadInt(&iter, &numfds);
+ DCHECK(numfds == num_wire_fds);
+ if (numfds != num_wire_fds) {
+ LOG(ERROR) << "numfds " << numfds << " != num_wire_fds " << num_wire_fds;
+ goto error_reply;
+ }
+ for (int i = 0; i < numfds; i++) {
+ int fd;
+ if (!request.ReadInt(&iter, &fd)) {
+ LOG(ERROR) << "can't read fd?";
+ goto error_reply;
+ }
+ fds_to_remap.push_back(std::pair<int, int>(wire_fds[i], fd));
+ }
+
+ // Mitosis!
+ childpid = fork();
+
+ if (childpid != 0) {
+ // parent
+ // first off, close our copy of the child's file descriptors
+ for (file_handle_mapping_vector::const_iterator
+ it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
+ close(it->first);
+ }
+
+ // Finish formatting the reply
+ reply->WriteInt(childpid);
+ if (childpid == (pid_t) -1) {
+ reply->WriteInt(errno);
+ return false;
+ } else {
+ reply->WriteInt(0);
+ }
+ } else {
+ // child
+ // Apply file descriptor map
+ 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));
+ }
+
+ // Avoid closing descriptor children will need to contact fork server.
+ fd_shuffle.push_back(InjectionArc(client_fd_, client_fd_, false));
+ // Avoid closing log descriptor we're using
+ int logfd = logging::GetLoggingFileDescriptor();
+ if (logfd != -1)
+ fd_shuffle.push_back(InjectionArc(logfd, logfd, false));
+ // And of course avoid closing the cached fds.
+ std::map<std::string, int>::iterator i;
+ for (i = cached_fds_.begin(); i != cached_fds_.end(); ++i) {
+ fd_shuffle.push_back(InjectionArc(i->second, i->second, false));
+ }
+
+ // If there is any clash in the mapping, this function will DCHECK.
+ if (!ShuffleFileDescriptors(fd_shuffle))
+ exit(127);
+
+ // Open this after shuffle to avoid using reserved slots.
+ lockfd_ = open(lockfile_.c_str(), O_RDWR, 0);
+ if (lockfd_ == -1) {
+ // TODO(dkegel): real error handling
+ exit(126);
+ }
+ // Mark it as not to be closed.
+ fd_shuffle.push_back(InjectionArc(lockfd_, lockfd_, false));
+
+ // Also closes reserved fds.
+ CloseSuperfluousFds(fd_shuffle);
+
+ *newargv = argv;
+ // Because *newargv is set, we will return to main instead of looping
+ }
+ return true;
+
+ error_reply:
+ reply->WriteInt(-1);
+ reply->WriteInt(-1);
+ for (int i=0; i<num_wire_fds; i++)
+ close(wire_fds[i]);
+ return false;
+}
+
+// Runs in browser process, called by ProcessWatcher::EnsureProcessTerminated().
+void ZygoteManager::EnsureProcessTerminated(pid_t childpid) {
+ if (client_fd_ == -1)
+ return;
+
+ Pickle pickle;
+
+ pickle.WriteString(kZMagic);
+ pickle.WriteInt(getpid());
+ pickle.WriteInt(ZMREAP);
+ pickle.WriteInt(childpid);
+
+ int bytes_sent = HANDLE_EINTR(
+ write(client_fd_, const_cast<void*>(pickle.data()), pickle.size()));
+
+ if (bytes_sent < 1) {
+ LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_;
+ }
+}
+
+// Runs in zygote manager process
+void ZygoteManager::EnsureProcessTerminatedHandler(const Pickle& request,
+ void* iter) {
+ pid_t childpid;
+ request.ReadInt(&iter, &childpid);
+ NOTIMPLEMENTED();
+ // TODO(dkegel): put childpid on a watch list, and terminate it
+ // after a while as chrome/common/process_watcher does.
+}
+
+static bool ValidateFilename(const std::string& filename) {
+ // We only have to open one kind of file, but we don't know
+ // the directory it's in, so be as restrictive as we can within
+ // those bounds.
+
+ static const char* allowed_prefix = "/";
+ if (filename.compare(0, strlen(allowed_prefix), allowed_prefix) != 0) {
+ LOG(ERROR) << "filename did not start with " << allowed_prefix;
+ return false;
+ }
+ static const char* allowed_suffix = ".pak";
+ if ((filename.length() <= strlen(allowed_suffix)) ||
+ (filename.compare(filename.length() - strlen(allowed_suffix),
+ strlen(allowed_suffix), allowed_suffix) != 0)) {
+ LOG(ERROR) << "filename did not end in " << allowed_suffix;
+ return false;
+ }
+ if (filename.find("../") != std::string::npos) {
+ LOG(ERROR) << "filename contained relative component";
+ return false;
+ }
+ static const char* forbidden_prefixes[] = {
+ "/var/", "/tmp/", "/etc/", "/dev/", "/proc/", 0 };
+ for (const char** p = forbidden_prefixes;
+ *p; p++) {
+ if (filename.compare(0, strlen(*p), *p) == 0) {
+ LOG(ERROR) << "filename began with " << *p;
+ return false;
+ }
+ }
+ return true;
+}
+
+// Runs in browser process
+int ZygoteManager::OpenFile(const std::string& filename) {
+ // For security reasons, we only support .pak files,
+ // and only in certain locations.
+ if (!ValidateFilename(filename)) {
+ LOG(INFO) << "ZygoteManager: filename " << filename << " disallowed.";
+ return -1;
+ }
+
+ std::map<std::string, int>::iterator mapiter = cached_fds_.find(filename);
+ if (mapiter != cached_fds_.end())
+ return mapiter->second;
+
+ if (client_fd_ == -1)
+ return -1;
+
+ Pickle pickle;
+
+ pickle.WriteString(kZMagic);
+ pickle.WriteInt(getpid());
+ pickle.WriteInt(ZMOPEN);
+ pickle.WriteString(filename);
+
+ // Get ready to receive fds
+ ::msghdr msg = {0};
+ struct iovec iov = {msg_buf_, kMAX_MSG_LEN};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_buf_;
+ msg.msg_controllen = kMAX_CMSG_LEN;
+
+ ssize_t bytes_sent;
+ ssize_t bytes_read = 0;
+
+ if (flock(lockfd_, LOCK_EX))
+ LOG(ERROR) << "flock failed, errno " << errno;
+ bytes_sent = HANDLE_EINTR(
+ write(client_fd_, const_cast<void *>(pickle.data()), pickle.size()));
+ if (bytes_sent > 0) {
+ bytes_read = HANDLE_EINTR(recvmsg(client_fd_, &msg, MSG_WAITALL));
+ }
+ if (flock(lockfd_, LOCK_UN))
+ LOG(ERROR) << "flock failed, errno " << errno;
+
+ if (bytes_sent < 1) {
+ LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_;
+ return -1;
+ }
+ if (bytes_read < 1) {
+ LOG(ERROR) << "Can't get from zm, errno " << errno;
+ return -1;
+ }
+
+ // Locate the sole block of sent file descriptors within the list of
+ // control messages
+ const int* wire_fds = NULL;
+ unsigned num_wire_fds = 0;
+ if (msg.msg_controllen > 0) {
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ assert(payload_len % sizeof(int) == 0);
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ num_wire_fds = payload_len / sizeof(int);
+ break;
+ }
+ }
+ }
+ DCHECK(!(msg.msg_flags & MSG_CTRUNC));
+
+ // Unpickle the reply
+ Pickle reply(msg_buf_, bytes_read);
+ void* iter = NULL;
+ int kind = UnpickleHeader(reply, &iter);
+ if (kind != ZMOPENED) {
+ LOG(ERROR) << "reply wrong kind " << kind;
+ goto error_close;
+ }
+ int newfd_errno;
+ if (!reply.ReadInt(&iter, &newfd_errno)) {
+ LOG(ERROR) << "open failed, can't read errno";
+ goto error_close;
+ }
+ if (newfd_errno != 0) {
+ LOG(ERROR) << "open failed, errno " << newfd_errno;
+ goto error_close;
+ }
+ if (num_wire_fds != 1) {
+ LOG(ERROR) << "open failed, reply wrong number fds " << num_wire_fds;
+ goto error_close;
+ }
+ if (wire_fds[0] == -1) {
+ LOG(ERROR) << "open failed, fd -1";
+ NOTREACHED();
+ return -1;
+ }
+ return wire_fds[0];
+
+ error_close:
+ for (unsigned i=0; i<num_wire_fds; i++)
+ close(wire_fds[i]);
+ return -1;
+}
+
+// Runs in zygote manager process
+bool ZygoteManager::OpenFileHandler(const Pickle& request, void* iter,
+ Pickle* reply, ::msghdr* msg) {
+ reply->WriteInt(ZMOPENED);
+
+ std::string filename;
+ if (!request.ReadString(&iter, &filename)) {
+ LOG(ERROR) << "no filename?";
+ return false;
+ }
+ if (!ValidateFilename(filename)) {
+ // Fake a unix error code
+ reply->WriteInt(EPERM);
+ return false;
+ }
+
+ std::map<std::string, int>::iterator i;
+ int newfd;
+ std::map<std::string, int>::iterator mapiter = cached_fds_.find(filename);
+ if (mapiter == cached_fds_.end()) {
+ // Verify that file is a plain file
+ struct stat statbuf;
+ if (lstat(filename.c_str(), &statbuf) != 0) {
+ LOG(ERROR) << "can't stat " << filename << ", errno " << errno;
+ return false;
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ LOG(ERROR) << "not regular file " << filename;
+ // Fake a unix error code
+ reply->WriteInt(EISDIR);
+ return false;
+ }
+ newfd = open(filename.c_str(), O_RDONLY, 0);
+ if (newfd != -1) {
+ cached_fds_[filename] = newfd;
+ } else {
+ LOG(ERROR) << "can't open " << filename << ", errno " << errno;
+ }
+ } else {
+ newfd = mapiter->second;
+ }
+ if (newfd == -1) {
+ reply->WriteInt(errno);
+ } else {
+ reply->WriteInt(0);
+ msg->msg_control = cmsg_buf_;
+ msg->msg_controllen = CMSG_LEN(sizeof(int));
+ struct cmsghdr* cmsg;
+ cmsg = CMSG_FIRSTHDR(msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = msg->msg_controllen;
+ int* wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ wire_fds[0] = newfd;
+ }
+
+ return true;
+}
+
+// Runs in zygote manager process
+bool ZygoteManager::ReadAndHandleMessage(std::vector<std::string>** newargv) {
+ // Wait for activity either on canary fd or main fd.
+ struct pollfd watcher[2];
+ memset(watcher, 0, sizeof(watcher));
+ watcher[0].fd = canary_fd_;
+ watcher[0].events = POLLIN|POLLHUP;
+ watcher[1].fd = server_fd_;
+ watcher[1].events = POLLIN;
+ // Wait at most one minute. This lets us detect case where
+ // canary socket is closed abruptly because the main client aborted.
+ // Also lets us reap dead children once a minute even if we don't get SIGCHLD.
+ // We'd like to wait less time, but that's hard on battery life.
+ // Note: handle EINTR manually here, not with wrapper, as we need
+ // to return when we're interrupted so caller can reap promptly.
+ int nactive = poll(watcher, 2, 60*1000);
+
+ if (nactive == -1) {
+ if (errno == EINTR) {
+ // Probably SIGCHLD. Return to main loop so it can reap.
+ return true;
+ }
+ LOG(ERROR) << "poll failed, errno " << errno << ", aborting";
+ return false;
+ }
+
+ // If it was the canary, exit
+ if (watcher[0].revents != 0) {
+ LOG(INFO) << "notified of peer destruction, exiting";
+ return false;
+ }
+ if ((watcher[1].revents & POLLIN) != POLLIN) {
+ // spurious wakeup?
+ return true;
+ }
+
+ ssize_t bytes_read = 0;
+ struct msghdr msg = {0};
+ struct iovec iov = {msg_buf_, kMAX_MSG_LEN};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_buf_;
+ msg.msg_controllen = kMAX_CMSG_LEN;
+ bytes_read = HANDLE_EINTR(recvmsg(server_fd_, &msg, MSG_WAITALL));
+ if (bytes_read == 0) {
+ LOG(ERROR) << "got EOF, aborting";
+ return false;
+ }
+ if (bytes_read == -1) {
+ LOG(ERROR) << "got errno " << errno << ", aborting";
+ return false;
+ }
+
+ // Locate the sole block of sent file descriptors within the list of
+ // control messages
+ const int* wire_fds = NULL;
+ unsigned num_wire_fds = 0;
+ if (msg.msg_controllen > 0) {
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ assert(payload_len % sizeof(int) == 0);
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ num_wire_fds = payload_len / sizeof(int);
+ break;
+ }
+ }
+ }
+ DCHECK(!(msg.msg_flags & MSG_CTRUNC));
+
+ // Unpickle/parse message
+ Pickle pickle(msg_buf_, bytes_read);
+ void* iter = NULL;
+ std::string magic;
+ if (!pickle.ReadString(&iter, &magic) || magic != std::string(kZMagic)) {
+ LOG(ERROR) << "msg didn't start with " << kZMagic << ", got " << magic;
+ for (unsigned i=0; i<num_wire_fds; i++)
+ close(wire_fds[i]);
+ return true;
+ }
+ pid_t clientpid = (pid_t) -1;
+ pickle.ReadInt(&iter, &clientpid);
+ int kind;
+ pickle.ReadInt(&iter, &kind);
+
+ Pickle reply;
+ reply.WriteString(kZMagic);
+ reply.WriteInt(clientpid);
+
+ struct msghdr replymsg = {0};
+ memset(&replymsg, 0, sizeof(replymsg));
+
+ switch (kind) {
+ case ZMPING:
+ DCHECK_EQ(0U, num_wire_fds);
+ PingHandler(pickle, iter, &reply, newargv);
+ break;
+ case ZMFORK:
+ // TODO(dkegel): real error handling
+ (void) LongForkHandler(pickle, iter, &reply, newargv, wire_fds,
+ num_wire_fds);
+ if (*newargv != NULL) {
+ // Child. Just return to caller, who will return from SetLongFork.
+ return true;
+ }
+ break;
+ case ZMREAP:
+ DCHECK_EQ(0U, num_wire_fds);
+ EnsureProcessTerminatedHandler(pickle, iter);
+ // no reply to this message
+ return true;
+ case ZMOPEN:
+ DCHECK_EQ(0U, num_wire_fds);
+ // TODO(dkegel): real error handling
+ (void) OpenFileHandler(pickle, iter, &reply, &replymsg);
+ break;
+ default:
+ // TODO(dkegel): real error handling
+ LOG(ERROR) << "Unknown message kind " << kind;
+ DCHECK_EQ(0U, num_wire_fds);
+ break;
+ }
+
+ struct iovec riov = {const_cast<void *>(reply.data()), reply.size()};
+ replymsg.msg_iov = &riov;
+ replymsg.msg_iovlen = 1;
+
+ int bytes_sent;
+ bytes_sent = HANDLE_EINTR(sendmsg(server_fd_, &replymsg, MSG_WAITALL));
+ if (bytes_sent != static_cast<int>(riov.iov_len)) {
+ // TODO(dkegel): real error handling
+ LOG(ERROR) << "Can't send reply.";
+ return false;
+ }
+ return true;
+}
+
+// Called only by ChromeMain(), forks the zygote manager process.
+std::vector<std::string>* ZygoteManager::Start() {
+ DCHECK(lockfd_ == -1);
+ DCHECK(canary_fd_ == -1);
+ DCHECK(server_fd_ == -1);
+ DCHECK(client_fd_ == -1);
+
+#ifndef OFFICIAL_BUILD
+ // Disallow nested ZygoteManager servers
+ CHECK(getenv("ZYGOTE_MANAGER_STARTED") == NULL) << "already started?!";
+ (void) setenv("ZYGOTE_MANAGER_STARTED", "1", 1);
+#endif
+
+ int pipe_fds[2];
+
+ // Avoid using the reserved fd slots.
+ int reserved_fds[kReservedFds];
+ for (int i=0; i < kReservedFds; i++)
+ reserved_fds[i] = open("/dev/null", O_RDONLY, 0);
+
+ // Create the main communications pipe.
+ int err = HANDLE_EINTR(socketpair(AF_UNIX, SOCK_DGRAM, 0, pipe_fds));
+ if (err != 0) {
+ // TODO(dkegel): real error handling
+ exit(99);
+ }
+ server_fd_ = pipe_fds[1];
+ client_fd_ = pipe_fds[0];
+
+ // Create the pipe used only to relay destruction event server.
+ // Must be SOCK_STREAM so close() is sensed by poll().
+ err = HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds));
+ if (err != 0) {
+ // TODO(dkegel): real error handling
+ exit(99);
+ }
+
+ // Create lock file.
+ // TODO(dkegel): get rid of this
+ char lockfile[256];
+ strcpy(lockfile, "/tmp/zygote_manager_lock.XXXXXX");
+ lockfd_ = mkstemp(lockfile);
+ if (lockfd_ == -1) {
+ // TODO(dkegel): real error handling
+ exit(99);
+ }
+ lockfile_.assign(lockfile);
+
+ // Fork a fork server.
+ pid_t childpid = fork();
+
+ if (!childpid) {
+ for (int i=0; i < kReservedFds; i++)
+ close(reserved_fds[i]);
+
+ // Original child. Continues on with the main program
+ // and becomes the first client.
+ close(server_fd_);
+ server_fd_ = -1;
+
+ close(pipe_fds[1]);
+ canary_fd_ = pipe_fds[0];
+
+ // Return now to indicate this is the original process.
+ return NULL;
+ } else {
+ close(lockfd_);
+
+ close(pipe_fds[0]);
+ canary_fd_ = pipe_fds[1];
+
+ // We need to accept SIGCHLD, even though our handler is a no-op because
+ // otherwise we cannot wait on children. (According to POSIX 2001.)
+ // (And otherwise poll() might not wake up on SIGCHLD.)
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIGCHLDHandler;
+ CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
+
+ // Original process. Acts as the server.
+ while (true) {
+ std::vector<std::string>* newargv = NULL;
+ if (!ReadAndHandleMessage(&newargv))
+ break;
+ if (newargv) {
+ // Return new commandline to show caller this is a new child process.
+ return newargv;
+ }
+ // Server process continues around loop.
+
+ // Reap children.
+ while (true) {
+ int status = -1;
+ pid_t reaped = waitpid(-1, &status, WNOHANG);
+ if (reaped != -1 && reaped != 0) {
+ LOG(INFO) << "Reaped pid " << reaped;
+ continue;
+ }
+ break;
+ }
+ }
+ // Server cleanup after EOF or error reading from the socket.
+ Delete(FilePath(lockfile_), false);
+ // TODO(dkegel): real error handling
+ LOG(INFO) << "exiting. " << cached_fds_.size() << " cached fds.";
+ std::map<std::string, int>::iterator i;
+ for (i = cached_fds_.begin(); i != cached_fds_.end(); ++i) {
+ LOG(INFO) << "Closing fd " << i->second << " filename " << i->first;
+ close(i->second);
+ }
+ exit(0);
+ }
+}
+}
+#endif // defined(OS_LINUX)
diff --git a/base/zygote_manager.h b/base/zygote_manager.h
new file mode 100644
index 0000000..5b94533
--- /dev/null
+++ b/base/zygote_manager.h
@@ -0,0 +1,143 @@
+// 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_ZYGOTE_MANAGER_H_
+#define BASE_ZYGOTE_MANAGER_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_LINUX)
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/pickle.h"
+#include "base/time.h"
+#include "base/process_util.h" // for file_handle_mapping_vector
+
+namespace base {
+
+class ZygoteManager {
+ public:
+ // The normal way to get a ZygoteManager is via this singleton factory.
+ static ZygoteManager* Get();
+
+ ZygoteManager() :
+ server_fd_(-1), client_fd_(-1), canary_fd_(-1), lockfd_(-1) {
+ }
+
+ ~ZygoteManager();
+
+ // Measure round trip time. Return true on success.
+ // Only used during testing.
+ bool Ping(base::TimeDelta* delta);
+
+ // Start the zygote manager.
+ // Called only once, but returns many times.
+ // Returns once in original process and once in each spawned child.
+ // In original process, returns NULL.
+ // In child processes, returns the argv to use for the child.
+ // In Chromium, called from ChromeMain().
+ std::vector<std::string>* Start();
+
+ // Like longjmp() and base::LaunchApp().
+ // Ask the fork server to spawn a new process with
+ // the given commandline and the given file descriptors.
+ // Returns process id of copy, or -1 on failure.
+ // In Chromium, called from base::ForkApp(), which is
+ // called from BrowserRenderProcessHost::Init().
+ pid_t LongFork(const std::vector<std::string>& argv,
+ const file_handle_mapping_vector& fds_to_remap);
+
+ // Tell ZygoteManager that we expect the given process to
+ // exit on its own soon. If it doesn't die within a few
+ // seconds, kill it. Does not block (unless pipe to server full).
+ // In Chromium, called from ProcessWatcher::EnsureProcessTerminated().
+ void EnsureProcessTerminated(pid_t childpid);
+
+ // Open a file, or retrieve a previously cached file descriptor
+ // for this file. The files are opened for readonly access.
+ // Caution: do not seek file descriptors returned
+ // by this API, as all children share the same file objects, so
+ // a seek on one is a seek on all.
+ // Works even if the file is unlinked after the first call
+ // (e.g. when an app is updated by the linux system autoupdater).
+ // Returns file descriptor, or -1 for error.
+ // In Chromium, called from MemoryMappedFile::MapFileToMemory().
+ // Only allows openeing files named .pak in reasonable looking locations.
+ int OpenFile(const std::string& filename);
+
+ private:
+ int UnpickleHeader(const Pickle& reply, void** iter);
+
+ // Returns false on EOF
+ // Sets *newargv to a new commandline if the remote side requested a fork.
+ bool ReadAndHandleMessage(std::vector<std::string>** newargv);
+
+ void PingHandler(const Pickle& request, void* iter, Pickle* reply,
+ std::vector<std::string>** newargv);
+
+ bool LongForkHandler(const Pickle& request, void* iter, Pickle* reply,
+ std::vector<std::string>** newargv,
+ const int wire_fds[], int num_wire_fds);
+
+ void EnsureProcessTerminatedHandler(const Pickle& request, void* iter);
+
+ bool OpenFileHandler(const Pickle& request, void* iter, Pickle* reply,
+ ::msghdr* msg);
+
+ // The fd used by the server to receive requests
+ int server_fd_;
+
+ // The fd used by the clients to send requests
+ int client_fd_;
+
+ // fd used only to notify server of destruction.
+ int canary_fd_;
+
+ // Temporary file used only for locking.
+ // Each client must do its own open for locking to work;
+ // inherited file descriptors can't lock each other out.
+ // FIXME: locking is lame.
+ std::string lockfile_;
+ int lockfd_;
+
+ enum message_kind_t { ZMPING, ZMPINGED,
+ ZMFORK, ZMFORKED,
+ ZMREAP,
+ ZMOPEN, ZMOPENED,
+ ZMBAD };
+
+ // See common/reserved_file_descriptors.h for who uses the reserved
+ // file descriptors. kReservedFds is one plus the highest fd mentioned there.
+ // TODO(dkegel): move kReservedFds to reserved_file_descriptors.h
+ static const int kReservedFds = 5;
+
+ static const int kMAX_MSG_LEN = 2000;
+ static const int kMAX_CMSG_LEN = 100;
+
+ static const char kZMagic[];
+
+ char msg_buf_[kMAX_MSG_LEN];
+ char cmsg_buf_[kMAX_CMSG_LEN];
+
+ // Where we remember file descriptors for already-opened files.
+ // Both client and server maintain this table.
+ // Client should check the table before requesting the
+ // server to open a file, as it might have been already
+ // opened before this client was forked.
+ std::map<std::string, int> cached_fds_;
+};
+
+} // namespace base
+
+#endif // defined(OS_LINUX)
+
+#endif // BASE_ZYGOTE_MANAGER_H_
diff --git a/chrome/app/breakpad_linux.cc b/chrome/app/breakpad_linux.cc
index ef3dfa2..0ee0bed 100644
--- a/chrome/app/breakpad_linux.cc
+++ b/chrome/app/breakpad_linux.cc
@@ -14,13 +14,13 @@
#include "base/file_version_info_linux.h"
#include "base/path_service.h"
#include "base/rand_util.h"
+#include "base/reserved_file_descriptors.h"
#include "breakpad/linux/directory_reader.h"
#include "breakpad/linux/exception_handler.h"
#include "breakpad/linux/linux_libc_support.h"
#include "breakpad/linux/linux_syscall_support.h"
#include "breakpad/linux/memory.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/installer/util/google_update_settings.h"
static const char kUploadURL[] =
@@ -501,11 +501,13 @@ RendererCrashHandler(const void* crash_context, size_t crash_context_size,
}
void EnableRendererCrashDumping() {
- const int fd = Singleton<base::GlobalDescriptors>()->Get(kCrashDumpSignal);
+ // When the browser forks off our process, it installs the crash signal file
+ // descriptor in slot kMagicCrashSignalFd.
+
// We deliberately leak this object.
google_breakpad::ExceptionHandler* handler =
new google_breakpad::ExceptionHandler("" /* unused */, NULL, NULL,
- (void*) fd, true);
+ (void*) kMagicCrashSignalFd, true);
handler->set_crash_handler(RendererCrashHandler);
}
diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc
index 33e774c..b6ba22f 100644
--- a/chrome/app/chrome_dll_main.cc
+++ b/chrome/app/chrome_dll_main.cc
@@ -34,9 +34,6 @@
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/debug_util.h"
-#if defined(OS_POSIX)
-#include "base/global_descriptors_posix.h"
-#endif
#include "base/icu_util.h"
#include "base/message_loop.h"
#include "base/path_service.h"
@@ -48,6 +45,9 @@
#if defined(OS_WIN)
#include "base/win_util.h"
#endif
+#if defined(OS_LINUX)
+#include "base/zygote_manager.h"
+#endif
#if defined(OS_MACOSX)
#include "chrome/app/breakpad_mac.h"
#elif defined(OS_LINUX)
@@ -57,7 +57,6 @@
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_counters.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/logging_chrome.h"
@@ -76,7 +75,6 @@ extern int RendererMain(const MainFunctionParams&);
extern int PluginMain(const MainFunctionParams&);
extern int WorkerMain(const MainFunctionParams&);
extern int UtilityMain(const MainFunctionParams&);
-extern int ZygoteMain(const MainFunctionParams&);
#if defined(OS_WIN)
// TODO(erikkay): isn't this already defined somewhere?
@@ -294,19 +292,21 @@ int ChromeMain(int argc, const char** argv) {
// its main event loop to get rid of the cruft.
base::ScopedNSAutoreleasePool autorelease_pool;
-#if defined(OS_POSIX)
- base::GlobalDescriptors* g_fds = Singleton<base::GlobalDescriptors>::get();
- g_fds->Set(kPrimaryIPCChannel,
- kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
-#if defined(OS_LINUX)
- g_fds->Set(kCrashDumpSignal,
- kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor);
-#endif
-#endif
-
// Initialize the command line.
#if defined(OS_WIN)
CommandLine::Init(0, NULL);
+#elif defined(OS_LINUX)
+ base::ZygoteManager* zm = base::ZygoteManager::Get();
+ std::vector<std::string>* zargv = NULL;
+ if (zm)
+ zargv = zm->Start();
+ if (zargv) {
+ // Forked child.
+ CommandLine::Init(*zargv);
+ } else {
+ // Original process.
+ CommandLine::Init(argc, argv);
+ }
#else
CommandLine::Init(argc, argv);
#endif
@@ -339,13 +339,11 @@ int ChromeMain(int argc, const char** argv) {
CHECK(sigaction(SIGPIPE, &action, 0) == 0);
#endif // OS_POSIX
} else {
-#if defined(OS_WIN)
std::wstring channel_name =
parsed_command_line.GetSwitchValue(switches::kProcessChannelID);
browser_pid = StringToInt(WideToASCII(channel_name));
DCHECK(browser_pid != 0);
-#endif
#if defined(OS_POSIX)
// When you hit Ctrl-C in a terminal running the browser
@@ -491,13 +489,6 @@ int ChromeMain(int argc, const char** argv) {
#else
NOTIMPLEMENTED();
#endif
- } else if (process_type == switches::kZygoteProcess) {
-#if defined(OS_LINUX)
- if (ZygoteMain(main_params))
- RendererMain(main_params);
-#else
- NOTIMPLEMENTED();
-#endif
} else if (process_type.empty()) {
#if defined(OS_LINUX)
// Glib type system initialization. Needed at least for gconf,
diff --git a/chrome/app/chrome_main_uitest.cc b/chrome/app/chrome_main_uitest.cc
index f229751..5ce05c2 100644
--- a/chrome/app/chrome_main_uitest.cc
+++ b/chrome/app/chrome_main_uitest.cc
@@ -18,14 +18,9 @@ TEST_F(ChromeMainTest, AppLaunch) {
if (UITest::in_process_renderer()) {
EXPECT_EQ(1, UITest::GetBrowserProcessCount());
} else {
-#if defined(OS_LINUX)
- // On Linux we'll have three processes: browser, renderer and zygote.
- EXPECT_EQ(3, UITest::GetBrowserProcessCount());
-#else
// We should have two instances of the browser process alive -
// one is the Browser and the other is the Renderer.
EXPECT_EQ(2, UITest::GetBrowserProcessCount());
-#endif
}
}
diff --git a/chrome/browser/plugin_process_host.cc b/chrome/browser/plugin_process_host.cc
index 5216755..921f860 100644
--- a/chrome/browser/plugin_process_host.cc
+++ b/chrome/browser/plugin_process_host.cc
@@ -14,9 +14,6 @@
#include "app/app_switches.h"
#include "base/command_line.h"
-#if defined(OS_POSIX)
-#include "base/global_descriptors_posix.h"
-#endif
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
@@ -33,7 +30,6 @@
#include "chrome/browser/renderer_host/browser_render_process_host.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_plugin_lib.h"
#include "chrome/common/chrome_switches.h"
@@ -397,10 +393,10 @@ bool PluginProcessHost::Init(const WebPluginInfo& info,
// This code is duplicated with browser_render_process_host.cc, but
// there's not a good place to de-duplicate it.
base::file_handle_mapping_vector fds_to_map;
- const int ipcfd = channel().GetClientFileDescriptor();
- if (ipcfd > -1)
- fds_to_map.push_back(std::pair<int, int>(
- ipcfd, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
+ int src_fd = -1, dest_fd = -1;
+ channel().GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1)
+ fds_to_map.push_back(std::pair<int, int>(src_fd, dest_fd));
base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
#endif
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 29e7ef0..829ef47 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -22,6 +22,7 @@
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/rand_util.h"
+#include "base/reserved_file_descriptors.h"
#include "base/scoped_ptr.h"
#include "base/shared_memory.h"
#include "base/singleton.h"
@@ -34,6 +35,9 @@
#include "chrome/browser/history/history.h"
#include "chrome/browser/plugin_service.h"
#include "chrome/browser/profile.h"
+#if defined(OS_LINUX)
+#include "chrome/browser/renderer_host/render_crash_handler_host_linux.h"
+#endif
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_widget_helper.h"
#include "chrome/browser/renderer_host/render_widget_host.h"
@@ -41,7 +45,6 @@
#include "chrome/browser/renderer_host/web_cache_manager.h"
#include "chrome/browser/visitedlink_master.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/common/child_process_info.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/common/notification_service.h"
@@ -51,11 +54,6 @@
#include "chrome/renderer/render_process.h"
#include "grit/generated_resources.h"
-#if defined(OS_LINUX)
-#include "chrome/browser/zygote_host_linux.h"
-#include "chrome/browser/renderer_host/render_crash_handler_host_linux.h"
-#endif
-
using WebKit::WebCache;
#if defined(OS_WIN)
@@ -136,8 +134,7 @@ BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile)
backgrounded_(true),
ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_(
base::TimeDelta::FromSeconds(5),
- this, &BrowserRenderProcessHost::ClearTransportDIBCache)),
- zygote_child_(false) {
+ this, &BrowserRenderProcessHost::ClearTransportDIBCache)) {
widget_helper_ = new RenderWidgetHelper();
registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
@@ -173,13 +170,7 @@ BrowserRenderProcessHost::~BrowserRenderProcessHost() {
audio_renderer_host_->Destroy();
if (process_.handle() && !run_renderer_in_process()) {
- if (zygote_child_) {
-#if defined(OS_LINUX)
- Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle());
-#endif
- } else {
- ProcessWatcher::EnsureProcessTerminated(process_.handle());
- }
+ ProcessWatcher::EnsureProcessTerminated(process_.handle());
}
ClearTransportDIBCache();
@@ -303,9 +294,7 @@ bool BrowserRenderProcessHost::Init() {
ASCIIToWide(field_trial->MakePersistentString()));
#if defined(OS_POSIX)
- const bool has_cmd_prefix =
- browser_command_line.HasSwitch(switches::kRendererCmdPrefix);
- if (has_cmd_prefix) {
+ if (browser_command_line.HasSwitch(switches::kRendererCmdPrefix)) {
// launch the renderer child with some prefix (usually "gdb --args")
const std::wstring prefix =
browser_command_line.GetSwitchValue(switches::kRendererCmdPrefix);
@@ -345,42 +334,24 @@ bool BrowserRenderProcessHost::Init() {
base::ProcessHandle process = 0;
#if defined(OS_WIN)
process = sandbox::StartProcess(&cmd_line);
-#elif defined(OS_POSIX)
-#if defined(OS_LINUX)
- if (!has_cmd_prefix) {
- base::GlobalDescriptors::Mapping mapping;
- const int ipcfd = channel_->GetClientFileDescriptor();
- mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd));
- const int crash_signal_fd =
- Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
- if (crash_signal_fd >= 0) {
- mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal,
- crash_signal_fd));
- }
- process = Singleton<ZygoteHost>()->ForkRenderer(cmd_line.argv(), mapping);
- zygote_child_ = true;
- } else {
-#endif
- // NOTE: This code is duplicated with plugin_process_host.cc, but
- // there's not a good place to de-duplicate it.
- base::file_handle_mapping_vector fds_to_map;
- const int ipcfd = channel_->GetClientFileDescriptor();
- fds_to_map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3));
-#if defined(OS_LINUX)
- const int crash_signal_fd =
- Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
- if (crash_signal_fd >= 0) {
- fds_to_map.push_back(std::make_pair(crash_signal_fd,
- kCrashDumpSignal + 3));
- }
-#endif
- base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
- zygote_child_ = false;
+#else
+ // NOTE: This code is duplicated with plugin_process_host.cc, but
+ // there's not a good place to de-duplicate it.
+ base::file_handle_mapping_vector fds_to_map;
+ int src_fd = -1, dest_fd = -1;
+ channel_->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1)
+ fds_to_map.push_back(std::pair<int, int>(src_fd, dest_fd));
#if defined(OS_LINUX)
- }
+ const int crash_signal_fd =
+ Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
+ if (crash_signal_fd >= 0)
+ fds_to_map.push_back(std::make_pair(crash_signal_fd, kMagicCrashSignalFd));
+ base::ForkApp(cmd_line.argv(), fds_to_map, &process);
+#else
+ base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
#endif
#endif
-
if (!process) {
channel_.reset();
return false;
@@ -695,7 +666,7 @@ void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) {
const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
if (cmd_line.HasSwitch(switches::kRendererCmdPrefix))
return;
- CHECK(peer_pid == process_.pid()) << peer_pid << " " << process_.pid();
+ CHECK(peer_pid == process_.pid());
}
}
}
diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h
index 6c259f0..b380211 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.h
+++ b/chrome/browser/renderer_host/browser_render_process_host.h
@@ -154,9 +154,6 @@ class BrowserRenderProcessHost : public RenderProcessHost,
// Used in single-process mode.
scoped_ptr<RendererMainThread> in_process_renderer_;
- // True iff the renderer is a child of a zygote process.
- bool zygote_child_;
-
DISALLOW_COPY_AND_ASSIGN(BrowserRenderProcessHost);
};
diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc
deleted file mode 100644
index f56e6e93..0000000
--- a/chrome/browser/zygote_host_linux.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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 "chrome/browser/zygote_host_linux.h"
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include "base/command_line.h"
-#include "base/eintr_wrapper.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-#include "base/pickle.h"
-#include "base/process_util.h"
-#include "base/unix_domain_socket_posix.h"
-
-#include "chrome/common/chrome_switches.h"
-
-ZygoteHost::ZygoteHost() {
- std::wstring chrome_path;
- CHECK(PathService::Get(base::FILE_EXE, &chrome_path));
- CommandLine cmd_line(chrome_path);
-
- cmd_line.AppendSwitchWithValue(switches::kProcessType,
- switches::kZygoteProcess);
-
- int fds[2];
- CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
- base::file_handle_mapping_vector fds_to_map;
- fds_to_map.push_back(std::make_pair(fds[1], 3));
-
- const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
- if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) {
- const std::wstring prefix =
- browser_command_line.GetSwitchValue(switches::kZygoteCmdPrefix);
- cmd_line.PrependWrapper(prefix);
- }
-
- base::ProcessHandle process;
- base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
- CHECK(process != -1) << "Failed to launch zygote process";
-
- close(fds[1]);
- control_fd_ = fds[0];
-}
-
-ZygoteHost::~ZygoteHost() {
- close(control_fd_);
-}
-
-pid_t ZygoteHost::ForkRenderer(
- const std::vector<std::string>& argv,
- const base::GlobalDescriptors::Mapping& mapping) {
- Pickle pickle;
-
- pickle.WriteInt(kCmdFork);
- pickle.WriteInt(argv.size());
- for (std::vector<std::string>::const_iterator
- i = argv.begin(); i != argv.end(); ++i)
- pickle.WriteString(*i);
-
- pickle.WriteInt(mapping.size());
-
- std::vector<int> fds;
- for (base::GlobalDescriptors::Mapping::const_iterator
- i = mapping.begin(); i != mapping.end(); ++i) {
- pickle.WriteUInt32(i->first);
- fds.push_back(i->second);
- }
-
- if (!base::SendMsg(control_fd_, pickle.data(), pickle.size(), fds))
- return -1;
-
- pid_t pid;
- if (HANDLE_EINTR(read(control_fd_, &pid, sizeof(pid))) != sizeof(pid))
- return -1;
-
- return pid;
-}
-
-void ZygoteHost::EnsureProcessTerminated(pid_t process) {
- Pickle pickle;
-
- pickle.WriteInt(kCmdReap);
- pickle.WriteInt(process);
-
- HANDLE_EINTR(write(control_fd_, pickle.data(), pickle.size()));
-}
diff --git a/chrome/browser/zygote_host_linux.h b/chrome/browser/zygote_host_linux.h
deleted file mode 100644
index 279918d..0000000
--- a/chrome/browser/zygote_host_linux.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
-#define CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
-
-#include <string>
-#include <vector>
-
-#include "base/global_descriptors_posix.h"
-#include "base/singleton.h"
-
-// http://code.google.com/p/chromium/wiki/LinuxZygote
-
-// The zygote host is the interface, in the browser process, to the zygote
-// process.
-class ZygoteHost {
- public:
- ~ZygoteHost();
-
- pid_t ForkRenderer(const std::vector<std::string>& command_line,
- const base::GlobalDescriptors::Mapping& mapping);
- void EnsureProcessTerminated(pid_t process);
-
- // These are the command codes used on the wire between the browser and the
- // zygote.
- enum {
- kCmdFork = 0, // Fork off a new renderer.
- kCmdReap = 1, // Reap a renderer child.
- };
-
- private:
- friend struct DefaultSingletonTraits<ZygoteHost>;
- ZygoteHost();
- void LaunchZygoteProcess();
-
- int control_fd_; // the socket to the zygote
-};
-
-#endif // CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc
deleted file mode 100644
index d450c39..0000000
--- a/chrome/browser/zygote_main_linux.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// 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 <unistd.h>
-#include <sys/epoll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/signal.h>
-
-#include "base/command_line.h"
-#include "base/eintr_wrapper.h"
-#include "base/global_descriptors_posix.h"
-#include "base/pickle.h"
-#include "base/unix_domain_socket_posix.h"
-
-#include "chrome/browser/zygote_host_linux.h"
-#include "chrome/common/chrome_descriptors.h"
-#include "chrome/common/main_function_params.h"
-#include "chrome/common/process_watcher.h"
-
-// http://code.google.com/p/chromium/wiki/LinuxZygote
-
-// This is the object which implements the zygote. The ZygoteMain function,
-// which is called from ChromeMain, at the the bottom and simple constructs one
-// of these objects and runs it.
-class Zygote {
- public:
- bool ProcessRequests() {
- // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
- // browser on it.
-
- // We need to accept SIGCHLD, even though our handler is a no-op because
- // otherwise we cannot wait on children. (According to POSIX 2001.)
- struct sigaction action;
- memset(&action, 0, sizeof(action));
- action.sa_handler = SIGCHLDHandler;
- CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
-
- for (;;) {
- if (HandleRequestFromBrowser(3))
- return true;
- }
- }
-
- private:
- // See comment below, where sigaction is called.
- static void SIGCHLDHandler(int signal) { }
-
- // ---------------------------------------------------------------------------
- // Requests from the browser...
-
- // Read and process a request from the browser. Returns true if we are in a
- // new process and thus need to unwind back into ChromeMain.
- bool HandleRequestFromBrowser(int fd) {
- std::vector<int> fds;
- static const unsigned kMaxMessageLength = 2048;
- char buf[kMaxMessageLength];
- const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds);
- if (len == -1) {
- LOG(WARNING) << "Error reading message from browser: " << errno;
- return false;
- }
-
- if (len == 0) {
- // EOF from the browser. We should die.
- _exit(0);
- return false;
- }
-
- Pickle pickle(buf, len);
- void* iter = NULL;
-
- int kind;
- if (!pickle.ReadInt(&iter, &kind))
- goto error;
-
- if (kind == ZygoteHost::kCmdFork) {
- return HandleForkRequest(fd, pickle, iter, fds);
- } else if (kind == ZygoteHost::kCmdReap) {
- if (fds.size())
- goto error;
- return HandleReapRequest(fd, pickle, iter);
- }
-
- error:
- LOG(WARNING) << "Error parsing message from browser";
- for (std::vector<int>::const_iterator
- i = fds.begin(); i != fds.end(); ++i)
- close(*i);
- return false;
- }
-
- bool HandleReapRequest(int fd, Pickle& pickle, void* iter) {
- pid_t child;
-
- if (!pickle.ReadInt(&iter, &child)) {
- LOG(WARNING) << "Error parsing reap request from browser";
- return false;
- }
-
- ProcessWatcher::EnsureProcessTerminated(child);
-
- return false;
- }
-
- // Handle a 'fork' request from the browser: this means that the browser
- // wishes to start a new renderer.
- bool HandleForkRequest(int fd, Pickle& pickle, void* iter,
- std::vector<int>& fds) {
- std::vector<std::string> args;
- int argc, numfds;
- base::GlobalDescriptors::Mapping mapping;
- pid_t child;
-
- if (!pickle.ReadInt(&iter, &argc))
- goto error;
-
- for (int i = 0; i < argc; ++i) {
- std::string arg;
- if (!pickle.ReadString(&iter, &arg))
- goto error;
- args.push_back(arg);
- }
-
- if (!pickle.ReadInt(&iter, &numfds))
- goto error;
- if (numfds != static_cast<int>(fds.size()))
- goto error;
-
- for (int i = 0; i < numfds; ++i) {
- base::GlobalDescriptors::Key key;
- if (!pickle.ReadUInt32(&iter, &key))
- goto error;
- mapping.push_back(std::make_pair(key, fds[i]));
- }
-
- child = fork();
-
- if (!child) {
- close(3); // our socket from the browser is in fd 3
- Singleton<base::GlobalDescriptors>()->Reset(mapping);
- CommandLine::Reset();
- CommandLine::Init(args);
- return true;
- }
-
- for (std::vector<int>::const_iterator
- i = fds.begin(); i != fds.end(); ++i)
- close(*i);
-
- HANDLE_EINTR(write(fd, &child, sizeof(child)));
- return false;
-
- error:
- LOG(WARNING) << "Error parsing fork request from browser";
- for (std::vector<int>::const_iterator
- i = fds.begin(); i != fds.end(); ++i)
- close(*i);
- return false;
- }
- // ---------------------------------------------------------------------------
-};
-
-bool ZygoteMain(const MainFunctionParams& params) {
- Zygote zygote;
- return zygote.ProcessRequests();
-}
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index c1aefab..d9ea3ea 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1613,8 +1613,6 @@
'browser/worker_host/worker_process_host.h',
'browser/worker_host/worker_service.cc',
'browser/worker_host/worker_service.h',
- 'browser/zygote_host_linux.cc',
- 'browser/zygote_main_linux.cc',
'tools/build/win/precompiled_wtl.h',
'tools/build/win/precompiled_wtl.cc',
diff --git a/chrome/common/chrome_descriptors.h b/chrome/common/chrome_descriptors.h
deleted file mode 100644
index 3d6be45..0000000
--- a/chrome/common/chrome_descriptors.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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 CHROME_COMMON_CHROME_DESCRIPTORS_H_
-#define CHROME_COMMON_CHROME_DESCRIPTORS_H_
-
-// This is a list of global descriptor keys to be used with the
-// base::GlobalDescriptors object (see base/global_descriptors_posix.h)
-enum {
- kPrimaryIPCChannel = 0,
- kCrashDumpSignal = 1,
-};
-
-#endif // CHROME_COMMON_CHROME_DESCRIPTORS_H_
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index e5bce97..48511d3 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -63,9 +63,6 @@ const wchar_t kHomePage[] = L"homepage";
// Causes the process to run as renderer instead of as browser.
const wchar_t kRendererProcess[] = L"renderer";
-// Causes the process to run as a renderer zygote.
-const wchar_t kZygoteProcess[] = L"zygote";
-
// Path to the exe to run for the renderer and plugin subprocesses.
const wchar_t kBrowserSubprocessPath[] = L"browser-subprocess-path";
@@ -493,9 +490,6 @@ const wchar_t kNoDefaultBrowserCheck[] = L"no-default-browser-check";
// Enables the benchmarking extensions.
const wchar_t kEnableBenchmarking[] = L"enable-benchmarking";
-// The prefix used when starting the zygote process. (i.e. 'gdb --args')
-const wchar_t kZygoteCmdPrefix[] = L"zygote-cmd-prefix";
-
// Enables using ThumbnailStore instead of ThumbnailDatabase for setting and
// getting thumbnails for the new tab page.
const wchar_t kThumbnailStore[] = L"thumbnail-store";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 33a0239..8259d72 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -26,7 +26,6 @@ extern const wchar_t kProcessChannelID[];
extern const wchar_t kTestingChannelID[];
extern const wchar_t kHomePage[];
extern const wchar_t kRendererProcess[];
-extern const wchar_t kZygoteProcess[];
extern const wchar_t kBrowserSubprocessPath[];
extern const wchar_t kPluginProcess[];
extern const wchar_t kWorkerProcess[];
@@ -186,8 +185,6 @@ extern const wchar_t kEnableBenchmarking[];
extern const wchar_t kNoDefaultBrowserCheck[];
-extern const wchar_t kZygoteCmdPrefix[];
-
extern const wchar_t kThumbnailStore[];
} // namespace switches
diff --git a/chrome/common/ipc_channel.h b/chrome/common/ipc_channel.h
index 85b35fa..f619508 100644
--- a/chrome/common/ipc_channel.h
+++ b/chrome/common/ipc_channel.h
@@ -87,13 +87,14 @@ class Channel : public Message::Sender {
#if defined(OS_POSIX)
// On POSIX an IPC::Channel wraps a socketpair(), this method returns the
- // FD # for the client end of the socket.
+ // FD # for the client end of the socket and the equivalent FD# to use for
+ // mapping it into the Child process.
// This method may only be called on the server side of a channel.
//
// If the kTestingChannelID flag is specified on the command line then
// a named FIFO is used as the channel transport mechanism rather than a
- // socketpair() in which case this method returns -1.
- int GetClientFileDescriptor() const;
+ // socketpair() in which case this method returns -1 for both parameters.
+ void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
#endif // defined(OS_POSIX)
private:
diff --git a/chrome/common/ipc_channel_posix.cc b/chrome/common/ipc_channel_posix.cc
index babc16c..52cad9d 100644
--- a/chrome/common/ipc_channel_posix.cc
+++ b/chrome/common/ipc_channel_posix.cc
@@ -17,16 +17,15 @@
#include "base/command_line.h"
#include "base/eintr_wrapper.h"
-#include "base/global_descriptors_posix.h"
#include "base/lock.h"
#include "base/logging.h"
#include "base/process_util.h"
+#include "base/reserved_file_descriptors.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/singleton.h"
#include "base/stats_counters.h"
#include "chrome/common/chrome_counters.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/file_descriptor_set_posix.h"
#include "chrome/common/ipc_logging.h"
@@ -41,7 +40,7 @@ namespace IPC {
//
// When creating a child subprocess, the parent side of the fork
// arranges it such that the initial control channel ends up on the
-// magic file descriptor kPrimaryIPCChannel in the child. Future
+// magic file descriptor kClientChannelFd in the child. Future
// connections (file descriptors) can then be passed via that
// connection via sendmsg().
@@ -51,7 +50,7 @@ namespace {
// The PipeMap class works around this quirk related to unit tests:
//
// When running as a server, we install the client socket in a
-// specific file descriptor number (@kPrimaryIPCChannel). However, we
+// specific file descriptor number (@kClientChannelFd). However, we
// also have to support the case where we are running unittests in the
// same process. (We do not support forking without execing.)
//
@@ -59,7 +58,7 @@ namespace {
// The IPC server object will install a mapping in PipeMap from the
// name which it was given to the client pipe. When forking the client, the
// GetClientFileDescriptorMapping will ensure that the socket is installed in
-// the magic slot (@kPrimaryIPCChannel). The client will search for the
+// the magic slot (@kClientChannelFd). The client will search for the
// mapping, but it won't find any since we are in a new process. Thus the
// magic fd number is returned. Once the client connects, the server will
// close its copy of the client socket and remove the mapping.
@@ -125,7 +124,10 @@ int ChannelNameToClientFD(const std::string& channel_id) {
// If we don't find an entry, we assume that the correct value has been
// inserted in the magic slot.
- return Singleton<base::GlobalDescriptors>()->Get(kPrimaryIPCChannel);
+ // kClientChannelFd is the file descriptor number that a client process
+ // expects to find its IPC socket; see reserved_file_descriptors.h.
+
+ return kClientChannelFd;
}
//------------------------------------------------------------------------------
@@ -669,8 +671,11 @@ bool Channel::ChannelImpl::Send(Message* message) {
return true;
}
-int Channel::ChannelImpl::GetClientFileDescriptor() const {
- return client_pipe_;
+void Channel::ChannelImpl::GetClientFileDescriptorMapping(int *src_fd,
+ int *dest_fd) const {
+ DCHECK(mode_ == MODE_SERVER);
+ *src_fd = client_pipe_;
+ *dest_fd = kClientChannelFd;
}
// Called by libevent when we can read from th pipe without blocking.
@@ -798,8 +803,8 @@ bool Channel::Send(Message* message) {
return channel_impl_->Send(message);
}
-int Channel::GetClientFileDescriptor() const {
- return channel_impl_->GetClientFileDescriptor();
+void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const {
+ return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
}
} // namespace IPC
diff --git a/chrome/common/ipc_channel_posix.h b/chrome/common/ipc_channel_posix.h
index ed3d727..414730a 100644
--- a/chrome/common/ipc_channel_posix.h
+++ b/chrome/common/ipc_channel_posix.h
@@ -29,7 +29,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
void Close();
void set_listener(Listener* listener) { listener_ = listener; }
bool Send(Message* message);
- int GetClientFileDescriptor() const;
+ void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
private:
bool CreatePipe(const std::string& channel_id, Mode mode);
diff --git a/chrome/common/ipc_channel_proxy.cc b/chrome/common/ipc_channel_proxy.cc
index c77ee7e..6e8919a3 100644
--- a/chrome/common/ipc_channel_proxy.cc
+++ b/chrome/common/ipc_channel_proxy.cc
@@ -289,10 +289,11 @@ void ChannelProxy::RemoveFilter(MessageFilter* filter) {
// See the TODO regarding lazy initialization of the channel in
// ChannelProxy::Init().
// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe.
-int ChannelProxy::GetClientFileDescriptor() const {
+void ChannelProxy::GetClientFileDescriptorMapping(int *src_fd,
+ int *dest_fd) const {
Channel *channel = context_.get()->channel_;
DCHECK(channel); // Channel must have been created first.
- return channel->GetClientFileDescriptor();
+ channel->GetClientFileDescriptorMapping(src_fd, dest_fd);
}
#endif
diff --git a/chrome/common/ipc_channel_proxy.h b/chrome/common/ipc_channel_proxy.h
index 8adcb5b..a395b1f 100644
--- a/chrome/common/ipc_channel_proxy.h
+++ b/chrome/common/ipc_channel_proxy.h
@@ -121,7 +121,7 @@ class ChannelProxy : public Message::Sender {
// Calls through to the underlying channel's methods.
// TODO(playmobil): For now this is only implemented in the case of
// create_pipe_now = true, we need to figure this out for the latter case.
- int GetClientFileDescriptor() const;
+ void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
#endif // defined(OS_POSIX)
protected:
diff --git a/chrome/common/ipc_tests.cc b/chrome/common/ipc_tests.cc
index c2e0baa..d347fb5 100644
--- a/chrome/common/ipc_tests.cc
+++ b/chrome/common/ipc_tests.cc
@@ -21,16 +21,11 @@
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/debug_on_start.h"
-#if defined(OS_POSIX)
-#include "base/at_exit.h"
-#include "base/global_descriptors_posix.h"
-#endif
#include "base/perftimer.h"
#include "base/perf_test_suite.h"
#include "base/test_suite.h"
#include "base/thread.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/common/ipc_channel.h"
#include "chrome/common/ipc_channel_proxy.h"
#include "chrome/common/ipc_message_utils.h"
@@ -91,9 +86,11 @@ base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
base::file_handle_mapping_vector fds_to_map;
- const int ipcfd = channel->GetClientFileDescriptor();
- if (ipcfd > -1) {
- fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
+ int src_fd;
+ int dest_fd;
+ channel->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
}
base::ProcessHandle ret = NULL;
@@ -261,9 +258,11 @@ TEST_F(IPCChannelTest, ChannelProxyTest) {
bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDebugChildren);
base::file_handle_mapping_vector fds_to_map;
- const int ipcfd = chan.GetClientFileDescriptor();
- if (ipcfd > -1) {
- fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
+ int src_fd;
+ int dest_fd;
+ chan.GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
}
base::ProcessHandle process_handle = MultiProcessTest::SpawnChild(
diff --git a/chrome/common/process_watcher_posix.cc b/chrome/common/process_watcher_posix.cc
index f1ae4f4..497b80b 100644
--- a/chrome/common/process_watcher_posix.cc
+++ b/chrome/common/process_watcher_posix.cc
@@ -11,6 +11,7 @@
#include "base/eintr_wrapper.h"
#include "base/platform_thread.h"
+#include "base/zygote_manager.h"
// Return true if the given child is dead. This will also reap the process.
// Doesn't block.
@@ -69,8 +70,20 @@ class BackgroundReaper : public PlatformThread::Delegate {
// static
void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
// If the child is already dead, then there's nothing to do
- if (IsChildDead(process))
+ const int result = HANDLE_EINTR(waitpid(process, NULL, WNOHANG));
+ if (result > 0)
return;
+ if (result == -1) {
+#if defined(OS_LINUX)
+ // If it wasn't our child, maybe it was the zygote manager's child
+ base::ZygoteManager* zm = base::ZygoteManager::Get();
+ if (zm) {
+ zm->EnsureProcessTerminated(process);
+ return;
+ }
+#endif // defined(OS_LINUX)
+ NOTREACHED();
+ }
BackgroundReaper* reaper = new BackgroundReaper(process);
PlatformThread::CreateNonJoinable(0, reaper);
diff --git a/chrome/plugin/plugin_main.cc b/chrome/plugin/plugin_main.cc
index fb8880f..1bbb624 100644
--- a/chrome/plugin/plugin_main.cc
+++ b/chrome/plugin/plugin_main.cc
@@ -22,9 +22,6 @@
#if defined(OS_WIN)
#include "chrome/test/injection_test_dll.h"
#include "sandbox/src/sandbox.h"
-#elif defined(OS_LINUX)
-#include "chrome/common/chrome_descriptors.h"
-#include "base/global_descriptors_posix.h"
#endif
// main() routine for running as the plugin process.
diff --git a/chrome/renderer/renderer_main_unittest.cc b/chrome/renderer/renderer_main_unittest.cc
index 605101e..18e67fa 100644
--- a/chrome/renderer/renderer_main_unittest.cc
+++ b/chrome/renderer/renderer_main_unittest.cc
@@ -53,9 +53,11 @@ void RendererMainTest::TearDown() {
ProcessHandle RendererMainTest::SpawnChild(const std::wstring &procname,
IPC::Channel *channel) {
base::file_handle_mapping_vector fds_to_map;
- const int ipcfd = channel->GetClientFileDescriptor();
- if (ipcfd > -1) {
- fds_to_map.push_back(std::pair<int,int>(ipcfd, 3));
+ int src_fd;
+ int dest_fd;
+ channel->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
}
return MultiProcessTest::SpawnChild(procname, fds_to_map, false);
diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc
index 4410e2e..e54f6a6 100644
--- a/chrome/test/automation/automation_proxy.cc
+++ b/chrome/test/automation/automation_proxy.cc
@@ -13,7 +13,6 @@
#include "base/process_util.h"
#include "base/ref_counted.h"
#include "base/waitable_event.h"
-#include "chrome/common/chrome_descriptors.h"
#include "chrome/test/automation/automation_constants.h"
#include "chrome/test/automation/automation_messages.h"
#include "chrome/test/automation/browser_proxy.h"
@@ -441,9 +440,10 @@ scoped_refptr<BrowserProxy> AutomationProxy::GetLastActiveBrowserWindow() {
#if defined(OS_POSIX)
base::file_handle_mapping_vector AutomationProxy::fds_to_map() const {
base::file_handle_mapping_vector map;
- const int ipcfd = channel_->GetClientFileDescriptor();
- if (ipcfd > -1)
- map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3));
+ int src_fd = -1, dest_fd = -1;
+ channel_->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1)
+ map.push_back(std::make_pair(src_fd, dest_fd));
return map;
}
#endif // defined(OS_POSIX)
diff --git a/chrome/test/chrome_process_util.cc b/chrome/test/chrome_process_util.cc
index 4a5f5bb..296b291 100644
--- a/chrome/test/chrome_process_util.cc
+++ b/chrome/test/chrome_process_util.cc
@@ -5,7 +5,6 @@
#include "chrome/test/chrome_process_util.h"
#include <vector>
-#include <set>
#include "base/process_util.h"
#include "base/time.h"
@@ -15,6 +14,26 @@
using base::Time;
using base::TimeDelta;
+namespace {
+
+class ChromeProcessFilter : public base::ProcessFilter {
+ public:
+ explicit ChromeProcessFilter(base::ProcessId browser_pid)
+ : browser_pid_(browser_pid) {}
+
+ virtual bool Includes(base::ProcessId pid, base::ProcessId parent_pid) const {
+ // Match browser process itself and its children.
+ return browser_pid_ == pid || browser_pid_ == parent_pid;
+ }
+
+ private:
+ base::ProcessId browser_pid_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeProcessFilter);
+};
+
+} // namespace
+
void TerminateAllChromeProcesses(const FilePath& data_dir) {
// Total time the function will wait for chrome processes
// to terminate after it told them to do so.
@@ -51,24 +70,6 @@ void TerminateAllChromeProcesses(const FilePath& data_dir) {
base::CloseProcessHandle(*it);
}
-class ChildProcessFilter : public base::ProcessFilter {
- public:
- explicit ChildProcessFilter(base::ProcessId parent_pid)
- : parent_pids_(&parent_pid, (&parent_pid) + 1) {}
-
- explicit ChildProcessFilter(std::vector<base::ProcessId> parent_pids)
- : parent_pids_(parent_pids.begin(), parent_pids.end()) {}
-
- virtual bool Includes(base::ProcessId pid, base::ProcessId parent_pid) const {
- return parent_pids_.find(parent_pid) != parent_pids_.end();
- }
-
- private:
- const std::set<base::ProcessId> parent_pids_;
-
- DISALLOW_COPY_AND_ASSIGN(ChildProcessFilter);
-};
-
ChromeProcessList GetRunningChromeProcesses(const FilePath& data_dir) {
ChromeProcessList result;
@@ -76,32 +77,37 @@ ChromeProcessList GetRunningChromeProcesses(const FilePath& data_dir) {
if (browser_pid < 0)
return result;
- ChildProcessFilter filter(browser_pid);
+ // Normally, the browser is the parent process for all the renderers
+ base::ProcessId parent_pid = browser_pid;
+
+#if defined(OS_LINUX)
+ // But if the browser's parent is the same executable as the browser,
+ // then it's the zygote manager, and it's the parent for all the renderers.
+ base::ProcessId manager_pid = base::GetParentProcessId(browser_pid);
+ FilePath selfPath = base::GetProcessExecutablePath(browser_pid);
+ FilePath managerPath = base::GetProcessExecutablePath(manager_pid);
+ if (!selfPath.empty() && !managerPath.empty() && selfPath == managerPath) {
+ LOG(INFO) << "Zygote manager in use.";
+ parent_pid = manager_pid;
+ }
+#endif
+
+ ChromeProcessFilter filter(parent_pid);
base::NamedProcessIterator it(chrome::kBrowserProcessExecutableName, &filter);
const ProcessEntry* process_entry;
while ((process_entry = it.NextProcessEntry())) {
#if defined(OS_WIN)
result.push_back(process_entry->th32ProcessID);
+#elif defined(OS_LINUX)
+ // Don't count the zygote manager, that screws up unit tests,
+ // and it will exit cleanly on its own when first client exits.
+ if (process_entry->pid != manager_pid)
+ result.push_back(process_entry->pid);
#elif defined(OS_POSIX)
result.push_back(process_entry->pid);
#endif
}
-#if defined(OS_LINUX)
- // On Linux we might be running with a zygote process for the renderers.
- // Because of that we sweep the list of processes again and pick those which
- // are children of one of the processes that we've already seen.
- {
- ChildProcessFilter filter(result);
- base::NamedProcessIterator it(chrome::kBrowserProcessExecutableName,
- &filter);
- while ((process_entry = it.NextProcessEntry()))
- result.push_back(process_entry->pid);
- }
-#endif
-
- result.push_back(browser_pid);
-
return result;
}