#include "remoting/host/win/worker_process_launcher.h"
#include <Windows.h>
#include <utility>
#include "base/location.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "remoting/host/base/host_exit_codes.h"
#include "remoting/host/worker_process_ipc_delegate.h"
using base::win::ScopedHandle;
const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
0,
100,
2,
0,
60000,
-1,
false,
};
const int kKillProcessTimeoutSeconds = 5;
const int kLaunchResultTimeoutSeconds = 5;
namespace remoting {
WorkerProcessLauncher::Delegate::~Delegate() {}
WorkerProcessLauncher::WorkerProcessLauncher(
std::unique_ptr<WorkerProcessLauncher::Delegate> launcher_delegate,
WorkerProcessIpcDelegate* ipc_handler)
: ipc_handler_(ipc_handler),
launcher_delegate_(std::move(launcher_delegate)),
exit_code_(CONTROL_C_EXIT),
kill_process_timeout_(base::Seconds(kKillProcessTimeoutSeconds)),
launch_backoff_(&kDefaultBackoffPolicy) {
DCHECK(ipc_handler_);
LaunchWorker();
}
WorkerProcessLauncher::~WorkerProcessLauncher() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ipc_handler_ = nullptr;
StopWorker();
}
void WorkerProcessLauncher::Crash(const base::Location& location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
launcher_delegate_->CrashProcess(location);
launcher_delegate_->CloseChannel();
if (!kill_process_timer_.IsRunning()) {
kill_process_timer_.Start(FROM_HERE, kill_process_timeout_, this,
&WorkerProcessLauncher::StopWorker);
}
}
void WorkerProcessLauncher::GetRemoteAssociatedInterface(
mojo::GenericPendingAssociatedReceiver receiver) {
launcher_delegate_->GetRemoteAssociatedInterface(std::move(receiver));
}
void WorkerProcessLauncher::OnProcessLaunched(
base::win::ScopedHandle worker_process) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!launch_timer_.IsRunning());
DCHECK(!process_watcher_.GetWatchedObject());
DCHECK(!worker_process_.is_valid());
if (!process_watcher_.StartWatchingOnce(worker_process.Get(), this)) {
StopWorker();
return;
}
worker_process_ = std::move(worker_process);
}
void WorkerProcessLauncher::OnFatalError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
StopWorker();
}
void WorkerProcessLauncher::OnChannelConnected(int32_t peer_pid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ipc_handler_->OnChannelConnected(peer_pid);
}
void WorkerProcessLauncher::OnChannelError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!kill_process_timer_.IsRunning()) {
kill_process_timer_.Start(FROM_HERE, kill_process_timeout_, this,
&WorkerProcessLauncher::StopWorker);
}
}
void WorkerProcessLauncher::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ipc_handler_->OnAssociatedInterfaceRequest(interface_name, std::move(handle));
}
void WorkerProcessLauncher::OnObjectSignaled(HANDLE object) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!process_watcher_.GetWatchedObject());
DCHECK_EQ(exit_code_, CONTROL_C_EXIT);
DCHECK_EQ(worker_process_.Get(), object);
if (!::GetExitCodeProcess(worker_process_.Get(), &exit_code_)) {
PLOG(INFO) << "Failed to query the exit code of the worker process";
exit_code_ = CONTROL_C_EXIT;
}
worker_process_.Close();
StopWorker();
}
void WorkerProcessLauncher::LaunchWorker() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!kill_process_timer_.IsRunning());
DCHECK(!launch_timer_.IsRunning());
DCHECK(!process_watcher_.GetWatchedObject());
DCHECK(!launch_result_timer_.IsRunning());
exit_code_ = CONTROL_C_EXIT;
launch_result_timer_.Start(FROM_HERE,
base::Seconds(kLaunchResultTimeoutSeconds), this,
&WorkerProcessLauncher::RecordLaunchResult);
launcher_delegate_->LaunchProcess(this);
}
void WorkerProcessLauncher::RecordLaunchResult() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!worker_process_.is_valid()) {
LOG(WARNING) << "A worker process failed to start within "
<< kLaunchResultTimeoutSeconds << " seconds.";
launch_backoff_.InformOfRequest(false);
StopWorker();
return;
}
launch_backoff_.InformOfRequest(true);
}
void WorkerProcessLauncher::RecordSuccessfulLaunchForTest() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (launch_result_timer_.IsRunning()) {
launch_result_timer_.Stop();
RecordLaunchResult();
}
}
void WorkerProcessLauncher::SetKillProcessTimeoutForTest(
const base::TimeDelta& timeout) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
kill_process_timeout_ = timeout;
}
void WorkerProcessLauncher::StopWorker() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (launch_result_timer_.IsRunning()) {
launch_backoff_.InformOfRequest(false);
launch_result_timer_.Stop();
}
process_watcher_.StopWatching();
worker_process_.Close();
kill_process_timer_.Stop();
launcher_delegate_->KillProcess();
if (stopping()) {
return;
}
ipc_handler_->OnWorkerProcessStopped();
if (kMinPermanentErrorExitCode <= exit_code_ &&
exit_code_ <= kMaxPermanentErrorExitCode) {
ipc_handler_->OnPermanentError(exit_code_);
return;
}
launch_timer_.Start(FROM_HERE, launch_backoff_.GetTimeUntilRelease(), this,
&WorkerProcessLauncher::LaunchWorker);
}
}