| // 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 |