blob: 4bf9be9ac5818a0aa969324d82a76dff32b1ea93 [file]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/setup/daemon_controller.h"
#include <memory>
#include <utility>
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_pump_type.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "build/build_config.h"
#include "remoting/base/auto_thread.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/host/host_config.h"
namespace remoting {
namespace {
// Name of the Daemon Controller's worker thread.
const char kDaemonControllerThreadName[] = "Daemon Controller thread";
// The configuration keys that cannot be specified in UpdateConfig().
const char* const kReadonlyKeys[] = {
kHostIdConfigPath, kHostOwnerConfigPath, kServiceAccountConfigPath,
kDeprecatedXmppLoginConfigPath, kDeprecatedHostOwnerEmailConfigPath};
} // namespace
// static
const base::flat_set<std::string_view>&
DaemonController::GetUnprivilegedConfigKeys() {
static base::NoDestructor<base::flat_set<std::string_view>> unprivileged_keys(
{kHostIdConfigPath, kServiceAccountConfigPath,
kDeprecatedXmppLoginConfigPath, kUsageStatsConsentConfigPath});
return *unprivileged_keys;
}
DaemonController::DaemonController(std::unique_ptr<Delegate> delegate)
: caller_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
delegate_(std::move(delegate)) {
// Launch the delegate thread.
delegate_thread_ = std::make_unique<AutoThread>(kDaemonControllerThreadName);
#if BUILDFLAG(IS_WIN)
delegate_thread_->SetComInitType(AutoThread::COM_INIT_STA);
delegate_task_runner_ =
delegate_thread_->StartWithType(base::MessagePumpType::UI);
#else
delegate_task_runner_ =
delegate_thread_->StartWithType(base::MessagePumpType::DEFAULT);
#endif
}
bool DaemonController::is_privileged() const {
return delegate_->is_privileged();
}
#if BUILDFLAG(IS_LINUX)
bool DaemonController::is_multi_process() const {
return delegate_->is_multi_process();
}
#endif
DaemonController::State DaemonController::GetState() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
return delegate_->GetState();
}
void DaemonController::GetConfig(GetConfigCallback done) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
GetConfigCallback wrapped_done =
base::BindOnce(&DaemonController::InvokeConfigCallbackAndScheduleNext,
this, std::move(done));
base::OnceClosure request = base::BindOnce(&DaemonController::DoGetConfig,
this, std::move(wrapped_done));
ServiceOrQueueRequest(std::move(request));
}
void DaemonController::CheckPermission(bool it2me, BoolCallback callback) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
return delegate_->CheckPermission(it2me, std::move(callback));
}
void DaemonController::SetConfigAndStart(base::DictValue config,
bool consent,
CompletionCallback done) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CompletionCallback wrapped_done =
base::BindOnce(&DaemonController::InvokeCompletionCallbackAndScheduleNext,
this, std::move(done));
base::OnceClosure request =
base::BindOnce(&DaemonController::DoSetConfigAndStart, this,
std::move(config), consent, std::move(wrapped_done));
ServiceOrQueueRequest(std::move(request));
}
void DaemonController::UpdateConfig(base::DictValue config,
CompletionCallback done) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
for (const char* key : kReadonlyKeys) {
if (config.Find(key)) {
LOG(ERROR) << "Cannot update config: '" << key << "' is read-only.";
std::move(done).Run(RESULT_FAILED);
return;
}
}
CompletionCallback wrapped_done =
base::BindOnce(&DaemonController::InvokeCompletionCallbackAndScheduleNext,
this, std::move(done));
base::OnceClosure request =
base::BindOnce(&DaemonController::DoUpdateConfig, this, std::move(config),
std::move(wrapped_done));
ServiceOrQueueRequest(std::move(request));
}
void DaemonController::Stop(CompletionCallback done) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
CompletionCallback wrapped_done =
base::BindOnce(&DaemonController::InvokeCompletionCallbackAndScheduleNext,
this, std::move(done));
base::OnceClosure request =
base::BindOnce(&DaemonController::DoStop, this, std::move(wrapped_done));
ServiceOrQueueRequest(std::move(request));
}
void DaemonController::GetUsageStatsConsent(GetUsageStatsConsentCallback done) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
GetUsageStatsConsentCallback wrapped_done =
base::BindOnce(&DaemonController::InvokeConsentCallbackAndScheduleNext,
this, std::move(done));
base::OnceClosure request = base::BindOnce(
&DaemonController::DoGetUsageStatsConsent, this, std::move(wrapped_done));
ServiceOrQueueRequest(std::move(request));
}
DaemonController::~DaemonController() {
// Make sure |delegate_| is deleted on the background thread.
delegate_task_runner_->DeleteSoon(FROM_HERE, delegate_.release());
// Stop the thread.
delegate_task_runner_ = nullptr;
caller_task_runner_->DeleteSoon(FROM_HERE, delegate_thread_.release());
}
void DaemonController::DoGetConfig(GetConfigCallback done) {
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
std::optional<base::DictValue> config = delegate_->GetConfig();
if (config.has_value()) {
for (auto it = config->begin(); it != config->end();) {
// Do not include other keys since they may contain sensitive information.
if (!GetUnprivilegedConfigKeys().contains(it->first)) {
LOG(ERROR) << "Removed unknown key: " << it->first;
it = config->erase(it);
} else {
++it;
}
}
}
caller_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(done), std::move(config)));
}
void DaemonController::DoSetConfigAndStart(base::DictValue config,
bool consent,
CompletionCallback done) {
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
delegate_->SetConfigAndStart(std::move(config), consent, std::move(done));
}
void DaemonController::DoUpdateConfig(base::DictValue config,
CompletionCallback done) {
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
delegate_->UpdateConfig(std::move(config), std::move(done));
}
void DaemonController::DoStop(CompletionCallback done) {
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
delegate_->Stop(std::move(done));
}
void DaemonController::DoGetUsageStatsConsent(
GetUsageStatsConsentCallback done) {
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
UsageStatsConsent consent = delegate_->GetUsageStatsConsent();
caller_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(done), consent));
}
void DaemonController::InvokeCompletionCallbackAndScheduleNext(
CompletionCallback done,
AsyncResult result) {
if (!caller_task_runner_->BelongsToCurrentThread()) {
caller_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&DaemonController::InvokeCompletionCallbackAndScheduleNext, this,
std::move(done), result));
return;
}
std::move(done).Run(result);
OnServicingDone();
}
void DaemonController::InvokeConfigCallbackAndScheduleNext(
GetConfigCallback done,
std::optional<base::DictValue> config) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
std::move(done).Run(std::move(config));
OnServicingDone();
}
void DaemonController::InvokeConsentCallbackAndScheduleNext(
GetUsageStatsConsentCallback done,
const UsageStatsConsent& consent) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
std::move(done).Run(consent);
OnServicingDone();
}
void DaemonController::OnServicingDone() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
servicing_request_ = false;
ServiceNextRequest();
}
void DaemonController::ServiceOrQueueRequest(base::OnceClosure request) {
pending_requests_.push(std::move(request));
if (!servicing_request_) {
ServiceNextRequest();
}
}
void DaemonController::ServiceNextRequest() {
if (!pending_requests_.empty()) {
base::OnceClosure request = std::move(pending_requests_.front());
pending_requests_.pop();
delegate_task_runner_->PostTask(FROM_HERE, std::move(request));
servicing_request_ = true;
}
}
#if BUILDFLAG(IS_CHROMEOS)
scoped_refptr<DaemonController> DaemonController::Create() {
NOTREACHED();
}
#endif
} // namespace remoting