Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // 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.
| |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/child_process_launcher_elevated_win.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "base/files/file_path.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/metrics/histogram.h" | |
| 13 #include "base/process/process.h" | |
| 14 #include "base/threading/thread.h" | |
| 15 #include "content/public/browser/browser_thread.h" | |
| 16 #include "content/public/browser/content_browser_client.h" | |
| 17 #include "content/public/common/content_descriptors.h" | |
| 18 #include "content/public/common/content_switches.h" | |
| 19 #include "content/public/common/result_codes.h" | |
| 20 | |
| 21 namespace content { | |
| 22 | |
| 23 // 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.
| |
| 24 // ref counted object allows us to automatically terminate the process when the | |
| 25 // parent class destructs, while still holding on to state that we need. | |
| 26 class ChildProcessLauncherElevated::Context | |
| 27 : public base::RefCountedThreadSafe<ChildProcessLauncherElevated::Context> { | |
| 28 public: | |
| 29 Context() | |
| 30 : client_(NULL), | |
| 31 client_thread_id_(BrowserThread::UI), | |
| 32 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), | |
| 33 exit_code_(RESULT_CODE_NORMAL_EXIT), | |
| 34 starting_(true), | |
| 35 terminate_child_on_shutdown_(true) {} | |
| 36 | |
| 37 void Launch( | |
| 38 CommandLine* cmd_line, | |
| 39 int child_process_id, | |
| 40 Client* client) { | |
| 41 client_ = client; | |
| 42 | |
| 43 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); | |
| 44 | |
| 45 BrowserThread::PostTask( | |
| 46 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 47 base::Bind( | |
| 48 &Context::LaunchInternal, | |
| 49 make_scoped_refptr(this), | |
| 50 client_thread_id_, | |
| 51 child_process_id, | |
| 52 cmd_line)); | |
| 53 } | |
| 54 | |
| 55 void ResetClient() { | |
| 56 // No need for locking as this function gets called on the same thread that | |
| 57 // client_ would be used. | |
| 58 CHECK(BrowserThread::CurrentlyOn(client_thread_id_)); | |
| 59 client_ = NULL; | |
| 60 } | |
| 61 | |
| 62 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) { | |
| 63 terminate_child_on_shutdown_ = terminate_on_shutdown; | |
| 64 } | |
| 65 | |
| 66 private: | |
| 67 friend class | |
| 68 base::RefCountedThreadSafe<ChildProcessLauncherElevated::Context>; | |
| 69 friend class ChildProcessLauncherElevated; | |
| 70 | |
| 71 ~Context() { | |
| 72 Terminate(); | |
| 73 } | |
| 74 | |
| 75 static void RecordHistograms(const base::TimeTicks begin_launch_time) { | |
| 76 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
| |
| 77 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) { | |
| 78 RecordLaunchHistograms(launch_time); | |
| 79 } else { | |
| 80 BrowserThread::PostTask( | |
| 81 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 82 base::Bind( | |
| 83 &ChildProcessLauncherElevated::Context::RecordLaunchHistograms, | |
| 84 launch_time)); | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 static void RecordLaunchHistograms(const base::TimeDelta launch_time) { | |
| 89 // Log the launch time, separating out the first one (which will likely be | |
| 90 // slower due to the rest of the browser initializing at the same time). | |
| 91 static bool done_first_launch = false; | |
| 92 if (done_first_launch) { | |
| 93 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); | |
| 94 } else { | |
| 95 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); | |
| 96 done_first_launch = true; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 static void LaunchInternal( | |
| 101 // |this_object| is NOT thread safe. Only use it to post a task back. | |
| 102 scoped_refptr<Context> this_object, | |
| 103 BrowserThread::ID client_thread_id, | |
| 104 int child_process_id, | |
| 105 CommandLine* cmd_line) { | |
| 106 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); | |
| 107 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); | |
| 108 | |
| 109 base::ProcessHandle handle; | |
| 110 base::LaunchOptions options; | |
| 111 options.start_hidden = true; | |
| 112 base::LaunchElevatedProcess(*cmd_line, options, &handle); | |
| 113 | |
| 114 if (handle) | |
| 115 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
| |
| 116 | |
| 117 BrowserThread::PostTask( | |
| 118 client_thread_id, FROM_HERE, | |
| 119 base::Bind( | |
| 120 &Context::Notify, | |
| 121 this_object.get(), | |
| 122 handle)); | |
| 123 } | |
| 124 | |
| 125 void Notify( | |
| 126 base::ProcessHandle handle) { | |
| 127 starting_ = false; | |
| 128 process_.set_handle(handle); | |
| 129 if (!handle) | |
| 130 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
| |
| 131 | |
| 132 if (client_) { | |
| 133 client_->OnProcessLaunched(); | |
| 134 } else { | |
| 135 Terminate(); | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 void Terminate() { | |
| 140 if (!process_.handle()) | |
| 141 return; | |
| 142 | |
| 143 if (!terminate_child_on_shutdown_) | |
| 144 return; | |
| 145 | |
| 146 // 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.
| |
| 147 // don't this on the UI/IO threads. | |
| 148 BrowserThread::PostTask( | |
| 149 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 150 base::Bind( | |
| 151 &Context::TerminateInternal, | |
| 152 process_.handle())); | |
| 153 process_.set_handle(base::kNullProcessHandle); | |
| 154 } | |
| 155 | |
| 156 static void SetProcessBackgrounded(base::ProcessHandle handle, | |
| 157 bool background) { | |
| 158 base::Process process(handle); | |
| 159 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
| |
| 160 } | |
| 161 | |
| 162 static void TerminateInternal( | |
| 163 base::ProcessHandle handle) { | |
| 164 base::Process process(handle); | |
| 165 // Client has gone away, so just kill the process. Using exit code 0 | |
| 166 // means that UMA won't treat this as a crash. | |
| 167 process.Terminate(RESULT_CODE_NORMAL_EXIT); | |
| 168 // 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
| |
| 169 process.Close(); | |
| 170 } | |
| 171 | |
| 172 Client* client_; | |
| 173 BrowserThread::ID client_thread_id_; | |
| 174 base::Process process_; | |
| 175 base::TerminationStatus termination_status_; | |
| 176 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
| |
| 177 bool starting_; | |
| 178 // Controls whether the child process should be terminated on browser | |
| 179 // shutdown. Default behavior is to terminate the child. | |
| 180 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
| |
| 181 }; | |
| 182 | |
| 183 | |
| 184 ChildProcessLauncherElevated::ChildProcessLauncherElevated( | |
| 185 CommandLine* cmd_line, | |
| 186 int child_process_id, | |
| 187 Client* client) { | |
| 188 context_ = new Context(); | |
| 189 context_->Launch( | |
| 190 cmd_line, | |
| 191 child_process_id, | |
| 192 client); | |
| 193 } | |
| 194 | |
| 195 ChildProcessLauncherElevated::~ChildProcessLauncherElevated() { | |
| 196 context_->ResetClient(); | |
| 197 } | |
| 198 | |
| 199 bool ChildProcessLauncherElevated::IsStarting() { | |
| 200 return context_->starting_; | |
| 201 } | |
| 202 | |
| 203 base::ProcessHandle ChildProcessLauncherElevated::GetHandle() { | |
| 204 DCHECK(!context_->starting_); | |
| 205 return context_->process_.handle(); | |
| 206 } | |
| 207 | |
| 208 base::TerminationStatus ChildProcessLauncherElevated::GetChildTerminationStatus( | |
| 209 bool known_dead, | |
| 210 int* exit_code) { | |
| 211 base::ProcessHandle handle = context_->process_.handle(); | |
| 212 if (handle == base::kNullProcessHandle) { | |
| 213 // Process is already gone, so return the cached termination status. | |
| 214 if (exit_code) | |
| 215 *exit_code = context_->exit_code_; | |
| 216 return context_->termination_status_; | |
| 217 } | |
| 218 context_->termination_status_ = | |
| 219 base::GetTerminationStatus(handle, &context_->exit_code_); | |
| 220 | |
| 221 if (exit_code) | |
| 222 *exit_code = context_->exit_code_; | |
| 223 | |
| 224 // POSIX: If the process crashed, then the kernel closed the socket | |
| 225 // for it and so the child has already died by the time we get | |
| 226 // here. Since GetTerminationStatus called waitpid with WNOHANG, | |
| 227 // it'll reap the process. However, if GetTerminationStatus didn't | |
| 228 // reap the child (because it was still running), we'll need to | |
| 229 // Terminate via ProcessWatcher. So we can't close the handle here. | |
| 230 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) | |
| 231 context_->process_.Close(); | |
| 232 | |
| 233 return context_->termination_status_; | |
| 234 } | |
| 235 | |
| 236 void ChildProcessLauncherElevated::SetProcessBackgrounded(bool background) { | |
| 237 BrowserThread::PostTask( | |
| 238 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 239 base::Bind( | |
| 240 &ChildProcessLauncherElevated::Context::SetProcessBackgrounded, | |
| 241 GetHandle(), background)); | |
| 242 } | |
| 243 | |
| 244 void ChildProcessLauncherElevated::SetTerminateChildOnShutdown( | |
| 245 bool terminate_on_shutdown) { | |
| 246 if (context_.get()) | |
| 247 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); | |
| 248 } | |
| 249 | |
| 250 } // namespace content | |
| OLD | NEW |