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