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.
5 #include "content/browser/child_process_launcher.h"
7 #include <utility> // For std::pair.
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/files/scoped_file.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/metrics/histogram.h"
16 #include "base/process/process.h"
17 #include "base/synchronization/lock.h"
18 #include "base/threading/thread.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/content_browser_client.h"
21 #include "content/public/common/content_descriptors.h"
22 #include "content/public/common/content_switches.h"
23 #include "content/public/common/result_codes.h"
24 #include "content/public/common/sandboxed_process_launcher_delegate.h"
27 #include "base/files/file_path.h"
28 #include "content/common/sandbox_win.h"
29 #include "content/public/common/sandbox_init.h"
30 #elif defined(OS_MACOSX)
31 #include "content/browser/mach_broker_mac.h"
32 #elif defined(OS_ANDROID)
33 #include "base/android/jni_android.h"
34 #include "content/browser/android/child_process_launcher_android.h"
35 #elif defined(OS_POSIX)
36 #include "base/memory/shared_memory.h"
37 #include "base/memory/singleton.h"
38 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
39 #include "content/browser/zygote_host/zygote_host_impl_linux.h"
40 #include "content/common/child_process_sandbox_support_impl_linux.h"
44 #include "base/metrics/stats_table.h"
45 #include "base/posix/global_descriptors.h"
50 // Having the functionality of ChildProcessLauncher be in an internal
51 // ref counted object allows us to automatically terminate the process when the
52 // parent class destructs, while still holding on to state that we need.
53 class ChildProcessLauncher::Context
54 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
58 client_thread_id_(BrowserThread::UI),
59 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
60 exit_code_(RESULT_CODE_NORMAL_EXIT),
62 // TODO(earthdok): Re-enable on CrOS http://crbug.com/360622
63 #if (defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
64 defined(THREAD_SANITIZER)) && !defined(OS_CHROMEOS)
65 terminate_child_on_shutdown_(false)
67 terminate_child_on_shutdown_(true)
69 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
76 SandboxedProcessLauncherDelegate* delegate,
77 CommandLine* cmd_line,
82 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
84 #if defined(OS_ANDROID)
85 // We need to close the client end of the IPC channel to reliably detect
86 // child termination. We will close this fd after we create the child
87 // process which is asynchronous on Android.
88 ipcfd_ = delegate->GetIpcFd();
90 BrowserThread::PostTask(
91 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
93 &Context::LaunchInternal,
94 make_scoped_refptr(this),
101 #if defined(OS_ANDROID)
102 static void OnChildProcessStarted(
103 // |this_object| is NOT thread safe. Only use it to post a task back.
104 scoped_refptr<Context> this_object,
105 BrowserThread::ID client_thread_id,
106 const base::TimeTicks begin_launch_time,
107 base::ProcessHandle handle) {
108 RecordHistograms(begin_launch_time);
109 if (BrowserThread::CurrentlyOn(client_thread_id)) {
110 // This is always invoked on the UI thread which is commonly the
111 // |client_thread_id| so we can shortcut one PostTask.
112 this_object->Notify(handle);
114 BrowserThread::PostTask(
115 client_thread_id, FROM_HERE,
117 &ChildProcessLauncher::Context::Notify,
125 // No need for locking as this function gets called on the same thread that
126 // client_ would be used.
127 CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
131 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
132 terminate_child_on_shutdown_ = terminate_on_shutdown;
136 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
137 friend class ChildProcessLauncher;
143 static void RecordHistograms(const base::TimeTicks begin_launch_time) {
144 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
145 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
146 RecordLaunchHistograms(launch_time);
148 BrowserThread::PostTask(
149 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
150 base::Bind(&ChildProcessLauncher::Context::RecordLaunchHistograms,
155 static void RecordLaunchHistograms(const base::TimeDelta launch_time) {
156 // Log the launch time, separating out the first one (which will likely be
157 // slower due to the rest of the browser initializing at the same time).
158 static bool done_first_launch = false;
159 if (done_first_launch) {
160 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
162 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
163 done_first_launch = true;
167 static void LaunchInternal(
168 // |this_object| is NOT thread safe. Only use it to post a task back.
169 scoped_refptr<Context> this_object,
170 BrowserThread::ID client_thread_id,
171 int child_process_id,
172 SandboxedProcessLauncherDelegate* delegate,
173 CommandLine* cmd_line) {
174 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
176 bool launch_elevated = delegate->ShouldLaunchElevated();
177 #elif defined(OS_ANDROID)
178 int ipcfd = delegate->GetIpcFd();
179 #elif defined(OS_MACOSX)
180 base::EnvironmentMap env = delegate->GetEnvironment();
181 int ipcfd = delegate->GetIpcFd();
182 #elif defined(OS_POSIX)
183 bool use_zygote = delegate->ShouldUseZygote();
184 base::EnvironmentMap env = delegate->GetEnvironment();
185 int ipcfd = delegate->GetIpcFd();
187 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
188 base::TimeTicks begin_launch_time = base::TimeTicks::Now();
191 base::ProcessHandle handle = base::kNullProcessHandle;
192 if (launch_elevated) {
193 base::LaunchOptions options;
194 options.start_hidden = true;
195 base::LaunchElevatedProcess(*cmd_line, options, &handle);
197 handle = StartSandboxedProcess(delegate, cmd_line);
199 #elif defined(OS_POSIX)
200 std::string process_type =
201 cmd_line->GetSwitchValueASCII(switches::kProcessType);
202 std::vector<FileDescriptorInfo> files_to_register;
203 files_to_register.push_back(
204 FileDescriptorInfo(kPrimaryIPCChannel,
205 base::FileDescriptor(ipcfd, false)));
206 base::StatsTable* stats_table = base::StatsTable::current();
208 base::SharedMemory::IsHandleValid(
209 stats_table->GetSharedMemoryHandle())) {
210 files_to_register.push_back(
211 FileDescriptorInfo(kStatsTableSharedMemFd,
212 stats_table->GetSharedMemoryHandle()));
216 #if defined(OS_ANDROID)
217 // Android WebView runs in single process, ensure that we never get here
218 // when running in single process mode.
219 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
221 GetContentClient()->browser()->
222 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id,
225 StartChildProcess(cmd_line->argv(), child_process_id, files_to_register,
226 base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted,
227 this_object, client_thread_id, begin_launch_time));
229 #elif defined(OS_POSIX)
230 base::ProcessHandle handle = base::kNullProcessHandle;
231 // We need to close the client end of the IPC channel to reliably detect
232 // child termination.
233 base::ScopedFD ipcfd_closer(ipcfd);
235 #if !defined(OS_MACOSX)
236 GetContentClient()->browser()->
237 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id,
240 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(),
244 // Fall through to the normal posix case below when we're not zygoting.
245 #endif // !defined(OS_MACOSX)
247 // Convert FD mapping to FileHandleMappingVector
248 base::FileHandleMappingVector fds_to_map;
249 for (size_t i = 0; i < files_to_register.size(); ++i) {
250 fds_to_map.push_back(std::make_pair(
251 files_to_register[i].fd.fd,
252 files_to_register[i].id +
253 base::GlobalDescriptors::kBaseDescriptor));
256 #if !defined(OS_MACOSX)
257 if (process_type == switches::kRendererProcess) {
258 const int sandbox_fd =
259 RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
260 fds_to_map.push_back(std::make_pair(
264 #endif // defined(OS_MACOSX)
266 // Actually launch the app.
267 base::LaunchOptions options;
268 options.environ = env;
269 options.fds_to_remap = &fds_to_map;
271 #if defined(OS_MACOSX)
272 // Hold the MachBroker lock for the duration of LaunchProcess. The child
273 // will send its task port to the parent almost immediately after startup.
274 // The Mach message will be delivered to the parent, but updating the
275 // record of the launch will wait until after the placeholder PID is
276 // inserted below. This ensures that while the child process may send its
277 // port to the parent prior to the parent leaving LaunchProcess, the
278 // order in which the record in MachBroker is updated is correct.
279 MachBroker* broker = MachBroker::GetInstance();
280 broker->GetLock().Acquire();
282 // Make sure the MachBroker is running, and inform it to expect a
283 // check-in from the new process.
284 broker->EnsureRunning();
285 #endif // defined(OS_MACOSX)
287 bool launched = base::LaunchProcess(*cmd_line, options, &handle);
289 #if defined(OS_MACOSX)
291 broker->AddPlaceholderForPid(handle);
293 // After updating the broker, release the lock and let the child's
294 // messasge be processed on the broker's thread.
295 broker->GetLock().Release();
296 #endif // defined(OS_MACOSX)
299 handle = base::kNullProcessHandle;
301 #endif // else defined(OS_POSIX)
302 #if !defined(OS_ANDROID)
304 RecordHistograms(begin_launch_time);
305 BrowserThread::PostTask(
306 client_thread_id, FROM_HERE,
310 #if defined(OS_POSIX) && !defined(OS_MACOSX)
314 #endif // !defined(OS_ANDROID)
318 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
321 base::ProcessHandle handle) {
322 #if defined(OS_ANDROID)
323 // Finally close the ipcfd
324 base::ScopedFD ipcfd_closer(ipcfd_);
327 process_.set_handle(handle);
329 LOG(ERROR) << "Failed to launch child process";
331 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
336 client_->OnProcessLaunched();
338 client_->OnProcessLaunchFailed();
346 if (!process_.handle())
349 if (!terminate_child_on_shutdown_)
352 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
353 // don't this on the UI/IO threads.
354 BrowserThread::PostTask(
355 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
357 &Context::TerminateInternal,
358 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
362 process_.set_handle(base::kNullProcessHandle);
365 static void SetProcessBackgrounded(base::ProcessHandle handle,
367 base::Process process(handle);
368 process.SetProcessBackgrounded(background);
369 #if defined(OS_ANDROID)
370 SetChildProcessInForeground(handle, !background);
374 static void TerminateInternal(
375 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
378 base::ProcessHandle handle) {
379 #if defined(OS_ANDROID)
380 VLOG(0) << "ChromeProcess: Stopping process with handle " << handle;
381 StopChildProcess(handle);
383 base::Process process(handle);
384 // Client has gone away, so just kill the process. Using exit code 0
385 // means that UMA won't treat this as a crash.
386 process.Terminate(RESULT_CODE_NORMAL_EXIT);
387 // On POSIX, we must additionally reap the child.
388 #if defined(OS_POSIX)
389 #if !defined(OS_MACOSX)
391 // If the renderer was created via a zygote, we have to proxy the reaping
392 // through the zygote process.
393 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle);
397 base::EnsureProcessTerminated(handle);
401 #endif // defined(OS_ANDROID)
405 BrowserThread::ID client_thread_id_;
406 base::Process process_;
407 base::TerminationStatus termination_status_;
410 // Controls whether the child process should be terminated on browser
411 // shutdown. Default behavior is to terminate the child.
412 bool terminate_child_on_shutdown_;
413 #if defined(OS_ANDROID)
414 // The fd to close after creating the process.
416 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
422 ChildProcessLauncher::ChildProcessLauncher(
423 SandboxedProcessLauncherDelegate* delegate,
424 CommandLine* cmd_line,
425 int child_process_id,
427 context_ = new Context();
435 ChildProcessLauncher::~ChildProcessLauncher() {
436 context_->ResetClient();
439 bool ChildProcessLauncher::IsStarting() {
440 return context_->starting_;
443 base::ProcessHandle ChildProcessLauncher::GetHandle() {
444 DCHECK(!context_->starting_);
445 return context_->process_.handle();
448 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
451 base::ProcessHandle handle = context_->process_.handle();
452 if (handle == base::kNullProcessHandle) {
453 // Process is already gone, so return the cached termination status.
455 *exit_code = context_->exit_code_;
456 return context_->termination_status_;
458 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
459 if (context_->zygote_) {
460 context_->termination_status_ = ZygoteHostImpl::GetInstance()->
461 GetTerminationStatus(handle, known_dead, &context_->exit_code_);
462 } else if (known_dead) {
463 context_->termination_status_ =
464 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_);
466 #elif defined(OS_MACOSX)
468 context_->termination_status_ =
469 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_);
471 #elif defined(OS_ANDROID)
472 if (IsChildProcessOomProtected(handle)) {
473 context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
478 context_->termination_status_ =
479 base::GetTerminationStatus(handle, &context_->exit_code_);
483 *exit_code = context_->exit_code_;
485 // POSIX: If the process crashed, then the kernel closed the socket
486 // for it and so the child has already died by the time we get
487 // here. Since GetTerminationStatus called waitpid with WNOHANG,
488 // it'll reap the process. However, if GetTerminationStatus didn't
489 // reap the child (because it was still running), we'll need to
490 // Terminate via ProcessWatcher. So we can't close the handle here.
491 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
492 context_->process_.Close();
494 return context_->termination_status_;
497 void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
498 BrowserThread::PostTask(
499 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
501 &ChildProcessLauncher::Context::SetProcessBackgrounded,
502 GetHandle(), background));
505 void ChildProcessLauncher::SetTerminateChildOnShutdown(
506 bool terminate_on_shutdown) {
508 context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
511 } // namespace content