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 |