| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/child_process_launcher.h" |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/files/file_util.h" |
| #include "base/i18n/icu_util.h" |
| #include "base/logging.h" |
| #include "base/process/launch.h" |
| #include "build/build_config.h" |
| #include "content/public/common/result_codes.h" |
| #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| |
| namespace content { |
| |
| using internal::ChildProcessLauncherHelper; |
| |
| ChildProcessLauncher::ChildProcessLauncher( |
| std::unique_ptr<SandboxedProcessLauncherDelegate> delegate, |
| std::unique_ptr<base::CommandLine> command_line, |
| int child_process_id, |
| Client* client, |
| std::unique_ptr<mojo::edk::OutgoingBrokerClientInvitation> |
| broker_client_invitation, |
| const mojo::edk::ProcessErrorCallback& process_error_callback, |
| bool terminate_on_shutdown) |
| : client_(client), |
| termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), |
| exit_code_(RESULT_CODE_NORMAL_EXIT), |
| starting_(true), |
| #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ |
| defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ |
| defined(UNDEFINED_SANITIZER) |
| terminate_child_on_shutdown_(false), |
| #else |
| terminate_child_on_shutdown_(terminate_on_shutdown), |
| #endif |
| weak_factory_(this) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); |
| |
| helper_ = new ChildProcessLauncherHelper( |
| child_process_id, client_thread_id_, std::move(command_line), |
| std::move(delegate), weak_factory_.GetWeakPtr(), terminate_on_shutdown, |
| std::move(broker_client_invitation), process_error_callback); |
| helper_->StartLaunchOnClientThread(); |
| } |
| |
| ChildProcessLauncher::~ChildProcessLauncher() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (process_.process.IsValid() && terminate_child_on_shutdown_) { |
| // Client has gone away, so just kill the process. |
| ChildProcessLauncherHelper::ForceNormalProcessTerminationAsync( |
| std::move(process_)); |
| } |
| } |
| |
| void ChildProcessLauncher::SetProcessPriority( |
| const ChildProcessLauncherPriority& priority) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| base::Process to_pass = process_.process.Duplicate(); |
| BrowserThread::PostTask( |
| BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| base::BindOnce( |
| &ChildProcessLauncherHelper::SetProcessPriorityOnLauncherThread, |
| helper_, base::Passed(&to_pass), priority)); |
| } |
| |
| void ChildProcessLauncher::Notify( |
| ChildProcessLauncherHelper::Process process, |
| int error_code) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| starting_ = false; |
| process_ = std::move(process); |
| |
| if (process_.process.IsValid()) { |
| client_->OnProcessLaunched(); |
| } else { |
| termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; |
| |
| // NOTE: May delete |this|. |
| client_->OnProcessLaunchFailed(error_code); |
| } |
| } |
| |
| bool ChildProcessLauncher::IsStarting() { |
| // TODO(crbug.com/469248): This fails in some tests. |
| // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return starting_; |
| } |
| |
| const base::Process& ChildProcessLauncher::GetProcess() const { |
| // TODO(crbug.com/469248): This fails in some tests. |
| // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return process_.process; |
| } |
| |
| base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( |
| bool known_dead, |
| int* exit_code) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!process_.process.IsValid()) { |
| // Process is already gone, so return the cached termination status. |
| if (exit_code) |
| *exit_code = exit_code_; |
| return termination_status_; |
| } |
| |
| termination_status_ = |
| helper_->GetTerminationStatus(process_, known_dead, &exit_code_); |
| if (exit_code) |
| *exit_code = exit_code_; |
| |
| // POSIX: If the process crashed, then the kernel closed the socket for it and |
| // so the child has already died by the time we get here. Since |
| // GetTerminationStatus called waitpid with WNOHANG, it'll reap the process. |
| // However, if GetTerminationStatus didn't reap the child (because it was |
| // still running), we'll need to Terminate via ProcessWatcher. So we can't |
| // close the handle here. |
| if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) { |
| process_.process.Exited(exit_code_); |
| process_.process.Close(); |
| } |
| |
| return termination_status_; |
| } |
| |
| bool ChildProcessLauncher::Terminate(int exit_code, bool wait) { |
| return IsStarting() ? false |
| : ChildProcessLauncherHelper::TerminateProcess( |
| GetProcess(), exit_code, wait); |
| } |
| |
| // static |
| bool ChildProcessLauncher::TerminateProcess(const base::Process& process, |
| int exit_code, |
| bool wait) { |
| return ChildProcessLauncherHelper::TerminateProcess(process, exit_code, wait); |
| } |
| |
| // static |
| void ChildProcessLauncher::SetRegisteredFilesForService( |
| const std::string& service_name, |
| catalog::RequiredFileMap required_files) { |
| ChildProcessLauncherHelper::SetRegisteredFilesForService( |
| service_name, std::move(required_files)); |
| } |
| |
| // static |
| void ChildProcessLauncher::ResetRegisteredFilesForTesting() { |
| ChildProcessLauncherHelper::ResetRegisteredFilesForTesting(); |
| } |
| |
| #if defined(OS_ANDROID) |
| // static |
| size_t ChildProcessLauncher::GetNumberOfRendererSlots() { |
| return ChildProcessLauncherHelper::GetNumberOfRendererSlots(); |
| } |
| #endif // OS_ANDROID |
| |
| ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( |
| Client* client) { |
| Client* ret = client_; |
| client_ = client; |
| return ret; |
| } |
| |
| bool ChildProcessLauncherPriority::operator==( |
| const ChildProcessLauncherPriority& other) const { |
| return background == other.background && |
| boost_for_pending_views == other.boost_for_pending_views |
| #if defined(OS_ANDROID) |
| && importance == other.importance |
| #endif |
| ; |
| } |
| |
| } // namespace content |