summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-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
24 files changed, 494 insertions, 145 deletions
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 @@ RendererCrashHandler(const void* crash_context, size_t crash_context_size,
}
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 b6ba22f..33e774c 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 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?
@@ -292,21 +294,19 @@ 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,11 +339,13 @@ 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
@@ -489,6 +491,13 @@ 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 5ce05c2..f229751 100644
--- a/chrome/app/chrome_main_uitest.cc
+++ b/chrome/app/chrome_main_uitest.cc
@@ -18,9 +18,14 @@ 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 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 @@ 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;
- 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 @@ BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile)
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 @@ BrowserRenderProcessHost::~BrowserRenderProcessHost() {
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 @@ bool BrowserRenderProcessHost::Init() {
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 @@ bool BrowserRenderProcessHost::Init() {
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 @@ void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) {
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 @@ 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
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..279918d
--- /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 d9ea3ea..c1aefab 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1613,6 +1613,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 48511d3..e5bce97 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -63,6 +63,9 @@ 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";
@@ -490,6 +493,9 @@ 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 8259d72..33a0239 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -26,6 +26,7 @@ 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[];
@@ -185,6 +186,8 @@ 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 f619508..85b35fa 100644
--- a/chrome/common/ipc_channel.h
+++ b/chrome/common/ipc_channel.h
@@ -87,14 +87,13 @@ 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 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 @@ 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 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 @@ 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 (@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 @@ 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 (@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 @@ 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.
- // 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 @@ bool Channel::ChannelImpl::Send(Message* message) {
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 @@ bool Channel::Send(Message* message) {
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 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
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 @@ 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.
-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 @@ 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.
- 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 @@ base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
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 @@ TEST_F(IPCChannelTest, ChannelProxyTest) {
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 @@ class BackgroundReaper : public PlatformThread::Delegate {
// 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 1bbb624..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..605101e 100644
--- a/chrome/renderer/renderer_main_unittest.cc
+++ b/chrome/renderer/renderer_main_unittest.cc
@@ -53,11 +53,9 @@ void RendererMainTest::TearDown() {
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 e54f6a6..4410e2e 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 @@ 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;
- 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..4a5f5bb 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,6 +51,24 @@ 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;
@@ -77,37 +76,32 @@ ChromeProcessList GetRunningChromeProcesses(const FilePath& data_dir) {
if (browser_pid < 0)
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;
}