Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/child_process_launcher.h" | 5 #include "content/browser/child_process_launcher.h" |
| 6 | 6 |
| 7 #include <memory> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/bind.h" | 7 #include "base/bind.h" |
| 11 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 12 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
| 13 #include "base/i18n/icu_util.h" | 10 #include "base/i18n/icu_util.h" |
| 14 #include "base/logging.h" | 11 #include "base/logging.h" |
| 15 #include "base/metrics/field_trial.h" | |
| 16 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/process/launch.h" | 13 #include "base/process/launch.h" |
| 18 #include "base/process/process.h" | |
| 19 #include "base/strings/string_number_conversions.h" | |
| 20 #include "base/synchronization/lock.h" | |
| 21 #include "base/threading/thread.h" | |
| 22 #include "build/build_config.h" | 14 #include "build/build_config.h" |
| 23 #include "content/public/browser/content_browser_client.h" | |
| 24 #include "content/public/common/content_descriptors.h" | |
| 25 #include "content/public/common/content_switches.h" | 15 #include "content/public/common/content_switches.h" |
| 26 #include "content/public/common/result_codes.h" | 16 #include "content/public/common/result_codes.h" |
| 27 #include "content/public/common/sandboxed_process_launcher_delegate.h" | 17 #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| 28 #include "mojo/edk/embedder/embedder.h" | |
| 29 #include "mojo/edk/embedder/named_platform_channel_pair.h" | |
| 30 #include "mojo/edk/embedder/platform_channel_pair.h" | 18 #include "mojo/edk/embedder/platform_channel_pair.h" |
| 31 #include "mojo/edk/embedder/scoped_platform_handle.h" | |
| 32 #include "ppapi/features/features.h" | |
| 33 | |
| 34 #if defined(OS_WIN) | |
| 35 #include "base/files/file_path.h" | |
| 36 #include "base/win/scoped_handle.h" | |
| 37 #include "base/win/win_util.h" | |
| 38 #include "content/common/sandbox_win.h" | |
| 39 #include "content/public/common/sandbox_init.h" | |
| 40 #include "sandbox/win/src/sandbox_types.h" | |
| 41 #elif defined(OS_MACOSX) | |
| 42 #include "content/browser/bootstrap_sandbox_manager_mac.h" | |
| 43 #include "content/browser/mach_broker_mac.h" | |
| 44 #include "sandbox/mac/bootstrap_sandbox.h" | |
| 45 #include "sandbox/mac/pre_exec_delegate.h" | |
| 46 #elif defined(OS_ANDROID) | |
| 47 #include "base/android/jni_android.h" | |
| 48 #include "content/browser/android/child_process_launcher_android.h" | |
| 49 #elif defined(OS_POSIX) | |
| 50 #include "base/memory/singleton.h" | |
| 51 #include "content/browser/renderer_host/render_sandbox_host_linux.h" | |
| 52 #include "content/browser/zygote_host/zygote_communication_linux.h" | |
| 53 #include "content/browser/zygote_host/zygote_host_impl_linux.h" | |
| 54 #include "content/common/child_process_sandbox_support_impl_linux.h" | |
| 55 #include "content/public/browser/zygote_handle_linux.h" | |
| 56 #endif | |
| 57 | |
| 58 #if defined(OS_POSIX) | |
| 59 #include "base/posix/global_descriptors.h" | |
| 60 #include "content/browser/file_descriptor_info_impl.h" | |
| 61 #include "gin/v8_initializer.h" | |
| 62 #endif | |
| 63 | 19 |
| 64 namespace content { | 20 namespace content { |
| 65 | 21 |
| 66 namespace { | 22 namespace { |
| 67 | 23 |
| 68 typedef base::Callback<void(ZygoteHandle, | |
| 69 #if defined(OS_ANDROID) | |
| 70 base::ScopedFD, | |
| 71 #endif | |
| 72 base::Process, | |
| 73 int)> | |
| 74 NotifyCallback; | |
| 75 | |
| 76 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { | 24 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { |
| 77 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 25 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
| 78 // Log the launch time, separating out the first one (which will likely be | 26 // Log the launch time, separating out the first one (which will likely be |
| 79 // slower due to the rest of the browser initializing at the same time). | 27 // slower due to the rest of the browser initializing at the same time). |
| 80 static bool done_first_launch = false; | 28 static bool done_first_launch = false; |
| 81 if (done_first_launch) { | 29 if (done_first_launch) { |
| 82 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); | 30 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); |
| 83 } else { | 31 } else { |
| 84 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); | 32 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); |
| 85 done_first_launch = true; | 33 done_first_launch = true; |
| 86 } | 34 } |
| 87 } | 35 } |
| 88 | 36 |
| 89 #if defined(OS_ANDROID) | |
| 90 // TODO(sievers): Remove this by defining better what happens on what | |
| 91 // thread in the corresponding Java code. | |
| 92 void OnChildProcessStartedAndroid(const NotifyCallback& callback, | |
| 93 BrowserThread::ID client_thread_id, | |
| 94 const base::TimeTicks begin_launch_time, | |
| 95 base::ScopedFD mojo_fd, | |
| 96 base::ProcessHandle handle) { | |
| 97 int launch_result = (handle == base::kNullProcessHandle) | |
| 98 ? LAUNCH_RESULT_FAILURE | |
| 99 : LAUNCH_RESULT_SUCCESS; | |
| 100 // This can be called on the launcher thread or UI thread. | |
| 101 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; | |
| 102 BrowserThread::PostTask( | |
| 103 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 104 base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); | |
| 105 | |
| 106 base::Closure callback_on_client_thread( | |
| 107 base::Bind(callback, nullptr, base::Passed(&mojo_fd), | |
| 108 base::Passed(base::Process(handle)), launch_result)); | |
| 109 if (BrowserThread::CurrentlyOn(client_thread_id)) { | |
| 110 callback_on_client_thread.Run(); | |
| 111 } else { | |
| 112 BrowserThread::PostTask( | |
| 113 client_thread_id, FROM_HERE, callback_on_client_thread); | |
| 114 } | |
| 115 } | |
| 116 #endif | |
| 117 | |
| 118 void LaunchOnLauncherThread( | |
| 119 const NotifyCallback& callback, | |
| 120 BrowserThread::ID client_thread_id, | |
| 121 int child_process_id, | |
| 122 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate, | |
| 123 mojo::edk::ScopedPlatformHandle client_handle, | |
| 124 std::unique_ptr<base::CommandLine> cmd_line) { | |
| 125 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | |
| 126 #if !defined(OS_ANDROID) | |
| 127 ZygoteHandle zygote = nullptr; | |
| 128 int launch_result = LAUNCH_RESULT_FAILURE; | |
| 129 #endif | |
| 130 #if defined(OS_WIN) | |
| 131 bool launch_elevated = delegate->ShouldLaunchElevated(); | |
| 132 #elif defined(OS_MACOSX) | |
| 133 base::EnvironmentMap env = delegate->GetEnvironment(); | |
| 134 #elif defined(OS_POSIX) && !defined(OS_ANDROID) | |
| 135 base::EnvironmentMap env = delegate->GetEnvironment(); | |
| 136 #endif | |
| 137 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); | |
| 138 | |
| 139 base::Process process; | |
| 140 #if defined(OS_WIN) | |
| 141 if (launch_elevated) { | |
| 142 // When establishing a Mojo connection, the pipe path has already been added | |
| 143 // to the command line. | |
| 144 base::LaunchOptions options; | |
| 145 options.start_hidden = true; | |
| 146 process = base::LaunchElevatedProcess(*cmd_line, options); | |
| 147 } else { | |
| 148 base::HandlesToInheritVector handles; | |
| 149 handles.push_back(client_handle.get().handle); | |
| 150 base::FieldTrialList::AppendFieldTrialHandleIfNeeded(&handles); | |
| 151 cmd_line->AppendSwitchASCII( | |
| 152 mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch, | |
| 153 base::UintToString(base::win::HandleToUint32(handles[0]))); | |
| 154 launch_result = StartSandboxedProcess( | |
| 155 delegate.get(), cmd_line.get(), handles, &process); | |
| 156 } | |
| 157 #elif defined(OS_POSIX) | |
| 158 std::string process_type = | |
| 159 cmd_line->GetSwitchValueASCII(switches::kProcessType); | |
| 160 std::unique_ptr<FileDescriptorInfo> files_to_register( | |
| 161 FileDescriptorInfoImpl::Create()); | |
| 162 | |
| 163 base::ScopedFD mojo_fd(client_handle.release().handle); | |
| 164 DCHECK(mojo_fd.is_valid()); | |
| 165 | |
| 166 int field_trial_handle = base::FieldTrialList::GetFieldTrialHandle(); | |
| 167 if (field_trial_handle != base::kInvalidPlatformFile) | |
| 168 files_to_register->Share(kFieldTrialDescriptor, field_trial_handle); | |
| 169 #if defined(OS_ANDROID) | |
| 170 files_to_register->Share(kMojoIPCChannel, mojo_fd.get()); | |
| 171 #else | |
| 172 files_to_register->Transfer(kMojoIPCChannel, std::move(mojo_fd)); | |
| 173 #endif | |
| 174 #endif | |
| 175 | |
| 176 #if defined(OS_POSIX) && !defined(OS_MACOSX) | |
| 177 GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess( | |
| 178 *cmd_line, child_process_id, files_to_register.get()); | |
| 179 #if defined(V8_USE_EXTERNAL_STARTUP_DATA) | |
| 180 bool snapshot_loaded = false; | |
| 181 base::MemoryMappedFile::Region region; | |
| 182 #if defined(OS_ANDROID) | |
| 183 auto maybe_register = [®ion, &files_to_register](int key, int fd) { | |
| 184 if (fd != -1) | |
| 185 files_to_register->ShareWithRegion(key, fd, region); | |
| 186 }; | |
| 187 maybe_register( | |
| 188 kV8NativesDataDescriptor, | |
| 189 gin::V8Initializer::GetOpenNativesFileForChildProcesses(®ion)); | |
| 190 maybe_register( | |
| 191 kV8SnapshotDataDescriptor32, | |
| 192 gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, true)); | |
| 193 maybe_register( | |
| 194 kV8SnapshotDataDescriptor64, | |
| 195 gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, false)); | |
| 196 | |
| 197 snapshot_loaded = true; | |
| 198 #else | |
| 199 base::PlatformFile natives_pf = | |
| 200 gin::V8Initializer::GetOpenNativesFileForChildProcesses(®ion); | |
| 201 DCHECK_GE(natives_pf, 0); | |
| 202 files_to_register->ShareWithRegion( | |
| 203 kV8NativesDataDescriptor, natives_pf, region); | |
| 204 | |
| 205 base::PlatformFile snapshot_pf = | |
| 206 gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion); | |
| 207 // Failure to load the V8 snapshot is not necessarily an error. V8 can start | |
| 208 // up (slower) without the snapshot. | |
| 209 if (snapshot_pf != -1) { | |
| 210 snapshot_loaded = true; | |
| 211 files_to_register->ShareWithRegion( | |
| 212 kV8SnapshotDataDescriptor, snapshot_pf, region); | |
| 213 } | |
| 214 #endif | |
| 215 | |
| 216 if (process_type != switches::kZygoteProcess) { | |
| 217 cmd_line->AppendSwitch(::switches::kV8NativesPassedByFD); | |
| 218 if (snapshot_loaded) { | |
| 219 cmd_line->AppendSwitch(::switches::kV8SnapshotPassedByFD); | |
| 220 } | |
| 221 } | |
| 222 #endif // defined(V8_USE_EXTERNAL_STARTUP_DATA) | |
| 223 #endif // defined(OS_POSIX) && !defined(OS_MACOSX) | |
| 224 | |
| 225 #if defined(OS_ANDROID) | |
| 226 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE | |
| 227 base::MemoryMappedFile::Region icu_region; | |
| 228 base::PlatformFile icu_pf = base::i18n::GetIcuDataFileHandle(&icu_region); | |
| 229 files_to_register->ShareWithRegion( | |
| 230 kAndroidICUDataDescriptor, icu_pf, icu_region); | |
| 231 #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE | |
| 232 | |
| 233 // Android WebView runs in single process, ensure that we never get here | |
| 234 // when running in single process mode. | |
| 235 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); | |
| 236 | |
| 237 StartChildProcess( | |
| 238 cmd_line->argv(), child_process_id, std::move(files_to_register), | |
| 239 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, | |
| 240 begin_launch_time, base::Passed(&mojo_fd))); | |
| 241 | |
| 242 #elif defined(OS_POSIX) | |
| 243 // We need to close the client end of the IPC channel to reliably detect | |
| 244 // child termination. | |
| 245 | |
| 246 #if !defined(OS_MACOSX) | |
| 247 ZygoteHandle* zygote_handle = | |
| 248 !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote) | |
| 249 ? delegate->GetZygote() | |
| 250 : nullptr; | |
| 251 // If |zygote_handle| is null, a zygote should not be used. | |
| 252 if (zygote_handle) { | |
| 253 // This code runs on the PROCESS_LAUNCHER thread so race conditions are not | |
| 254 // an issue with the lazy initialization. | |
| 255 if (*zygote_handle == nullptr) { | |
| 256 *zygote_handle = CreateZygote(); | |
| 257 } | |
| 258 zygote = *zygote_handle; | |
| 259 base::ProcessHandle handle = zygote->ForkRequest( | |
| 260 cmd_line->argv(), std::move(files_to_register), process_type); | |
| 261 process = base::Process(handle); | |
| 262 } else | |
| 263 // Fall through to the normal posix case below when we're not zygoting. | |
| 264 #endif // !defined(OS_MACOSX) | |
| 265 { | |
| 266 // Convert FD mapping to FileHandleMappingVector | |
| 267 base::FileHandleMappingVector fds_to_map = | |
| 268 files_to_register->GetMappingWithIDAdjustment( | |
| 269 base::GlobalDescriptors::kBaseDescriptor); | |
| 270 | |
| 271 #if !defined(OS_MACOSX) | |
| 272 if (process_type == switches::kRendererProcess) { | |
| 273 const int sandbox_fd = | |
| 274 RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); | |
| 275 fds_to_map.push_back(std::make_pair( | |
| 276 sandbox_fd, | |
| 277 GetSandboxFD())); | |
| 278 } | |
| 279 #endif // defined(OS_MACOSX) | |
| 280 | |
| 281 // Actually launch the app. | |
| 282 base::LaunchOptions options; | |
| 283 options.environ = env; | |
| 284 options.fds_to_remap = &fds_to_map; | |
| 285 | |
| 286 #if defined(OS_MACOSX) | |
| 287 // Hold the MachBroker lock for the duration of LaunchProcess. The child | |
| 288 // will send its task port to the parent almost immediately after startup. | |
| 289 // The Mach message will be delivered to the parent, but updating the | |
| 290 // record of the launch will wait until after the placeholder PID is | |
| 291 // inserted below. This ensures that while the child process may send its | |
| 292 // port to the parent prior to the parent leaving LaunchProcess, the | |
| 293 // order in which the record in MachBroker is updated is correct. | |
| 294 MachBroker* broker = MachBroker::GetInstance(); | |
| 295 broker->GetLock().Acquire(); | |
| 296 | |
| 297 // Make sure the MachBroker is running, and inform it to expect a | |
| 298 // check-in from the new process. | |
| 299 broker->EnsureRunning(); | |
| 300 | |
| 301 const SandboxType sandbox_type = delegate->GetSandboxType(); | |
| 302 std::unique_ptr<sandbox::PreExecDelegate> pre_exec_delegate; | |
| 303 if (BootstrapSandboxManager::ShouldEnable()) { | |
| 304 BootstrapSandboxManager* sandbox_manager = | |
| 305 BootstrapSandboxManager::GetInstance(); | |
| 306 if (sandbox_manager->EnabledForSandbox(sandbox_type)) { | |
| 307 pre_exec_delegate = sandbox_manager->sandbox()->NewClient(sandbox_type); | |
| 308 } | |
| 309 } | |
| 310 options.pre_exec_delegate = pre_exec_delegate.get(); | |
| 311 #endif // defined(OS_MACOSX) | |
| 312 | |
| 313 process = base::LaunchProcess(*cmd_line, options); | |
| 314 | |
| 315 #if defined(OS_MACOSX) | |
| 316 if (process.IsValid()) { | |
| 317 broker->AddPlaceholderForPid(process.Pid(), child_process_id); | |
| 318 } else { | |
| 319 if (pre_exec_delegate) { | |
| 320 BootstrapSandboxManager::GetInstance()->sandbox()->RevokeToken( | |
| 321 pre_exec_delegate->sandbox_token()); | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 // After updating the broker, release the lock and let the child's | |
| 326 // message be processed on the broker's thread. | |
| 327 broker->GetLock().Release(); | |
| 328 #endif // defined(OS_MACOSX) | |
| 329 } | |
| 330 #endif // else defined(OS_POSIX) | |
| 331 #if !defined(OS_ANDROID) | |
| 332 if (process.IsValid()) { | |
| 333 launch_result = LAUNCH_RESULT_SUCCESS; | |
| 334 RecordHistogramsOnLauncherThread(base::TimeTicks::Now() - | |
| 335 begin_launch_time); | |
| 336 } | |
| 337 BrowserThread::PostTask(client_thread_id, FROM_HERE, | |
| 338 base::Bind(callback, zygote, base::Passed(&process), | |
| 339 launch_result)); | |
| 340 #endif // !defined(OS_ANDROID) | |
| 341 } | |
| 342 | |
| 343 void TerminateOnLauncherThread(ZygoteHandle zygote, base::Process process) { | |
| 344 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | |
| 345 #if defined(OS_ANDROID) | |
| 346 VLOG(1) << "ChromeProcess: Stopping process with handle " | |
| 347 << process.Handle(); | |
| 348 StopChildProcess(process.Handle()); | |
| 349 #else | |
| 350 // Client has gone away, so just kill the process. Using exit code 0 | |
| 351 // means that UMA won't treat this as a crash. | |
| 352 process.Terminate(RESULT_CODE_NORMAL_EXIT, false); | |
| 353 // On POSIX, we must additionally reap the child. | |
| 354 #if defined(OS_POSIX) | |
| 355 #if !defined(OS_MACOSX) | |
| 356 if (zygote) { | |
| 357 // If the renderer was created via a zygote, we have to proxy the reaping | |
| 358 // through the zygote process. | |
| 359 zygote->EnsureProcessTerminated(process.Handle()); | |
| 360 } else | |
| 361 #endif // !OS_MACOSX | |
| 362 base::EnsureProcessTerminated(std::move(process)); | |
| 363 #endif // OS_POSIX | |
| 364 #endif // defined(OS_ANDROID) | |
| 365 } | |
| 366 | |
| 367 void SetProcessBackgroundedOnLauncherThread(base::Process process, | |
| 368 bool background) { | |
| 369 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | |
| 370 if (process.CanBackgroundProcesses()) { | |
| 371 #if defined(OS_MACOSX) | |
| 372 process.SetProcessBackgrounded(MachBroker::GetInstance(), background); | |
| 373 #else | |
| 374 process.SetProcessBackgrounded(background); | |
| 375 #endif // defined(OS_MACOSX) | |
| 376 } | |
| 377 #if defined(OS_ANDROID) | |
| 378 SetChildProcessInForeground(process.Handle(), !background); | |
| 379 #endif | |
| 380 } | |
| 381 | |
| 382 } // namespace | 37 } // namespace |
| 383 | 38 |
| 384 ChildProcessLauncher::ChildProcessLauncher( | 39 ChildProcessLauncher::ChildProcessLauncher( |
| 385 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate, | 40 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate, |
| 386 std::unique_ptr<base::CommandLine> cmd_line, | 41 std::unique_ptr<base::CommandLine> command_line, |
| 387 int child_process_id, | 42 int child_process_id, |
| 388 Client* client, | 43 Client* client, |
| 389 const std::string& mojo_child_token, | 44 const std::string& mojo_child_token, |
| 390 const mojo::edk::ProcessErrorCallback& process_error_callback, | 45 const mojo::edk::ProcessErrorCallback& process_error_callback, |
| 391 bool terminate_on_shutdown) | 46 bool terminate_on_shutdown) |
| 392 : client_(client), | 47 : client_(client), |
| 393 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), | 48 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), |
| 394 exit_code_(RESULT_CODE_NORMAL_EXIT), | 49 exit_code_(RESULT_CODE_NORMAL_EXIT), |
| 395 zygote_(nullptr), | 50 zygote_(nullptr), |
| 396 starting_(true), | 51 starting_(true), |
| 397 process_error_callback_(process_error_callback), | 52 process_error_callback_(process_error_callback), |
| 398 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ | 53 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ |
| 399 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ | 54 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ |
| 400 defined(UNDEFINED_SANITIZER) | 55 defined(UNDEFINED_SANITIZER) |
| 401 terminate_child_on_shutdown_(false), | 56 terminate_child_on_shutdown_(false), |
| 402 #else | 57 #else |
| 403 terminate_child_on_shutdown_(terminate_on_shutdown), | 58 terminate_child_on_shutdown_(terminate_on_shutdown), |
| 404 #endif | 59 #endif |
| 405 mojo_child_token_(mojo_child_token), | 60 mojo_child_token_(mojo_child_token), |
| 406 weak_factory_(this) { | 61 weak_factory_(this) { |
| 407 DCHECK(CalledOnValidThread()); | 62 DCHECK(CalledOnValidThread()); |
| 408 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); | 63 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); |
| 409 Launch(std::move(delegate), std::move(cmd_line), child_process_id); | 64 |
| 65 helper_ = new Helper(child_process_id, client_thread_id_, | |
| 66 std::move(command_line), std::move(delegate), | |
| 67 weak_factory_.GetWeakPtr(), terminate_on_shutdown); | |
| 68 helper_->LaunchOnClientThread(); | |
| 410 } | 69 } |
| 411 | 70 |
| 412 ChildProcessLauncher::~ChildProcessLauncher() { | 71 ChildProcessLauncher::~ChildProcessLauncher() { |
| 413 DCHECK(CalledOnValidThread()); | 72 DCHECK(CalledOnValidThread()); |
| 414 if (process_.IsValid() && terminate_child_on_shutdown_) { | 73 if (process_.IsValid() && terminate_child_on_shutdown_) |
| 415 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So | 74 Terminate(zygote_, std::move(process_)); |
| 416 // don't this on the UI/IO threads. | 75 } |
| 417 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 76 |
| 418 base::Bind(&TerminateOnLauncherThread, zygote_, | 77 ChildProcessLauncher::Helper::Helper( |
|
boliu
2017/01/12 02:00:07
nit: group ChildProcessLauncher methods together a
Jay Civelli
2017/01/12 23:05:40
Done (they were defined in the order they were dec
| |
| 419 base::Passed(&process_))); | 78 int child_process_id, |
| 79 BrowserThread::ID client_thread_id, | |
| 80 std::unique_ptr<base::CommandLine> command_line, | |
| 81 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate, | |
| 82 const base::WeakPtr<ChildProcessLauncher>& child_process_launcher, | |
| 83 bool terminate_on_shutdown) | |
| 84 : child_process_id_(child_process_id), | |
| 85 client_thread_id_(client_thread_id), | |
| 86 command_line_(std::move(command_line)), | |
| 87 delegate_(std::move(delegate)), | |
| 88 child_process_launcher_(child_process_launcher), | |
| 89 terminate_on_shutdown_(terminate_on_shutdown) { | |
| 90 } | |
| 91 | |
| 92 ChildProcessLauncher::Helper::~Helper() {} | |
| 93 | |
| 94 void ChildProcessLauncher::Helper::LaunchOnClientThread() { | |
| 95 DCHECK_CURRENTLY_ON(client_thread_id_); | |
| 96 | |
| 97 BeforeLaunchOnClientThread(); | |
| 98 | |
| 99 mojo_server_handle_ = PrepareMojoPipeHandlesOnClientThread(); | |
| 100 if (!mojo_server_handle_.is_valid()) { | |
| 101 mojo::edk::PlatformChannelPair channel_pair; | |
| 102 mojo_server_handle_ = channel_pair.PassServerHandle(); | |
| 103 mojo_client_handle_ = channel_pair.PassClientHandle(); | |
| 104 } | |
| 105 | |
| 106 BrowserThread::PostTask( | |
| 107 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 108 base::Bind(&Helper::LaunchOnLauncherThread, this)); | |
| 109 } | |
| 110 | |
| 111 void ChildProcessLauncher::Helper::LaunchOnLauncherThread() { | |
| 112 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | |
| 113 | |
| 114 begin_launch_time_ = base::TimeTicks::Now(); | |
| 115 | |
| 116 std::unique_ptr<FileMappedForLaunch> files_to_register = GetFilesToMap(); | |
| 117 | |
| 118 base::Process process; | |
| 119 ZygoteHandle zygote = nullptr; | |
| 120 bool is_synchronous_launch = true; | |
| 121 int launch_result = LAUNCH_RESULT_FAILURE; | |
| 122 if (ShouldForkAsZygote()) { | |
| 123 zygote = ForkAsZygote(std::move(files_to_register), &process); | |
| 124 launch_result = LAUNCH_RESULT_SUCCESS; | |
| 125 } else { | |
| 126 base::LaunchOptions options; | |
| 127 BeforeLaunchOnLauncherThread(*files_to_register, &options); | |
| 128 | |
| 129 process = LaunchProcessOnLauncherThread(options, | |
| 130 files_to_register.get(), | |
| 131 &is_synchronous_launch, | |
| 132 &launch_result); | |
| 133 | |
| 134 AfterLaunchOnLauncherThread(process, options); | |
| 135 } | |
| 136 | |
| 137 if (is_synchronous_launch) { | |
| 138 PostLaunchOnLauncherThread(std::move(process), zygote, launch_result); | |
| 420 } | 139 } |
| 421 } | 140 } |
| 422 | 141 |
| 423 void ChildProcessLauncher::Launch( | 142 void ChildProcessLauncher::Helper::PostLaunchOnLauncherThread( |
| 424 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate, | 143 base::Process process, |
| 425 std::unique_ptr<base::CommandLine> cmd_line, | 144 ZygoteHandle zygote, |
| 426 int child_process_id) { | 145 int launch_result) { |
| 427 DCHECK(CalledOnValidThread()); | 146 // Release the client handle now that the process has been started (the pipe |
| 147 // may not signal when the process dies otherwise and we would not detect the | |
| 148 // child process died). | |
| 149 mojo_client_handle_.reset(); | |
| 428 | 150 |
| 429 #if defined(OS_ANDROID) | 151 if (process.IsValid()) { |
|
boliu
2017/01/12 02:00:07
on android, old code didn't check this to record h
Jay Civelli
2017/01/12 23:05:40
Yes, and that seemed wrong (the other platforms be
| |
| 430 // Android only supports renderer, sandboxed utility and gpu. | 152 RecordHistogramsOnLauncherThread( |
| 431 std::string process_type = | 153 base::TimeTicks::Now() - begin_launch_time_); |
| 432 cmd_line->GetSwitchValueASCII(switches::kProcessType); | 154 } |
| 433 CHECK(process_type == switches::kGpuProcess || | |
| 434 process_type == switches::kRendererProcess || | |
| 435 #if BUILDFLAG(ENABLE_PLUGINS) | |
| 436 process_type == switches::kPpapiPluginProcess || | |
| 437 #endif | |
| 438 process_type == switches::kUtilityProcess) | |
| 439 << "Unsupported process type: " << process_type; | |
| 440 | 155 |
| 441 // Non-sandboxed utility or renderer process are currently not supported. | |
| 442 DCHECK(process_type == switches::kGpuProcess || | |
| 443 !cmd_line->HasSwitch(switches::kNoSandbox)); | |
| 444 | |
| 445 #endif | |
| 446 mojo::edk::ScopedPlatformHandle server_handle; | |
| 447 mojo::edk::ScopedPlatformHandle client_handle; | |
| 448 #if defined(OS_WIN) | |
| 449 if (delegate->ShouldLaunchElevated()) { | |
| 450 mojo::edk::NamedPlatformChannelPair named_pair; | |
| 451 server_handle = named_pair.PassServerHandle(); | |
| 452 named_pair.PrepareToPassClientHandleToChildProcess(cmd_line.get()); | |
| 453 } else | |
| 454 #endif | |
| 455 { | |
| 456 mojo::edk::PlatformChannelPair channel_pair; | |
| 457 server_handle = channel_pair.PassServerHandle(); | |
| 458 client_handle = channel_pair.PassClientHandle(); | |
| 459 } | |
| 460 NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch, | |
| 461 weak_factory_.GetWeakPtr(), | |
| 462 terminate_child_on_shutdown_, | |
| 463 base::Passed(&server_handle))); | |
| 464 BrowserThread::PostTask( | 156 BrowserThread::PostTask( |
| 465 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 157 client_thread_id_, FROM_HERE, |
| 466 base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_, | 158 base::Bind(&Helper::PostLaunchOnClientThread, |
| 467 child_process_id, base::Passed(&delegate), | 159 this, base::Passed(&process), zygote, launch_result)); |
| 468 base::Passed(&client_handle), base::Passed(&cmd_line))); | |
| 469 } | 160 } |
| 470 | 161 |
| 471 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { | 162 void ChildProcessLauncher::Helper::PostLaunchOnClientThread( |
| 472 DCHECK(CalledOnValidThread()); | 163 base::Process process, ZygoteHandle zygote, int error_code) { |
| 473 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 164 if (child_process_launcher_) { |
| 474 if (zygote_) { | 165 child_process_launcher_->Notify(zygote, std::move(mojo_server_handle_), |
| 475 termination_status_ = zygote_->GetTerminationStatus( | 166 std::move(process), error_code); |
| 476 process_.Handle(), known_dead, &exit_code_); | 167 } else if (process.IsValid() && terminate_on_shutdown_) { |
| 477 } else if (known_dead) { | 168 ChildProcessLauncher::Terminate(zygote, std::move(process)); |
| 478 termination_status_ = | |
| 479 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); | |
| 480 } else { | |
| 481 #elif defined(OS_MACOSX) | |
| 482 if (known_dead) { | |
| 483 termination_status_ = | |
| 484 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); | |
| 485 } else { | |
| 486 #elif defined(OS_ANDROID) | |
| 487 if (IsChildProcessOomProtected(process_.Handle())) { | |
| 488 termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED; | |
| 489 } else { | |
| 490 #else | |
| 491 { | |
| 492 #endif | |
| 493 termination_status_ = | |
| 494 base::GetTerminationStatus(process_.Handle(), &exit_code_); | |
| 495 } | 169 } |
| 496 } | 170 } |
| 497 | 171 |
| 172 std::string ChildProcessLauncher::Helper::GetProcessType() { | |
| 173 return command_line()->GetSwitchValueASCII(switches::kProcessType); | |
| 174 } | |
| 175 | |
| 498 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { | 176 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { |
| 499 DCHECK(CalledOnValidThread()); | 177 DCHECK(CalledOnValidThread()); |
| 500 base::Process to_pass = process_.Duplicate(); | 178 base::Process to_pass = process_.Duplicate(); |
| 501 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 179 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 502 base::Bind(&SetProcessBackgroundedOnLauncherThread, | 180 base::Bind(&SetProcessBackgroundedOnLauncherThread, |
| 503 base::Passed(&to_pass), background)); | 181 base::Passed(&to_pass), background)); |
| 504 } | 182 } |
| 505 | 183 |
| 506 void ChildProcessLauncher::DidLaunch( | |
| 507 base::WeakPtr<ChildProcessLauncher> instance, | |
| 508 bool terminate_on_shutdown, | |
| 509 mojo::edk::ScopedPlatformHandle server_handle, | |
| 510 ZygoteHandle zygote, | |
| 511 #if defined(OS_ANDROID) | |
| 512 base::ScopedFD mojo_fd, | |
| 513 #endif | |
| 514 base::Process process, | |
| 515 int error_code) { | |
| 516 if (!process.IsValid()) | |
| 517 LOG(ERROR) << "Failed to launch child process"; | |
| 518 | |
| 519 if (instance.get()) { | |
| 520 instance->Notify(zygote, std::move(server_handle), | |
| 521 std::move(process), error_code); | |
| 522 } else { | |
| 523 if (process.IsValid() && terminate_on_shutdown) { | |
| 524 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So | |
| 525 // don't this on the UI/IO threads. | |
| 526 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 527 base::Bind(&TerminateOnLauncherThread, zygote, | |
| 528 base::Passed(&process))); | |
| 529 } | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 void ChildProcessLauncher::Notify(ZygoteHandle zygote, | 184 void ChildProcessLauncher::Notify(ZygoteHandle zygote, |
| 534 mojo::edk::ScopedPlatformHandle server_handle, | 185 mojo::edk::ScopedPlatformHandle server_handle, |
| 535 base::Process process, | 186 base::Process process, |
| 536 int error_code) { | 187 int error_code) { |
| 537 DCHECK(CalledOnValidThread()); | 188 DCHECK(CalledOnValidThread()); |
| 538 starting_ = false; | 189 starting_ = false; |
| 539 process_ = std::move(process); | 190 process_ = std::move(process); |
| 540 | 191 |
| 541 if (process_.IsValid()) { | 192 if (process_.IsValid()) { |
| 542 // Set up Mojo IPC to the new process. | 193 // Set up Mojo IPC to the new process. |
| 543 mojo::edk::ChildProcessLaunched(process_.Handle(), std::move(server_handle), | 194 mojo::edk::ChildProcessLaunched(process_.Handle(), std::move(server_handle), |
| 544 mojo_child_token_, process_error_callback_); | 195 mojo_child_token_, process_error_callback_); |
| 545 } | 196 } |
| 546 | 197 |
| 547 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | |
| 548 zygote_ = zygote; | 198 zygote_ = zygote; |
| 549 #endif | |
| 550 if (process_.IsValid()) { | 199 if (process_.IsValid()) { |
| 551 client_->OnProcessLaunched(); | 200 client_->OnProcessLaunched(); |
| 552 } else { | 201 } else { |
| 553 mojo::edk::ChildProcessLaunchFailed(mojo_child_token_); | 202 mojo::edk::ChildProcessLaunchFailed(mojo_child_token_); |
| 554 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; | 203 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; |
| 555 client_->OnProcessLaunchFailed(error_code); | 204 client_->OnProcessLaunchFailed(error_code); |
| 556 } | 205 } |
| 557 } | 206 } |
| 558 | 207 |
| 559 bool ChildProcessLauncher::IsStarting() { | 208 bool ChildProcessLauncher::IsStarting() { |
| 560 // TODO(crbug.com/469248): This fails in some tests. | 209 // TODO(crbug.com/469248): This fails in some tests. |
| 561 // DCHECK(CalledOnValidThread()); | 210 // DCHECK(CalledOnValidThread()); |
| 562 return starting_; | 211 return starting_; |
| 563 } | 212 } |
| 564 | 213 |
| 565 const base::Process& ChildProcessLauncher::GetProcess() const { | 214 const base::Process& ChildProcessLauncher::GetProcess() const { |
| 566 // TODO(crbug.com/469248): This fails in some tests. | 215 // TODO(crbug.com/469248): This fails in some tests. |
| 567 // DCHECK(CalledOnValidThread()); | 216 // DCHECK(CalledOnValidThread()); |
| 568 return process_; | 217 return process_; |
| 569 } | 218 } |
| 570 | 219 |
| 571 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( | 220 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( |
| 572 bool known_dead, | 221 bool known_dead, |
| 573 int* exit_code) { | 222 int* exit_code) { |
| 574 DCHECK(CalledOnValidThread()); | 223 DCHECK(CalledOnValidThread()); |
| 575 if (!process_.IsValid()) { | 224 if (!process_.IsValid()) { |
| 576 // Process is already gone, so return the cached termination status. | 225 // Process is already gone, so return the cached termination status. |
| 577 if (exit_code) | 226 if (exit_code) |
| 578 *exit_code = exit_code_; | 227 *exit_code = exit_code_; |
| 579 return termination_status_; | 228 return termination_status_; |
| 580 } | 229 } |
| 581 | 230 |
| 582 UpdateTerminationStatus(known_dead); | 231 UpdateTerminationStatus(known_dead); |
| 583 if (exit_code) | 232 if (exit_code) |
| 584 *exit_code = exit_code_; | 233 *exit_code = exit_code_; |
| 585 | 234 |
| 586 // POSIX: If the process crashed, then the kernel closed the socket | 235 // POSIX: If the process crashed, then the kernel closed the socket for it and |
| 587 // for it and so the child has already died by the time we get | 236 // so the child has already died by the time we get here. Since |
| 588 // here. Since GetTerminationStatus called waitpid with WNOHANG, | 237 // GetTerminationStatus called waitpid with WNOHANG, it'll reap the process. |
| 589 // it'll reap the process. However, if GetTerminationStatus didn't | 238 // However, if GetTerminationStatus didn't reap the child (because it was |
| 590 // reap the child (because it was still running), we'll need to | 239 // still running), we'll need to Terminate via ProcessWatcher. So we can't |
| 591 // Terminate via ProcessWatcher. So we can't close the handle here. | 240 // close the handle here. |
| 592 if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) | 241 if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) |
| 593 process_.Close(); | 242 process_.Close(); |
| 594 | 243 |
| 595 return termination_status_; | 244 return termination_status_; |
| 596 } | 245 } |
| 597 | 246 |
| 247 bool ChildProcessLauncher::Terminate(int exit_code, bool wait) { | |
| 248 return IsStarting() ? false : TerminateProcess(GetProcess(), exit_code, wait); | |
| 249 } | |
| 250 | |
| 598 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( | 251 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( |
| 599 Client* client) { | 252 Client* client) { |
| 600 Client* ret = client_; | 253 Client* ret = client_; |
| 601 client_ = client; | 254 client_ = client; |
| 602 return ret; | 255 return ret; |
| 603 } | 256 } |
| 604 | 257 |
| 258 // static | |
| 259 void ChildProcessLauncher::Terminate( | |
| 260 ZygoteHandle zygote, base::Process process) { | |
| 261 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) { | |
| 262 ChildProcessLauncher::TerminateOnLauncherThread(zygote, std::move(process)); | |
| 263 return; | |
| 264 } | |
| 265 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So don't | |
| 266 // this on the UI/IO threads. | |
| 267 BrowserThread::PostTask( | |
| 268 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 269 base::Bind(&ChildProcessLauncher::TerminateOnLauncherThread, | |
| 270 zygote, base::Passed(&process))); | |
| 271 } | |
| 272 | |
| 273 | |
| 605 } // namespace content | 274 } // namespace content |
| OLD | NEW |