[go: nahoru, domu]

Linux: refactor zygote support

http://code.google.com/p/chromium/wiki/LinuxZygote

  * Move Chrome specific bits out of base
  * Move away from the idea of reserved file descriptors (which don't really work
  with zygotes)
  * Load resources before forking renderers (means that we don't need
  communication between the zygote process and the renderers)
  * Make sure that gdb works against the browser again
  * Make sure that we have different ASLR between the renderers and the browser.

http://codereview.chromium.org/119335

(This is a reland. First landed in r18109, reverted in r18112.)


git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18291 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/base.gyp b/base/base.gyp
index 5d09098..96deb53 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -133,6 +133,8 @@
         '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',
@@ -324,6 +326,7 @@
         'tracked_objects.cc',
         'tracked_objects.h',
         'tuple.h',
+        'unix_domain_socket_posix.cc',
         'values.cc',
         'values.h',
         'version.cc',
@@ -418,9 +421,6 @@
               # 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,9 +685,6 @@
       ],
       '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 735620d..582c8b84 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -168,6 +168,12 @@
 }
 
 // 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 903e9b52..121eb8c 100644
--- a/base/command_line.h
+++ b/base/command_line.h
@@ -45,6 +45,9 @@
   // 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 92b9d00..aca0ff9 100644
--- a/base/file_util_posix.cc
+++ b/base/file_util_posix.cc
@@ -27,7 +27,6 @@
 #include "base/logging.h"
 #include "base/string_util.h"
 #include "base/time.h"
-#include "base/zygote_manager.h"
 
 namespace file_util {
 
@@ -646,19 +645,8 @@
 }
 
 bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
-  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);
+  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
new file mode 100644
index 0000000..869d196
--- /dev/null
+++ b/base/global_descriptors_posix.cc
@@ -0,0 +1,46 @@
+// 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
new file mode 100644
index 0000000..f606a82
--- /dev/null
+++ b/base/global_descriptors_posix.h
@@ -0,0 +1,66 @@
+// 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 77e9583..b5af671 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -19,12 +19,9 @@
 #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;
@@ -40,7 +37,6 @@
 #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"
@@ -208,20 +204,6 @@
       }
     }
     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 4fe081f..41d04703 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -141,14 +141,6 @@
 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 ba2d8cdd..7560257 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -17,7 +17,6 @@
 #include "base/logging.h"
 #include "base/string_tokenizer.h"
 #include "base/string_util.h"
-#include "base/zygote_manager.h"
 
 namespace {
 
@@ -86,22 +85,6 @@
   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
deleted file mode 100644
index 5236a08..0000000
--- a/base/reserved_file_descriptors.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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
new file mode 100644
index 0000000..8d53d2a
--- /dev/null
+++ b/base/unix_domain_socket_posix.cc
@@ -0,0 +1,98 @@
+// 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
new file mode 100644
index 0000000..46c473a
--- /dev/null
+++ b/base/unix_domain_socket_posix.h
@@ -0,0 +1,22 @@
+// 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
deleted file mode 100644
index 7b2c026..0000000
--- a/base/zygote_manager.cc
+++ /dev/null
@@ -1,832 +0,0 @@
-// 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
deleted file mode 100644
index 5b94533..0000000
--- a/base/zygote_manager.h
+++ /dev/null
@@ -1,143 +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_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 0ee0bed..ef3dfa2 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,13 +501,11 @@
 }
 
 void EnableRendererCrashDumping() {
-  // When the browser forks off our process, it installs the crash signal file
-  // descriptor in slot kMagicCrashSignalFd.
-
+  const int fd = Singleton<base::GlobalDescriptors>()->Get(kCrashDumpSignal);
   // We deliberately leak this object.
   google_breakpad::ExceptionHandler* handler =
       new google_breakpad::ExceptionHandler("" /* unused */, NULL, NULL,
-                                            (void*) kMagicCrashSignalFd, true);
+                                            (void*) fd, true);
   handler->set_crash_handler(RendererCrashHandler);
 }
 
diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc
index c41c95bd..4759d02 100644
--- a/chrome/app/chrome_dll_main.cc
+++ b/chrome/app/chrome_dll_main.cc
@@ -34,6 +34,9 @@
 #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"
@@ -45,9 +48,6 @@
 #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,6 +57,7 @@
 #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"
@@ -75,6 +76,7 @@
 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?
@@ -299,21 +301,19 @@
   // 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
@@ -346,11 +346,15 @@
     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);
+#else
+    browser_pid = base::GetCurrentProcId();
+#endif
 
 #if defined(OS_POSIX)
     // When you hit Ctrl-C in a terminal running the browser
@@ -496,6 +500,13 @@
 #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 5ce05c2..f229751 100644
--- a/chrome/app/chrome_main_uitest.cc
+++ b/chrome/app/chrome_main_uitest.cc
@@ -18,9 +18,14 @@
   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 921f860..5216755 100644
--- a/chrome/browser/plugin_process_host.cc
+++ b/chrome/browser/plugin_process_host.cc
@@ -14,6 +14,9 @@
 
 #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"
@@ -30,6 +33,7 @@
 #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"
@@ -393,10 +397,10 @@
   // 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;
-  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));
+  const int ipcfd = channel().GetClientFileDescriptor();
+  if (ipcfd > -1)
+    fds_to_map.push_back(std::pair<int, int>(
+        ipcfd, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
   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 829ef47..29e7ef0 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -22,7 +22,6 @@
 #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"
@@ -35,9 +34,6 @@
 #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"
@@ -45,6 +41,7 @@
 #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"
@@ -54,6 +51,11 @@
 #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)
@@ -134,7 +136,8 @@
       backgrounded_(true),
       ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_(
             base::TimeDelta::FromSeconds(5),
-            this, &BrowserRenderProcessHost::ClearTransportDIBCache)) {
+            this, &BrowserRenderProcessHost::ClearTransportDIBCache)),
+      zygote_child_(false) {
   widget_helper_ = new RenderWidgetHelper();
 
   registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
@@ -170,7 +173,13 @@
     audio_renderer_host_->Destroy();
 
   if (process_.handle() && !run_renderer_in_process()) {
-    ProcessWatcher::EnsureProcessTerminated(process_.handle());
+    if (zygote_child_) {
+#if defined(OS_LINUX)
+      Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle());
+#endif
+    } else {
+      ProcessWatcher::EnsureProcessTerminated(process_.handle());
+    }
   }
 
   ClearTransportDIBCache();
@@ -294,7 +303,9 @@
         ASCIIToWide(field_trial->MakePersistentString()));
 
 #if defined(OS_POSIX)
-  if (browser_command_line.HasSwitch(switches::kRendererCmdPrefix)) {
+  const bool has_cmd_prefix =
+      browser_command_line.HasSwitch(switches::kRendererCmdPrefix);
+  if (has_cmd_prefix) {
     // launch the renderer child with some prefix (usually "gdb --args")
     const std::wstring prefix =
         browser_command_line.GetSwitchValue(switches::kRendererCmdPrefix);
@@ -334,24 +345,42 @@
     base::ProcessHandle process = 0;
 #if defined(OS_WIN)
     process = sandbox::StartProcess(&cmd_line);
-#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));
+#elif defined(OS_POSIX)
 #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);
+    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;
+#if defined(OS_LINUX)
+    }
 #endif
 #endif
+
     if (!process) {
       channel_.reset();
       return false;
@@ -666,7 +695,7 @@
       const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
       if (cmd_line.HasSwitch(switches::kRendererCmdPrefix))
         return;
-      CHECK(peer_pid == process_.pid());
+      CHECK(peer_pid == process_.pid()) << 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 b380211..6c259f0 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.h
+++ b/chrome/browser/renderer_host/browser_render_process_host.h
@@ -154,6 +154,9 @@
   // 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
new file mode 100644
index 0000000..f56e6e93
--- /dev/null
+++ b/chrome/browser/zygote_host_linux.cc
@@ -0,0 +1,90 @@
+// 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
new file mode 100644
index 0000000..279918d0
--- /dev/null
+++ b/chrome/browser/zygote_host_linux.h
@@ -0,0 +1,41 @@
+// 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
new file mode 100644
index 0000000..d450c39
--- /dev/null
+++ b/chrome/browser/zygote_main_linux.cc
@@ -0,0 +1,168 @@
+// 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 0490ce5..7a2d0b7 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1623,6 +1623,8 @@
         '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
new file mode 100644
index 0000000..3d6be45
--- /dev/null
+++ b/chrome/common/chrome_descriptors.h
@@ -0,0 +1,15 @@
+// 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 b12e31c9..c57b0bb 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -63,6 +63,9 @@
 // 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";
 
@@ -490,6 +493,9 @@
 // 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 03f1f11f..8d3785a 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -26,6 +26,7 @@
 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[];
@@ -185,6 +186,8 @@
 
 extern const wchar_t kNoDefaultBrowserCheck[];
 
+extern const wchar_t kZygoteCmdPrefix[];
+
 extern const wchar_t kThumbnailStore[];
 
 extern const wchar_t kTryChromeAgain[];
diff --git a/chrome/common/ipc_channel.h b/chrome/common/ipc_channel.h
index f619508..85b35fa 100644
--- a/chrome/common/ipc_channel.h
+++ b/chrome/common/ipc_channel.h
@@ -87,14 +87,13 @@
 
 #if defined(OS_POSIX)
   // On POSIX an IPC::Channel wraps a socketpair(), this method returns the
-  // FD # for the client end of the socket and the equivalent FD# to use for
-  // mapping it into the Child process.
+  // FD # for the client end of the socket.
   // 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 for both parameters.
-  void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+  // socketpair() in which case this method returns -1.
+  int GetClientFileDescriptor() const;
 #endif  // defined(OS_POSIX)
 
  private:
diff --git a/chrome/common/ipc_channel_posix.cc b/chrome/common/ipc_channel_posix.cc
index 52cad9d..babc16c 100644
--- a/chrome/common/ipc_channel_posix.cc
+++ b/chrome/common/ipc_channel_posix.cc
@@ -17,15 +17,16 @@
 
 #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"
@@ -40,7 +41,7 @@
 //
 // 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 kClientChannelFd in the child.  Future
+// magic file descriptor kPrimaryIPCChannel in the child.  Future
 // connections (file descriptors) can then be passed via that
 // connection via sendmsg().
 
@@ -50,7 +51,7 @@
 // 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 (@kClientChannelFd). However, we
+// specific file descriptor number (@kPrimaryIPCChannel). However, we
 // also have to support the case where we are running unittests in the
 // same process.  (We do not support forking without execing.)
 //
@@ -58,7 +59,7 @@
 //   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 (@kClientChannelFd). The client will search for the
+//   the magic slot (@kPrimaryIPCChannel). 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.
@@ -124,10 +125,7 @@
 
   // If we don't find an entry, we assume that the correct value has been
   // inserted in the magic slot.
-  // kClientChannelFd is the file descriptor number that a client process
-  // expects to find its IPC socket; see reserved_file_descriptors.h.
-
-  return kClientChannelFd;
+  return Singleton<base::GlobalDescriptors>()->Get(kPrimaryIPCChannel);
 }
 
 //------------------------------------------------------------------------------
@@ -671,11 +669,8 @@
   return true;
 }
 
-void Channel::ChannelImpl::GetClientFileDescriptorMapping(int *src_fd,
-                                                          int *dest_fd) const {
-  DCHECK(mode_ == MODE_SERVER);
-  *src_fd = client_pipe_;
-  *dest_fd = kClientChannelFd;
+int Channel::ChannelImpl::GetClientFileDescriptor() const {
+  return client_pipe_;
 }
 
 // Called by libevent when we can read from th pipe without blocking.
@@ -803,8 +798,8 @@
   return channel_impl_->Send(message);
 }
 
-void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const {
-  return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
+int Channel::GetClientFileDescriptor() const {
+  return channel_impl_->GetClientFileDescriptor();
 }
 
 }  // namespace IPC
diff --git a/chrome/common/ipc_channel_posix.h b/chrome/common/ipc_channel_posix.h
index 414730a..ed3d727 100644
--- a/chrome/common/ipc_channel_posix.h
+++ b/chrome/common/ipc_channel_posix.h
@@ -29,7 +29,7 @@
   void Close();
   void set_listener(Listener* listener) { listener_ = listener; }
   bool Send(Message* message);
-  void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+  int GetClientFileDescriptor() 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 6e8919a3..c77ee7e 100644
--- a/chrome/common/ipc_channel_proxy.cc
+++ b/chrome/common/ipc_channel_proxy.cc
@@ -289,11 +289,10 @@
 // See the TODO regarding lazy initialization of the channel in
 // ChannelProxy::Init().
 // We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe.
-void ChannelProxy::GetClientFileDescriptorMapping(int *src_fd,
-                                                  int *dest_fd) const {
+int ChannelProxy::GetClientFileDescriptor() const {
   Channel *channel = context_.get()->channel_;
   DCHECK(channel); // Channel must have been created first.
-  channel->GetClientFileDescriptorMapping(src_fd, dest_fd);
+  return channel->GetClientFileDescriptor();
 }
 #endif
 
diff --git a/chrome/common/ipc_channel_proxy.h b/chrome/common/ipc_channel_proxy.h
index a395b1f..8adcb5b 100644
--- a/chrome/common/ipc_channel_proxy.h
+++ b/chrome/common/ipc_channel_proxy.h
@@ -121,7 +121,7 @@
   // 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.
-  void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+  int GetClientFileDescriptor() const;
 #endif  // defined(OS_POSIX)
 
  protected:
diff --git a/chrome/common/ipc_tests.cc b/chrome/common/ipc_tests.cc
index d347fb5..c2e0baa 100644
--- a/chrome/common/ipc_tests.cc
+++ b/chrome/common/ipc_tests.cc
@@ -21,11 +21,16 @@
 #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"
@@ -86,11 +91,9 @@
       CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
 
   base::file_handle_mapping_vector fds_to_map;
-  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));
+  const int ipcfd = channel->GetClientFileDescriptor();
+  if (ipcfd > -1) {
+    fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
   }
 
   base::ProcessHandle ret = NULL;
@@ -258,11 +261,9 @@
     bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
                               switches::kDebugChildren);
     base::file_handle_mapping_vector fds_to_map;
-    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));
+    const int ipcfd = chan.GetClientFileDescriptor();
+    if (ipcfd > -1) {
+      fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
     }
 
     base::ProcessHandle process_handle = MultiProcessTest::SpawnChild(
diff --git a/chrome/common/process_watcher_posix.cc b/chrome/common/process_watcher_posix.cc
index 497b80b..f1ae4f4 100644
--- a/chrome/common/process_watcher_posix.cc
+++ b/chrome/common/process_watcher_posix.cc
@@ -11,7 +11,6 @@
 
 #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.
@@ -70,20 +69,8 @@
 // static
 void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
   // If the child is already dead, then there's nothing to do
-  const int result = HANDLE_EINTR(waitpid(process, NULL, WNOHANG));
-  if (result > 0)
+  if (IsChildDead(process))
     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 1bbb6240..fb8880f 100644
--- a/chrome/plugin/plugin_main.cc
+++ b/chrome/plugin/plugin_main.cc
@@ -22,6 +22,9 @@
 #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 18e67fa..605101ef 100644
--- a/chrome/renderer/renderer_main_unittest.cc
+++ b/chrome/renderer/renderer_main_unittest.cc
@@ -53,11 +53,9 @@
 ProcessHandle RendererMainTest::SpawnChild(const std::wstring &procname,
                                            IPC::Channel *channel) {
   base::file_handle_mapping_vector fds_to_map;
-  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));
+  const int ipcfd = channel->GetClientFileDescriptor();
+  if (ipcfd > -1) {
+    fds_to_map.push_back(std::pair<int,int>(ipcfd, 3));
   }
 
    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 27f1701d..37c1037 100644
--- a/chrome/test/automation/automation_proxy.cc
+++ b/chrome/test/automation/automation_proxy.cc
@@ -13,6 +13,7 @@
 #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"
@@ -440,10 +441,9 @@
 #if defined(OS_POSIX)
 base::file_handle_mapping_vector AutomationProxy::fds_to_map() const {
   base::file_handle_mapping_vector map;
-  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));
+  const int ipcfd = channel_->GetClientFileDescriptor();
+  if (ipcfd > -1)
+    map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3));
   return map;
 }
 #endif  // defined(OS_POSIX)
diff --git a/chrome/test/chrome_process_util.cc b/chrome/test/chrome_process_util.cc
index 296b291..9d858e4 100644
--- a/chrome/test/chrome_process_util.cc
+++ b/chrome/test/chrome_process_util.cc
@@ -5,6 +5,7 @@
 #include "chrome/test/chrome_process_util.h"
 
 #include <vector>
+#include <set>
 
 #include "base/process_util.h"
 #include "base/time.h"
@@ -14,26 +15,6 @@
 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.
@@ -70,44 +51,57 @@
     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;
 
   base::ProcessId browser_pid = ChromeBrowserProcessId(data_dir);
-  if (browser_pid < 0)
+  if (browser_pid == (base::ProcessId) -1)
     return result;
 
-  // 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);
+  ChildProcessFilter filter(browser_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;
 }