| Index: content/browser/child_process_launcher_elevated_win.cc
|
| diff --git a/content/browser/child_process_launcher_elevated_win.cc b/content/browser/child_process_launcher_elevated_win.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..336fa970ffceab1b05d58d21761146079da34348
|
| --- /dev/null
|
| +++ b/content/browser/child_process_launcher_elevated_win.cc
|
| @@ -0,0 +1,206 @@
|
| +// Copyright (c) 2014 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_elevated_win.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/command_line.h"
|
| +#include "base/files/file_path.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/metrics/histogram.h"
|
| +#include "base/process/process.h"
|
| +#include "base/threading/thread.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/content_browser_client.h"
|
| +#include "content/public/common/content_descriptors.h"
|
| +#include "content/public/common/content_switches.h"
|
| +#include "content/public/common/result_codes.h"
|
| +
|
| +namespace content {
|
| +
|
| +// By making this a ref-counted internal class we can track when the parent
|
| +// object destructs and still have all our state available to terminate the
|
| +// process.
|
| +class ChildProcessLauncherElevated::Context
|
| + : public base::RefCountedThreadSafe<ChildProcessLauncherElevated::Context> {
|
| + public:
|
| + Context()
|
| + : client_(NULL),
|
| + client_thread_id_(BrowserThread::UI),
|
| + termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
|
| + exit_code_(RESULT_CODE_NORMAL_EXIT),
|
| + starting_(true),
|
| + terminate_child_on_shutdown_(true) {}
|
| +
|
| + void Launch(
|
| + CommandLine* cmd_line,
|
| + int child_process_id,
|
| + Client* client) {
|
| + client_ = client;
|
| +
|
| + CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
|
| +
|
| + BrowserThread::PostTask(
|
| + BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
|
| + base::Bind(
|
| + &Context::LaunchInternal,
|
| + make_scoped_refptr(this),
|
| + client_thread_id_,
|
| + child_process_id,
|
| + 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;
|
| + }
|
| +
|
| + void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
|
| + terminate_child_on_shutdown_ = terminate_on_shutdown;
|
| + }
|
| +
|
| + private:
|
| + friend class
|
| + base::RefCountedThreadSafe<ChildProcessLauncherElevated::Context>;
|
| + friend class ChildProcessLauncherElevated;
|
| +
|
| + ~Context() {
|
| + Terminate();
|
| + }
|
| +
|
| + static void LaunchInternal(
|
| + // |this_object| is NOT thread safe. Only use it to post a task back.
|
| + scoped_refptr<Context> this_object,
|
| + BrowserThread::ID client_thread_id,
|
| + int child_process_id,
|
| + CommandLine* cmd_line) {
|
| + scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
|
| + base::TimeTicks begin_launch_time = base::TimeTicks::Now();
|
| +
|
| + base::ProcessHandle handle = base::kNullProcessHandle;
|
| + base::LaunchOptions options;
|
| + options.start_hidden = true;
|
| + base::LaunchElevatedProcess(*cmd_line, options, &handle);
|
| +
|
| + BrowserThread::PostTask(
|
| + client_thread_id, FROM_HERE,
|
| + base::Bind(
|
| + &Context::Notify,
|
| + this_object.get(),
|
| + handle));
|
| + }
|
| +
|
| + void Notify(base::ProcessHandle handle) {
|
| + starting_ = false;
|
| + process_.set_handle(handle);
|
| +
|
| + if (client_) {
|
| + if (handle) {
|
| + client_->OnProcessLaunched();
|
| + } else {
|
| + client_->OnProcessLaunchFailed();
|
| + }
|
| + } else {
|
| + Terminate();
|
| + }
|
| + }
|
| +
|
| + void Terminate() {
|
| + if (!process_.handle())
|
| + return;
|
| +
|
| + if (!terminate_child_on_shutdown_)
|
| + return;
|
| +
|
| + BrowserThread::PostTask(
|
| + BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
|
| + base::Bind(
|
| + &Context::TerminateInternal,
|
| + process_.handle()));
|
| + process_.set_handle(base::kNullProcessHandle);
|
| + }
|
| +
|
| + static void TerminateInternal(
|
| + 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(RESULT_CODE_NORMAL_EXIT);
|
| + process.Close();
|
| + }
|
| +
|
| + Client* client_;
|
| + BrowserThread::ID client_thread_id_;
|
| + base::Process process_;
|
| + base::TerminationStatus termination_status_;
|
| + int exit_code_;
|
| + bool starting_;
|
| + // Controls whether the child process should be terminated on browser
|
| + // shutdown. Default behavior is to terminate the child.
|
| + bool terminate_child_on_shutdown_;
|
| +};
|
| +
|
| +
|
| +ChildProcessLauncherElevated::ChildProcessLauncherElevated(
|
| + CommandLine* cmd_line,
|
| + int child_process_id,
|
| + Client* client) {
|
| + context_ = new Context();
|
| + context_->Launch(
|
| + cmd_line,
|
| + child_process_id,
|
| + client);
|
| +}
|
| +
|
| +ChildProcessLauncherElevated::~ChildProcessLauncherElevated() {
|
| + context_->ResetClient();
|
| +}
|
| +
|
| +bool ChildProcessLauncherElevated::IsStarting() {
|
| + return context_->starting_;
|
| +}
|
| +
|
| +base::ProcessHandle ChildProcessLauncherElevated::GetHandle() {
|
| + DCHECK(!context_->starting_);
|
| + return context_->process_.handle();
|
| +}
|
| +
|
| +base::TerminationStatus ChildProcessLauncherElevated::GetChildTerminationStatus(
|
| + bool known_dead,
|
| + int* exit_code) {
|
| + base::ProcessHandle handle = context_->process_.handle();
|
| + if (handle == base::kNullProcessHandle) {
|
| + // Process is already gone, so return the cached termination status.
|
| + if (exit_code)
|
| + *exit_code = context_->exit_code_;
|
| + return context_->termination_status_;
|
| + }
|
| + context_->termination_status_ =
|
| + base::GetTerminationStatus(handle, &context_->exit_code_);
|
| +
|
| + if (exit_code)
|
| + *exit_code = context_->exit_code_;
|
| +
|
| + if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
|
| + context_->process_.Close();
|
| +
|
| + return context_->termination_status_;
|
| +}
|
| +
|
| +// For elevated processes we cannot lower the priority. This should not be
|
| +// called and is a no-op.
|
| +void ChildProcessLauncherElevated::SetProcessBackgrounded(bool background) {
|
| + DLOG(ERROR) << "Cannot lower privilege of an elevated process.";
|
| +}
|
| +
|
| +void ChildProcessLauncherElevated::SetTerminateChildOnShutdown(
|
| + bool terminate_on_shutdown) {
|
| + if (context_.get())
|
| + context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
|
| +}
|
| +
|
| +} // namespace content
|
|
|