child_process_launcher.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/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" 25 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 44 45#if defined(OS_POSIX) 46#include "base/metrics/stats_table.h" 47#include "base/posix/global_descriptors.h" 48#endif 49 50namespace content { 51 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. 55class 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 74 { 75 } 76 77 void Launch( 78 SandboxedProcessLauncherDelegate* delegate, 79 CommandLine* cmd_line, 80 int child_process_id, 81 Client* client) { 82 client_ = client; 83 84 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); 85 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)); 101 } 102 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)); 122 } 123 } 124#endif 125 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; 131 } 132 133 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) { 134 terminate_child_on_shutdown_ = terminate_on_shutdown; 135 } 136 137 private: 138 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; 139 friend class ChildProcessLauncher; 140 141 ~Context() { 142 Terminate(); 143 } 144 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)); 154 } 155 } 156 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; 166 } 167 } 168 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(); 191 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); 200 } 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())); 215 } 216#endif 217 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)); 222 223 GetContentClient()->browser()-> 224 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, 225 &files_to_register); 226 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)); 230 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); 236 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) 248 { 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)); 256 } 257 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())); 265 } 266#endif // defined(OS_MACOSX) 267 268 // Actually launch the app. 269 base::LaunchOptions options; 270 options.environ = env; 271 options.fds_to_remap = &fds_to_map; 272 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(); 283 284 // Make sure the MachBroker is running, and inform it to expect a 285 // check-in from the new process. 286 broker->EnsureRunning(); 287 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); 293 } 294#endif // defined(OS_MACOSX) 295 296 bool launched = base::LaunchProcess(*cmd_line, options, &handle); 297 if (!launched) 298 handle = base::kNullProcessHandle; 299 300#if defined(OS_MACOSX) 301 if (ShouldEnableBootstrapSandbox() && 302 bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) { 303 GetBootstrapSandbox()->FinishedFork(handle); 304 } 305 306 if (launched) 307 broker->AddPlaceholderForPid(handle); 308 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) 313 } 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) 328 } 329 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"; 343 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(); 352 } 353 } else { 354 Terminate(); 355 } 356 } 357 358 void Terminate() { 359 if (!process_.handle()) 360 return; 361 362 if (!terminate_child_on_shutdown_) 363 return; 364 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); 376 } 377 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 385 } 386 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 409 { 410 base::EnsureProcessTerminated(handle); 411 } 412#endif // OS_POSIX 413 process.Close(); 414#endif // defined(OS_ANDROID) 415 } 416 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 432}; 433 434 435ChildProcessLauncher::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); 446} 447 448ChildProcessLauncher::~ChildProcessLauncher() { 449 context_->ResetClient(); 450} 451 452bool ChildProcessLauncher::IsStarting() { 453 return context_->starting_; 454} 455 456base::ProcessHandle ChildProcessLauncher::GetHandle() { 457 DCHECK(!context_->starting_); 458 return context_->process_.handle(); 459} 460 461base::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_; 470 } 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 489 { 490#endif 491 context_->termination_status_ = 492 base::GetTerminationStatus(handle, &context_->exit_code_); 493 } 494 495 if (exit_code) 496 *exit_code = context_->exit_code_; 497 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(); 506 507 return context_->termination_status_; 508} 509 510void ChildProcessLauncher::SetProcessBackgrounded(bool background) { 511 BrowserThread::PostTask( 512 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 513 base::Bind( 514 &ChildProcessLauncher::Context::SetProcessBackgrounded, 515 GetHandle(), background)); 516} 517 518void ChildProcessLauncher::SetTerminateChildOnShutdown( 519 bool terminate_on_shutdown) { 520 if (context_.get()) 521 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); 522} 523 524} // namespace content 525