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

Side by Side Diff: content/browser/child_process_launcher.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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/logging.h"
5 #include "content/browser/child_process_launcher.h" 6 #include "content/browser/child_process_launcher.h"
6 7 #include "content/browser/child_process_launcher_impl.h"
7 #include <utility> // For std::pair.
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/metrics/histogram.h"
15 #include "base/process/process.h"
16 #include "base/synchronization/lock.h"
17 #include "base/threading/thread.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/common/content_descriptors.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/public/common/result_codes.h"
23 8
24 #if defined(OS_WIN) 9 #if defined(OS_WIN)
25 #include "base/files/file_path.h" 10 #include "content/browser/child_process_launcher_elevated_win.h"
26 #include "content/common/sandbox_win.h"
27 #include "content/public/common/sandbox_init.h"
28 #include "content/public/common/sandboxed_process_launcher_delegate.h"
29 #elif defined(OS_MACOSX)
30 #include "content/browser/mach_broker_mac.h"
31 #elif defined(OS_ANDROID)
32 #include "base/android/jni_android.h"
33 #include "content/browser/android/child_process_launcher_android.h"
34 #elif defined(OS_POSIX)
35 #include "base/memory/singleton.h"
36 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
37 #include "content/browser/zygote_host/zygote_host_impl_linux.h"
38 #include "content/common/child_process_sandbox_support_impl_linux.h"
39 #endif
40
41 #if defined(OS_POSIX)
42 #include "base/posix/global_descriptors.h"
43 #endif 11 #endif
44 12
45 namespace content { 13 namespace content {
46 14
47 // Having the functionality of ChildProcessLauncher be in an internal 15 ChildProcessLauncher* ChildProcessLauncher::Create(
48 // ref counted object allows us to automatically terminate the process when the
49 // parent class destructs, while still holding on to state that we need.
50 class ChildProcessLauncher::Context
51 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
52 public:
53 Context()
54 : client_(NULL),
55 client_thread_id_(BrowserThread::UI),
56 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
57 exit_code_(RESULT_CODE_NORMAL_EXIT),
58 starting_(true)
59 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
60 , zygote_(false)
61 #endif
62 {
63 #if defined(OS_POSIX)
64 terminate_child_on_shutdown_ = !CommandLine::ForCurrentProcess()->
65 HasSwitch(switches::kChildCleanExit);
66 #else
67 terminate_child_on_shutdown_ = true;
68 #endif
69 }
70
71 void Launch(
72 #if defined(OS_WIN) 16 #if defined(OS_WIN)
73 SandboxedProcessLauncherDelegate* delegate, 17 SandboxedProcessLauncherDelegate* delegate,
74 #elif defined(OS_ANDROID)
75 int ipcfd,
76 #elif defined(OS_POSIX) 18 #elif defined(OS_POSIX)
77 bool use_zygote, 19 bool use_zygote,
78 const base::EnvironmentMap& environ, 20 const base::EnvironmentMap& environ,
79 int ipcfd, 21 int ipcfd,
80 #endif 22 #endif
81 CommandLine* cmd_line, 23 CommandLine* cmd_line,
82 int child_process_id, 24 int child_process_id,
83 Client* client) { 25 Client* client) {
84 client_ = client; 26 return new ChildProcessLauncherImpl(
85
86 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
87
88 #if defined(OS_ANDROID)
89 // We need to close the client end of the IPC channel to reliably detect
90 // child termination. We will close this fd after we create the child
91 // process which is asynchronous on Android.
92 ipcfd_ = ipcfd;
93 #endif
94 BrowserThread::PostTask(
95 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
96 base::Bind(
97 &Context::LaunchInternal,
98 make_scoped_refptr(this),
99 client_thread_id_,
100 child_process_id,
101 #if defined(OS_WIN)
102 delegate,
103 #elif defined(OS_ANDROID)
104 ipcfd,
105 #elif defined(OS_POSIX)
106 use_zygote,
107 environ,
108 ipcfd,
109 #endif
110 cmd_line));
111 }
112
113 #if defined(OS_ANDROID)
114 static void OnChildProcessStarted(
115 // |this_object| is NOT thread safe. Only use it to post a task back.
116 scoped_refptr<Context> this_object,
117 BrowserThread::ID client_thread_id,
118 const base::TimeTicks begin_launch_time,
119 base::ProcessHandle handle) {
120 RecordHistograms(begin_launch_time);
121 if (BrowserThread::CurrentlyOn(client_thread_id)) {
122 // This is always invoked on the UI thread which is commonly the
123 // |client_thread_id| so we can shortcut one PostTask.
124 this_object->Notify(handle);
125 } else {
126 BrowserThread::PostTask(
127 client_thread_id, FROM_HERE,
128 base::Bind(
129 &ChildProcessLauncher::Context::Notify,
130 this_object,
131 handle));
132 }
133 }
134 #endif
135
136 void ResetClient() {
137 // No need for locking as this function gets called on the same thread that
138 // client_ would be used.
139 CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
140 client_ = NULL;
141 }
142
143 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
144 terminate_child_on_shutdown_ = terminate_on_shutdown;
145 }
146
147 private:
148 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
149 friend class ChildProcessLauncher;
150
151 ~Context() {
152 Terminate();
153 }
154
155 static void RecordHistograms(const base::TimeTicks begin_launch_time) {
156 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
157 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
158 RecordLaunchHistograms(launch_time);
159 } else {
160 BrowserThread::PostTask(
161 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
162 base::Bind(&ChildProcessLauncher::Context::RecordLaunchHistograms,
163 launch_time));
164 }
165 }
166
167 static void RecordLaunchHistograms(const base::TimeDelta launch_time) {
168 // Log the launch time, separating out the first one (which will likely be
169 // slower due to the rest of the browser initializing at the same time).
170 static bool done_first_launch = false;
171 if (done_first_launch) {
172 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
173 } else {
174 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
175 done_first_launch = true;
176 }
177 }
178
179 static void LaunchInternal(
180 // |this_object| is NOT thread safe. Only use it to post a task back.
181 scoped_refptr<Context> this_object,
182 BrowserThread::ID client_thread_id,
183 int child_process_id,
184 #if defined(OS_WIN)
185 SandboxedProcessLauncherDelegate* delegate,
186 #elif defined(OS_ANDROID)
187 int ipcfd,
188 #elif defined(OS_POSIX)
189 bool use_zygote,
190 const base::EnvironmentMap& env,
191 int ipcfd,
192 #endif
193 CommandLine* cmd_line) {
194 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
195 base::TimeTicks begin_launch_time = base::TimeTicks::Now();
196
197 #if defined(OS_WIN)
198 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
199 base::ProcessHandle handle = StartSandboxedProcess(delegate, cmd_line);
200 #elif defined(OS_ANDROID)
201 // Android WebView runs in single process, ensure that we never get here
202 // when running in single process mode.
203 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
204
205 std::string process_type =
206 cmd_line->GetSwitchValueASCII(switches::kProcessType);
207 std::vector<FileDescriptorInfo> files_to_register;
208 files_to_register.push_back(
209 FileDescriptorInfo(kPrimaryIPCChannel,
210 base::FileDescriptor(ipcfd, false)));
211
212 GetContentClient()->browser()->
213 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id,
214 &files_to_register);
215
216 StartChildProcess(cmd_line->argv(), files_to_register,
217 base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted,
218 this_object, client_thread_id, begin_launch_time));
219
220 #elif defined(OS_POSIX)
221 base::ProcessHandle handle = base::kNullProcessHandle;
222 // We need to close the client end of the IPC channel to reliably detect
223 // child termination.
224 file_util::ScopedFD ipcfd_closer(&ipcfd);
225
226 std::string process_type =
227 cmd_line->GetSwitchValueASCII(switches::kProcessType);
228 std::vector<FileDescriptorInfo> files_to_register;
229 files_to_register.push_back(
230 FileDescriptorInfo(kPrimaryIPCChannel,
231 base::FileDescriptor(ipcfd, false)));
232
233 #if !defined(OS_MACOSX)
234 GetContentClient()->browser()->
235 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id,
236 &files_to_register);
237 if (use_zygote) {
238 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(),
239 files_to_register,
240 process_type);
241 } else
242 // Fall through to the normal posix case below when we're not zygoting.
243 #endif // !defined(OS_MACOSX)
244 {
245 // Convert FD mapping to FileHandleMappingVector
246 base::FileHandleMappingVector fds_to_map;
247 for (size_t i = 0; i < files_to_register.size(); ++i) {
248 fds_to_map.push_back(std::make_pair(
249 files_to_register[i].fd.fd,
250 files_to_register[i].id +
251 base::GlobalDescriptors::kBaseDescriptor));
252 }
253
254 #if !defined(OS_MACOSX)
255 if (process_type == switches::kRendererProcess) {
256 const int sandbox_fd =
257 RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
258 fds_to_map.push_back(std::make_pair(
259 sandbox_fd,
260 GetSandboxFD()));
261 }
262 #endif // defined(OS_MACOSX)
263
264 // Actually launch the app.
265 base::LaunchOptions options;
266 options.environ = env;
267 options.fds_to_remap = &fds_to_map;
268
269 #if defined(OS_MACOSX)
270 // Hold the MachBroker lock for the duration of LaunchProcess. The child
271 // will send its task port to the parent almost immediately after startup.
272 // The Mach message will be delivered to the parent, but updating the
273 // record of the launch will wait until after the placeholder PID is
274 // inserted below. This ensures that while the child process may send its
275 // port to the parent prior to the parent leaving LaunchProcess, the
276 // order in which the record in MachBroker is updated is correct.
277 MachBroker* broker = MachBroker::GetInstance();
278 broker->GetLock().Acquire();
279
280 // Make sure the MachBroker is running, and inform it to expect a
281 // check-in from the new process.
282 broker->EnsureRunning();
283 #endif // defined(OS_MACOSX)
284
285 bool launched = base::LaunchProcess(*cmd_line, options, &handle);
286
287 #if defined(OS_MACOSX)
288 if (launched)
289 broker->AddPlaceholderForPid(handle);
290
291 // After updating the broker, release the lock and let the child's
292 // messasge be processed on the broker's thread.
293 broker->GetLock().Release();
294 #endif // defined(OS_MACOSX)
295
296 if (!launched)
297 handle = base::kNullProcessHandle;
298 }
299 #endif // else defined(OS_POSIX)
300 #if !defined(OS_ANDROID)
301 if (handle)
302 RecordHistograms(begin_launch_time);
303 BrowserThread::PostTask(
304 client_thread_id, FROM_HERE,
305 base::Bind(
306 &Context::Notify,
307 this_object.get(),
308 #if defined(OS_POSIX) && !defined(OS_MACOSX)
309 use_zygote,
310 #endif
311 handle));
312 #endif // !defined(OS_ANDROID)
313 }
314
315 void Notify(
316 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
317 bool zygote,
318 #endif
319 base::ProcessHandle handle) {
320 #if defined(OS_ANDROID)
321 // Finally close the ipcfd
322 file_util::ScopedFD ipcfd_closer(&ipcfd_);
323 #endif
324 starting_ = false;
325 process_.set_handle(handle);
326 if (!handle)
327 LOG(ERROR) << "Failed to launch child process";
328
329 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
330 zygote_ = zygote;
331 #endif
332 if (client_) {
333 client_->OnProcessLaunched();
334 } else {
335 Terminate();
336 }
337 }
338
339 void Terminate() {
340 if (!process_.handle())
341 return;
342
343 if (!terminate_child_on_shutdown_)
344 return;
345
346 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
347 // don't this on the UI/IO threads.
348 BrowserThread::PostTask(
349 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
350 base::Bind(
351 &Context::TerminateInternal,
352 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
353 zygote_,
354 #endif
355 process_.handle()));
356 process_.set_handle(base::kNullProcessHandle);
357 }
358
359 static void SetProcessBackgrounded(base::ProcessHandle handle,
360 bool background) {
361 base::Process process(handle);
362 process.SetProcessBackgrounded(background);
363 }
364
365 static void TerminateInternal(
366 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
367 bool zygote,
368 #endif
369 base::ProcessHandle handle) {
370 #if defined(OS_ANDROID)
371 LOG(INFO) << "ChromeProcess: Stopping process with handle " << handle;
372 StopChildProcess(handle);
373 #else
374 base::Process process(handle);
375 // Client has gone away, so just kill the process. Using exit code 0
376 // means that UMA won't treat this as a crash.
377 process.Terminate(RESULT_CODE_NORMAL_EXIT);
378 // On POSIX, we must additionally reap the child.
379 #if defined(OS_POSIX)
380 #if !defined(OS_MACOSX)
381 if (zygote) {
382 // If the renderer was created via a zygote, we have to proxy the reaping
383 // through the zygote process.
384 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle);
385 } else
386 #endif // !OS_MACOSX
387 {
388 base::EnsureProcessTerminated(handle);
389 }
390 #endif // OS_POSIX
391 process.Close();
392 #endif // defined(OS_ANDROID)
393 }
394
395 Client* client_;
396 BrowserThread::ID client_thread_id_;
397 base::Process process_;
398 base::TerminationStatus termination_status_;
399 int exit_code_;
400 bool starting_;
401 // Controls whether the child process should be terminated on browser
402 // shutdown. Default behavior is to terminate the child.
403 bool terminate_child_on_shutdown_;
404 #if defined(OS_ANDROID)
405 // The fd to close after creating the process.
406 int ipcfd_;
407 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
408 bool zygote_;
409 #endif
410 };
411
412
413 ChildProcessLauncher::ChildProcessLauncher(
414 #if defined(OS_WIN)
415 SandboxedProcessLauncherDelegate* delegate,
416 #elif defined(OS_POSIX)
417 bool use_zygote,
418 const base::EnvironmentMap& environ,
419 int ipcfd,
420 #endif
421 CommandLine* cmd_line,
422 int child_process_id,
423 Client* client) {
424 context_ = new Context();
425 context_->Launch(
426 #if defined(OS_WIN) 27 #if defined(OS_WIN)
427 delegate, 28 delegate,
428 #elif defined(OS_ANDROID)
429 ipcfd,
430 #elif defined(OS_POSIX) 29 #elif defined(OS_POSIX)
431 use_zygote, 30 use_zygote,
432 environ, 31 base::EnvironmentMap& environ,
mef 2014/01/10 18:22:55 does base::EnvironmentMap& belong here?
Drew Haven 2014/01/16 02:52:05 Not at all. :)
433 ipcfd, 32 ipcfd,
434 #endif 33 #endif
435 cmd_line, 34 cmd_line,
436 child_process_id, 35 child_process_id,
437 client); 36 client);
438 } 37 }
439 38
440 ChildProcessLauncher::~ChildProcessLauncher() { 39 #if defined(OS_WIN)
441 context_->ResetClient(); 40 ChildProcessLauncher* ChildProcessLauncher::CreateElevated(
41 CommandLine* cmd_line,
42 int child_process_id,
43 Client* client) {
44 return new ChildProcessLauncherElevated(
45 cmd_line,
46 child_process_id,
47 client);
442 } 48 }
49 #endif
443 50
444 bool ChildProcessLauncher::IsStarting() { 51 } // namespace content
445 return context_->starting_;
446 }
447
448 base::ProcessHandle ChildProcessLauncher::GetHandle() {
449 DCHECK(!context_->starting_);
450 return context_->process_.handle();
451 }
452
453 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
454 bool known_dead,
455 int* exit_code) {
456 base::ProcessHandle handle = context_->process_.handle();
457 if (handle == base::kNullProcessHandle) {
458 // Process is already gone, so return the cached termination status.
459 if (exit_code)
460 *exit_code = context_->exit_code_;
461 return context_->termination_status_;
462 }
463 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
464 if (context_->zygote_) {
465 context_->termination_status_ = ZygoteHostImpl::GetInstance()->
466 GetTerminationStatus(handle, known_dead, &context_->exit_code_);
467 } else
468 #elif defined(OS_MACOSX)
469 if (known_dead) {
470 context_->termination_status_ =
471 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_);
472 } else
473 #endif
474 {
475 context_->termination_status_ =
476 base::GetTerminationStatus(handle, &context_->exit_code_);
477 }
478
479 if (exit_code)
480 *exit_code = context_->exit_code_;
481
482 // POSIX: If the process crashed, then the kernel closed the socket
483 // for it and so the child has already died by the time we get
484 // here. Since GetTerminationStatus called waitpid with WNOHANG,
485 // it'll reap the process. However, if GetTerminationStatus didn't
486 // reap the child (because it was still running), we'll need to
487 // Terminate via ProcessWatcher. So we can't close the handle here.
488 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
489 context_->process_.Close();
490
491 return context_->termination_status_;
492 }
493
494 void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
495 BrowserThread::PostTask(
496 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
497 base::Bind(
498 &ChildProcessLauncher::Context::SetProcessBackgrounded,
499 GetHandle(), background));
500 }
501
502 void ChildProcessLauncher::SetTerminateChildOnShutdown(
503 bool terminate_on_shutdown) {
504 if (context_.get())
505 context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
506 }
507
508 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698