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

Side by Side Diff: content/browser/child_process_launcher_elevated_win.cc

Issue 98603007: Launches a privileged utility process. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Mostly test cleanup. Created 6 years, 11 months 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 unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698