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..65ac6f119e6a802e5a6d5d0e7e1d62a2fdcfbeae |
--- /dev/null |
+++ b/content/browser/child_process_launcher_elevated_win.cc |
@@ -0,0 +1,250 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
mef
2014/01/10 18:22:55
nit: Copyright 2014
Drew Haven
2014/01/16 02:52:05
Done.
|
+// 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 { |
+ |
+// Having the functionality of ChildProcessLauncher be in an internal |
mef
2014/01/10 18:22:55
Could you re-word it?
Drew Haven
2014/01/16 02:52:05
Done.
|
+// 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) {} |
+ |
+ 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; |
mef
2014/01/10 18:22:55
Does this include the time of user sitting on UAC
Drew Haven
2014/01/16 02:52:05
Yes it will. That might mess up our statistics?
mef
2014/01/16 16:22:31
Good question. I think it is pretty useless, given
|
+ 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); |
mef
2014/01/10 18:22:55
Would we want to histogram failures as well?
Drew Haven
2014/01/16 02:52:05
In this case I'm duplicating what the non-elevated
mef
2014/01/16 16:22:31
Good point, especially if we can get meaningful fa
|
+ |
+ 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"; |
mef
2014/01/10 18:22:55
It seems that if launch fails, then there is no no
Drew Haven
2014/01/16 02:52:05
This is a good point. I'll add a method with a de
|
+ |
+ if (client_) { |
+ client_->OnProcessLaunched(); |
+ } 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 |
mef
2014/01/10 18:22:55
Stale comment?
Drew Haven
2014/01/16 02:52:05
Done.
|
+ // 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); |
mef
2014/01/10 18:22:55
Does this actually work on elevated process?
Drew Haven
2014/01/16 02:52:05
You're right. Since we shouldn't be able to lower
|
+ } |
+ |
+ 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); |
+ // On POSIX, we must additionally reap the child. |
mef
2014/01/10 18:22:55
This is _win module...
Drew Haven
2014/01/16 02:52:05
Done.
Yeah... bulk copies. Oops. Fortunately th
|
+ process.Close(); |
+ } |
+ |
+ Client* client_; |
+ BrowserThread::ID client_thread_id_; |
+ base::Process process_; |
+ base::TerminationStatus termination_status_; |
+ int exit_code_; |
mef
2014/01/10 18:22:55
not populated?
Drew Haven
2014/01/16 02:52:05
It is, see line 217. Interestingly those members
|
+ 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_; |
mef
2014/01/10 18:22:55
is it implemented?
Drew Haven
2014/01/16 02:52:05
It might be better phrased as terminate_child_on_d
|
+}; |
+ |
+ |
+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 |