Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Unified Diff: content/browser/child_process_launcher_elevated.cc

Issue 98603007: Launches a privileged utility process. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Adds missing child_process_launcher files. Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/child_process_launcher_elevated.cc
diff --git a/content/browser/child_process_launcher_elevated.cc b/content/browser/child_process_launcher_elevated.cc
new file mode 100644
index 0000000000000000000000000000000000000000..cd9a4944ccc67096e743757a283b272019eb2687
--- /dev/null
+++ b/content/browser/child_process_launcher_elevated.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2012 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.h"
+
+#include <utility> // For std::pair.
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/process/process.h"
+#include "base/synchronization/lock.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"
+
+#include "base/files/file_path.h"
+
+namespace content {
+
+// 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 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;
mef 2013/12/18 21:02:10 nit: tabbing
Drew Haven 2014/01/09 01:15:14 Done.
+ }
+
+ 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 RecordHistograms(const base::TimeTicks begin_launch_time) {
+ base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
+ if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
+ RecordLaunchHistograms(launch_time);
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&ChildProcessLauncherElevated::Context::RecordLaunchHistograms,
+ launch_time));
+ }
+ }
+
+ static void RecordLaunchHistograms(const base::TimeDelta launch_time) {
+ // Log the launch time, separating out the first one (which will likely be
+ // slower due to the rest of the browser initializing at the same time).
+ static bool done_first_launch = false;
+ if (done_first_launch) {
+ UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
+ } else {
+ UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
+ done_first_launch = true;
+ }
+ }
+
+ 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::LaunchOptions options;
+ options.start_hidden = true;
+ base::LaunchElevatedProcess(*cmd_line, options, &handle);
+
+ if (handle)
+ RecordHistograms(begin_launch_time);
+
+ 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 (!handle)
+ LOG(ERROR) << "Failed to launch child process";
+
+ if (client_) {
+ client_->OnProcessLaunched();
mef 2013/12/18 21:02:10 But if !handle, then process is NOT launched?
Drew Haven 2014/01/09 01:15:14 I'm not really sure what's going on here. It's ju
+ } else {
+ Terminate();
+ }
+ }
+
+ void Terminate() {
+ if (!process_.handle())
+ return;
+
+ if (!terminate_child_on_shutdown_)
+ 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,
+ base::Bind(
+ &Context::TerminateInternal,
+ process_.handle()));
+ process_.set_handle(base::kNullProcessHandle);
+ }
+
+ static void SetProcessBackgrounded(base::ProcessHandle handle,
+ bool background) {
+ base::Process process(handle);
+ process.SetProcessBackgrounded(background);
+ }
+
+ 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);
mef 2013/12/18 21:02:10 This will probably fail for elevated process, woul
Drew Haven 2014/01/09 01:15:14 I tested it on my Windows 7 box and the parent pro
+ // On POSIX, we must additionally reap the child.
mef 2013/12/18 21:02:10 Not sure what to do about POSIX stuff, given that
Drew Haven 2014/01/09 01:15:14 For now, at least, I made this class entirely wind
+ 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_;
+
+ // 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 (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
+ context_->process_.Close();
+
+ return context_->termination_status_;
+}
+
+void ChildProcessLauncherElevated::SetProcessBackgrounded(bool background) {
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(
+ &ChildProcessLauncherElevated::Context::SetProcessBackgrounded,
+ GetHandle(), background));
+}
+
+void ChildProcessLauncherElevated::SetTerminateChildOnShutdown(
+ bool terminate_on_shutdown) {
+ if (context_.get())
+ context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698