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