child_process_launcher.cc revision 4ad1aa43a48567659193a298fad74f55e00b3dd9
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/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" 41#endif 42 43#if defined(OS_POSIX) 44#include "base/metrics/stats_table.h" 45#include "base/posix/global_descriptors.h" 46#endif 47 48namespace content { 49 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. 53class ChildProcessLauncher::Context 54 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { 55 public: 56 Context() 57 : client_(NULL), 58 client_thread_id_(BrowserThread::UI), 59 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), 60 exit_code_(RESULT_CODE_NORMAL_EXIT), 61 starting_(true), 62 terminate_child_on_shutdown_(true) 63#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 64 , zygote_(false) 65#endif 66 { 67 } 68 69 void Launch( 70 SandboxedProcessLauncherDelegate* delegate, 71 CommandLine* cmd_line, 72 int child_process_id, 73 Client* client) { 74 client_ = client; 75 76 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); 77 78#if defined(OS_ANDROID) 79 // We need to close the client end of the IPC channel to reliably detect 80 // child termination. We will close this fd after we create the child 81 // process which is asynchronous on Android. 82 ipcfd_ = delegate->GetIpcFd(); 83#endif 84 BrowserThread::PostTask( 85 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 86 base::Bind( 87 &Context::LaunchInternal, 88 make_scoped_refptr(this), 89 client_thread_id_, 90 child_process_id, 91 delegate, 92 cmd_line)); 93 } 94 95#if defined(OS_ANDROID) 96 static void OnChildProcessStarted( 97 // |this_object| is NOT thread safe. Only use it to post a task back. 98 scoped_refptr<Context> this_object, 99 BrowserThread::ID client_thread_id, 100 const base::TimeTicks begin_launch_time, 101 base::ProcessHandle handle) { 102 RecordHistograms(begin_launch_time); 103 if (BrowserThread::CurrentlyOn(client_thread_id)) { 104 // This is always invoked on the UI thread which is commonly the 105 // |client_thread_id| so we can shortcut one PostTask. 106 this_object->Notify(handle); 107 } else { 108 BrowserThread::PostTask( 109 client_thread_id, FROM_HERE, 110 base::Bind( 111 &ChildProcessLauncher::Context::Notify, 112 this_object, 113 handle)); 114 } 115 } 116#endif 117 118 void ResetClient() { 119 // No need for locking as this function gets called on the same thread that 120 // client_ would be used. 121 CHECK(BrowserThread::CurrentlyOn(client_thread_id_)); 122 client_ = NULL; 123 } 124 125 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) { 126 terminate_child_on_shutdown_ = terminate_on_shutdown; 127 } 128 129 private: 130 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; 131 friend class ChildProcessLauncher; 132 133 ~Context() { 134 Terminate(); 135 } 136 137 static void RecordHistograms(const base::TimeTicks begin_launch_time) { 138 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; 139 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) { 140 RecordLaunchHistograms(launch_time); 141 } else { 142 BrowserThread::PostTask( 143 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 144 base::Bind(&ChildProcessLauncher::Context::RecordLaunchHistograms, 145 launch_time)); 146 } 147 } 148 149 static void RecordLaunchHistograms(const base::TimeDelta launch_time) { 150 // Log the launch time, separating out the first one (which will likely be 151 // slower due to the rest of the browser initializing at the same time). 152 static bool done_first_launch = false; 153 if (done_first_launch) { 154 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); 155 } else { 156 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); 157 done_first_launch = true; 158 } 159 } 160 161 static void LaunchInternal( 162 // |this_object| is NOT thread safe. Only use it to post a task back. 163 scoped_refptr<Context> this_object, 164 BrowserThread::ID client_thread_id, 165 int child_process_id, 166 SandboxedProcessLauncherDelegate* delegate, 167 CommandLine* cmd_line) { 168 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); 169#if defined(OS_WIN) 170 bool launch_elevated = delegate->ShouldLaunchElevated(); 171#elif defined(OS_ANDROID) 172 int ipcfd = delegate->GetIpcFd(); 173#elif defined(OS_MACOSX) 174 base::EnvironmentMap env = delegate->GetEnvironment(); 175 int ipcfd = delegate->GetIpcFd(); 176#elif defined(OS_POSIX) 177 bool use_zygote = delegate->ShouldUseZygote(); 178 base::EnvironmentMap env = delegate->GetEnvironment(); 179 int ipcfd = delegate->GetIpcFd(); 180#endif 181 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); 182 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); 183 184#if defined(OS_WIN) 185 base::ProcessHandle handle = base::kNullProcessHandle; 186 if (launch_elevated) { 187 base::LaunchOptions options; 188 options.start_hidden = true; 189 base::LaunchElevatedProcess(*cmd_line, options, &handle); 190 } else { 191 handle = StartSandboxedProcess(delegate, cmd_line); 192 } 193#elif defined(OS_POSIX) 194 std::string process_type = 195 cmd_line->GetSwitchValueASCII(switches::kProcessType); 196 std::vector<FileDescriptorInfo> files_to_register; 197 files_to_register.push_back( 198 FileDescriptorInfo(kPrimaryIPCChannel, 199 base::FileDescriptor(ipcfd, false))); 200 base::StatsTable* stats_table = base::StatsTable::current(); 201 if (stats_table && 202 base::SharedMemory::IsHandleValid( 203 stats_table->GetSharedMemoryHandle())) { 204 files_to_register.push_back( 205 FileDescriptorInfo(kStatsTableSharedMemFd, 206 stats_table->GetSharedMemoryHandle())); 207 } 208#endif 209 210#if defined(OS_ANDROID) 211 // Android WebView runs in single process, ensure that we never get here 212 // when running in single process mode. 213 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); 214 215 GetContentClient()->browser()-> 216 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, 217 &files_to_register); 218 219 StartChildProcess(cmd_line->argv(), child_process_id, files_to_register, 220 base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted, 221 this_object, client_thread_id, begin_launch_time)); 222 223#elif defined(OS_POSIX) 224 base::ProcessHandle handle = base::kNullProcessHandle; 225 // We need to close the client end of the IPC channel to reliably detect 226 // child termination. 227 base::ScopedFD ipcfd_closer(ipcfd); 228 229#if !defined(OS_MACOSX) 230 GetContentClient()->browser()-> 231 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, 232 &files_to_register); 233 if (use_zygote) { 234 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(), 235 files_to_register, 236 process_type); 237 } else 238 // Fall through to the normal posix case below when we're not zygoting. 239#endif // !defined(OS_MACOSX) 240 { 241 // Convert FD mapping to FileHandleMappingVector 242 base::FileHandleMappingVector fds_to_map; 243 for (size_t i = 0; i < files_to_register.size(); ++i) { 244 fds_to_map.push_back(std::make_pair( 245 files_to_register[i].fd.fd, 246 files_to_register[i].id + 247 base::GlobalDescriptors::kBaseDescriptor)); 248 } 249 250#if !defined(OS_MACOSX) 251 if (process_type == switches::kRendererProcess) { 252 const int sandbox_fd = 253 RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); 254 fds_to_map.push_back(std::make_pair( 255 sandbox_fd, 256 GetSandboxFD())); 257 } 258#endif // defined(OS_MACOSX) 259 260 // Actually launch the app. 261 base::LaunchOptions options; 262 options.environ = env; 263 options.fds_to_remap = &fds_to_map; 264 265#if defined(OS_MACOSX) 266 // Hold the MachBroker lock for the duration of LaunchProcess. The child 267 // will send its task port to the parent almost immediately after startup. 268 // The Mach message will be delivered to the parent, but updating the 269 // record of the launch will wait until after the placeholder PID is 270 // inserted below. This ensures that while the child process may send its 271 // port to the parent prior to the parent leaving LaunchProcess, the 272 // order in which the record in MachBroker is updated is correct. 273 MachBroker* broker = MachBroker::GetInstance(); 274 broker->GetLock().Acquire(); 275 276 // Make sure the MachBroker is running, and inform it to expect a 277 // check-in from the new process. 278 broker->EnsureRunning(); 279#endif // defined(OS_MACOSX) 280 281 bool launched = base::LaunchProcess(*cmd_line, options, &handle); 282 283#if defined(OS_MACOSX) 284 if (launched) 285 broker->AddPlaceholderForPid(handle); 286 287 // After updating the broker, release the lock and let the child's 288 // messasge be processed on the broker's thread. 289 broker->GetLock().Release(); 290#endif // defined(OS_MACOSX) 291 292 if (!launched) 293 handle = base::kNullProcessHandle; 294 } 295#endif // else defined(OS_POSIX) 296#if !defined(OS_ANDROID) 297 if (handle) 298 RecordHistograms(begin_launch_time); 299 BrowserThread::PostTask( 300 client_thread_id, FROM_HERE, 301 base::Bind( 302 &Context::Notify, 303 this_object.get(), 304#if defined(OS_POSIX) && !defined(OS_MACOSX) 305 use_zygote, 306#endif 307 handle)); 308#endif // !defined(OS_ANDROID) 309 } 310 311 void Notify( 312#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 313 bool zygote, 314#endif 315 base::ProcessHandle handle) { 316#if defined(OS_ANDROID) 317 // Finally close the ipcfd 318 base::ScopedFD ipcfd_closer(ipcfd_); 319#endif 320 starting_ = false; 321 process_.set_handle(handle); 322 if (!handle) 323 LOG(ERROR) << "Failed to launch child process"; 324 325#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 326 zygote_ = zygote; 327#endif 328 if (client_) { 329 if (handle) { 330 client_->OnProcessLaunched(); 331 } else { 332 client_->OnProcessLaunchFailed(); 333 } 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 VLOG(0) << "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 413ChildProcessLauncher::ChildProcessLauncher( 414 SandboxedProcessLauncherDelegate* delegate, 415 CommandLine* cmd_line, 416 int child_process_id, 417 Client* client) { 418 context_ = new Context(); 419 context_->Launch( 420 delegate, 421 cmd_line, 422 child_process_id, 423 client); 424} 425 426ChildProcessLauncher::~ChildProcessLauncher() { 427 context_->ResetClient(); 428} 429 430bool ChildProcessLauncher::IsStarting() { 431 return context_->starting_; 432} 433 434base::ProcessHandle ChildProcessLauncher::GetHandle() { 435 DCHECK(!context_->starting_); 436 return context_->process_.handle(); 437} 438 439base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( 440 bool known_dead, 441 int* exit_code) { 442 base::ProcessHandle handle = context_->process_.handle(); 443 if (handle == base::kNullProcessHandle) { 444 // Process is already gone, so return the cached termination status. 445 if (exit_code) 446 *exit_code = context_->exit_code_; 447 return context_->termination_status_; 448 } 449#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 450 if (context_->zygote_) { 451 context_->termination_status_ = ZygoteHostImpl::GetInstance()-> 452 GetTerminationStatus(handle, known_dead, &context_->exit_code_); 453 } else if (known_dead) { 454 context_->termination_status_ = 455 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); 456 } else { 457#elif defined(OS_MACOSX) 458 if (known_dead) { 459 context_->termination_status_ = 460 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); 461 } else { 462#elif defined(OS_ANDROID) 463 if (IsChildProcessOomProtected(handle)) { 464 context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED; 465 } else { 466#else 467 { 468#endif 469 context_->termination_status_ = 470 base::GetTerminationStatus(handle, &context_->exit_code_); 471 } 472 473 if (exit_code) 474 *exit_code = context_->exit_code_; 475 476 // POSIX: If the process crashed, then the kernel closed the socket 477 // for it and so the child has already died by the time we get 478 // here. Since GetTerminationStatus called waitpid with WNOHANG, 479 // it'll reap the process. However, if GetTerminationStatus didn't 480 // reap the child (because it was still running), we'll need to 481 // Terminate via ProcessWatcher. So we can't close the handle here. 482 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) 483 context_->process_.Close(); 484 485 return context_->termination_status_; 486} 487 488void ChildProcessLauncher::SetProcessBackgrounded(bool background) { 489 BrowserThread::PostTask( 490 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 491 base::Bind( 492 &ChildProcessLauncher::Context::SetProcessBackgrounded, 493 GetHandle(), background)); 494} 495 496void ChildProcessLauncher::SetTerminateChildOnShutdown( 497 bool terminate_on_shutdown) { 498 if (context_.get()) 499 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); 500} 501 502} // namespace content 503