summaryrefslogtreecommitdiffstats
path: root/content/browser/child_process_launcher.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/child_process_launcher.cc')
-rw-r--r--content/browser/child_process_launcher.cc348
1 files changed, 348 insertions, 0 deletions
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
new file mode 100644
index 0000000..ded41be
--- /dev/null
+++ b/content/browser/child_process_launcher.cc
@@ -0,0 +1,348 @@
+// Copyright (c) 2010 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 "content/browser/child_process_launcher.h"
+
+#include <utility> // For std::pair.
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+#include "chrome/common/chrome_descriptors.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/process_watcher.h"
+#include "chrome/common/result_codes.h"
+#include "content/browser/browser_thread.h"
+
+#if defined(OS_WIN)
+#include "base/file_path.h"
+#include "chrome/common/sandbox_policy.h"
+#elif defined(OS_LINUX)
+#include "base/singleton.h"
+#include "chrome/browser/crash_handler_host_linux.h"
+#include "chrome/browser/zygote_host_linux.h"
+#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "chrome/browser/mach_broker_mac.h"
+#endif
+
+#if defined(OS_POSIX)
+#include "base/global_descriptors_posix.h"
+#endif
+
+// Having the functionality of ChildProcessLauncher be in an internal
+// ref counted object allows us to automatically terminate the process when the
+// parent class destructs, while still holding on to state that we need.
+class ChildProcessLauncher::Context
+ : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
+ public:
+ Context()
+ : client_(NULL),
+ client_thread_id_(BrowserThread::UI),
+ starting_(true)
+#if defined(OS_LINUX)
+ , zygote_(false)
+#endif
+ {
+ }
+
+ void Launch(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& environ,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line,
+ Client* client) {
+ client_ = client;
+
+ CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
+
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &Context::LaunchInternal,
+#if defined(OS_WIN)
+ exposed_dir,
+#elif defined(OS_POSIX)
+ use_zygote,
+ environ,
+ ipcfd,
+#endif
+ cmd_line));
+ }
+
+ void ResetClient() {
+ // No need for locking as this function gets called on the same thread that
+ // client_ would be used.
+ CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
+ client_ = NULL;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
+ friend class ChildProcessLauncher;
+
+ ~Context() {
+ Terminate();
+ }
+
+ void LaunchInternal(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& env,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line) {
+ scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
+ base::ProcessHandle handle = base::kNullProcessHandle;
+#if defined(OS_WIN)
+ handle = sandbox::StartProcessWithAccess(cmd_line, exposed_dir);
+#elif defined(OS_POSIX)
+
+#if defined(OS_LINUX)
+ if (use_zygote) {
+ base::GlobalDescriptors::Mapping mapping;
+ mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd));
+ const int crash_signal_fd =
+ RendererCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket();
+ if (crash_signal_fd >= 0) {
+ mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal,
+ crash_signal_fd));
+ }
+ handle = ZygoteHost::GetInstance()->ForkRenderer(cmd_line->argv(),
+ mapping);
+ } else
+ // Fall through to the normal posix case below when we're not zygoting.
+#endif
+ {
+ base::file_handle_mapping_vector fds_to_map;
+ fds_to_map.push_back(std::make_pair(
+ ipcfd,
+ kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
+
+#if defined(OS_LINUX)
+ // On Linux, we need to add some extra file descriptors for crash handling
+ // and the sandbox.
+ bool is_renderer =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType) ==
+ switches::kRendererProcess;
+ bool is_plugin =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType) ==
+ switches::kPluginProcess;
+ bool is_gpu =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType) ==
+ switches::kGpuProcess;
+
+ if (is_renderer || is_plugin || is_gpu) {
+ int crash_signal_fd;
+ if (is_renderer) {
+ crash_signal_fd = RendererCrashHandlerHostLinux::GetInstance()->
+ GetDeathSignalSocket();
+ } else if (is_plugin) {
+ crash_signal_fd = PluginCrashHandlerHostLinux::GetInstance()->
+ GetDeathSignalSocket();
+ } else {
+ crash_signal_fd = GpuCrashHandlerHostLinux::GetInstance()->
+ GetDeathSignalSocket();
+ }
+ if (crash_signal_fd >= 0) {
+ fds_to_map.push_back(std::make_pair(
+ crash_signal_fd,
+ kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor));
+ }
+ }
+ if (is_renderer) {
+ const int sandbox_fd =
+ RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
+ fds_to_map.push_back(std::make_pair(
+ sandbox_fd,
+ kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
+ }
+#endif // defined(OS_LINUX)
+
+ bool launched = false;
+#if defined(OS_MACOSX)
+ // It is possible for the child process to die immediately after
+ // launching. To prevent leaking MachBroker map entries in this case,
+ // lock around all of LaunchApp(). If the child dies, the death
+ // notification will be processed by the MachBroker after the call to
+ // AddPlaceholderForPid(), enabling proper cleanup.
+ { // begin scope for AutoLock
+ MachBroker* broker = MachBroker::GetInstance();
+ base::AutoLock lock(broker->GetLock());
+
+ // This call to |PrepareForFork()| will start the MachBroker listener
+ // thread, if it is not already running. Therefore the browser process
+ // will be listening for Mach IPC before LaunchApp() is called.
+ broker->PrepareForFork();
+#endif
+ // Actually launch the app.
+ launched = base::LaunchApp(cmd_line->argv(), env, fds_to_map,
+ /* wait= */false, &handle);
+#if defined(OS_MACOSX)
+ if (launched)
+ broker->AddPlaceholderForPid(handle);
+ } // end scope for AutoLock
+#endif
+ if (!launched)
+ handle = base::kNullProcessHandle;
+ }
+#endif // else defined(OS_POSIX)
+
+ BrowserThread::PostTask(
+ client_thread_id_, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ChildProcessLauncher::Context::Notify,
+#if defined(OS_LINUX)
+ use_zygote,
+#endif
+ handle));
+ }
+
+ void Notify(
+#if defined(OS_LINUX)
+ bool zygote,
+#endif
+ base::ProcessHandle handle) {
+ starting_ = false;
+ process_.set_handle(handle);
+#if defined(OS_LINUX)
+ zygote_ = zygote;
+#endif
+ if (client_) {
+ client_->OnProcessLaunched();
+ } else {
+ Terminate();
+ }
+ }
+
+ void Terminate() {
+ if (!process_.handle())
+ return;
+
+ // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
+ // don't this on the UI/IO threads.
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ NewRunnableFunction(
+ &ChildProcessLauncher::Context::TerminateInternal,
+#if defined(OS_LINUX)
+ zygote_,
+#endif
+ process_.handle()));
+ process_.set_handle(base::kNullProcessHandle);
+ }
+
+ static void TerminateInternal(
+#if defined(OS_LINUX)
+ bool zygote,
+#endif
+ base::ProcessHandle handle) {
+ base::Process process(handle);
+ // Client has gone away, so just kill the process. Using exit code 0
+ // means that UMA won't treat this as a crash.
+ process.Terminate(ResultCodes::NORMAL_EXIT);
+ // On POSIX, we must additionally reap the child.
+#if defined(OS_POSIX)
+#if defined(OS_LINUX)
+ if (zygote) {
+ // If the renderer was created via a zygote, we have to proxy the reaping
+ // through the zygote process.
+ ZygoteHost::GetInstance()->EnsureProcessTerminated(handle);
+ } else
+#endif // OS_LINUX
+ {
+ ProcessWatcher::EnsureProcessTerminated(handle);
+ }
+#endif // OS_POSIX
+ process.Close();
+ }
+
+ Client* client_;
+ BrowserThread::ID client_thread_id_;
+ base::Process process_;
+ bool starting_;
+
+#if defined(OS_LINUX)
+ bool zygote_;
+#endif
+};
+
+
+ChildProcessLauncher::ChildProcessLauncher(
+#if defined(OS_WIN)
+ const FilePath& exposed_dir,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::environment_vector& environ,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line,
+ Client* client) {
+ context_ = new Context();
+ context_->Launch(
+#if defined(OS_WIN)
+ exposed_dir,
+#elif defined(OS_POSIX)
+ use_zygote,
+ environ,
+ ipcfd,
+#endif
+ cmd_line,
+ client);
+}
+
+ChildProcessLauncher::~ChildProcessLauncher() {
+ context_->ResetClient();
+}
+
+bool ChildProcessLauncher::IsStarting() {
+ return context_->starting_;
+}
+
+base::ProcessHandle ChildProcessLauncher::GetHandle() {
+ DCHECK(!context_->starting_);
+ return context_->process_.handle();
+}
+
+base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
+ int* exit_code) {
+ base::TerminationStatus status;
+ base::ProcessHandle handle = context_->process_.handle();
+#if defined(OS_LINUX)
+ if (context_->zygote_) {
+ status = ZygoteHost::GetInstance()->GetTerminationStatus(handle, exit_code);
+ } else
+#endif
+ {
+ status = base::GetTerminationStatus(handle, exit_code);
+ }
+
+ // POSIX: If the process crashed, then the kernel closed the socket
+ // for it and so the child has already died by the time we get
+ // here. Since GetTerminationStatus called waitpid with WNOHANG,
+ // it'll reap the process. However, if GetTerminationStatus didn't
+ // reap the child (because it was still running), we'll need to
+ // Terminate via ProcessWatcher. So we can't close the handle here.
+ if (status != base::TERMINATION_STATUS_STILL_RUNNING)
+ context_->process_.Close();
+
+ return status;
+}
+
+void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
+ DCHECK(!context_->starting_);
+ context_->process_.SetProcessBackgrounded(background);
+}