diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/plugin_process_host.cc | 12 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.cc | 75 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.h | 3 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.cc | 90 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.h | 41 | ||||
-rw-r--r-- | chrome/browser/zygote_main_linux.cc | 168 |
6 files changed, 362 insertions, 27 deletions
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(); +} |