123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- // Copyright (c) 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 "remoting/host/config_file_watcher.h"
- #include <memory>
- #include <string>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/files/file_path_watcher.h"
- #include "base/files/file_util.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/weak_ptr.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/timer/timer.h"
- #include "build/build_config.h"
- namespace remoting {
- // The name of the command-line switch used to specify the host configuration
- // file to use.
- const char kHostConfigSwitchName[] = "host-config";
- const base::FilePath::CharType kDefaultHostConfigFile[] =
- FILE_PATH_LITERAL("host.json");
- #if BUILDFLAG(IS_WIN)
- // Maximum number of times to try reading the configuration file before
- // reporting an error.
- const int kMaxRetries = 3;
- #endif // BUILDFLAG(IS_WIN)
- class ConfigFileWatcherImpl
- : public base::RefCountedThreadSafe<ConfigFileWatcherImpl> {
- public:
- // Creates a configuration file watcher that lives on the |io_task_runner|
- // thread but posts config file updates on on |main_task_runner|.
- ConfigFileWatcherImpl(
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- const base::FilePath& config_path);
- ConfigFileWatcherImpl(const ConfigFileWatcherImpl&) = delete;
- ConfigFileWatcherImpl& operator=(const ConfigFileWatcherImpl&) = delete;
- // Notify |delegate| of config changes.
- void Watch(ConfigWatcher::Delegate* delegate);
- // Stops watching the configuration file.
- void StopWatching();
- private:
- friend class base::RefCountedThreadSafe<ConfigFileWatcherImpl>;
- virtual ~ConfigFileWatcherImpl();
- void FinishStopping();
- void WatchOnIoThread();
- // Called every time the host configuration file is updated.
- void OnConfigUpdated(const base::FilePath& path, bool error);
- // Called to notify the delegate of updates/errors in the main thread.
- void NotifyUpdate(const std::string& config);
- void NotifyError();
- // Reads the configuration file and passes it to the delegate.
- void ReloadConfig();
- std::string config_;
- base::FilePath config_path_;
- std::unique_ptr<base::DelayTimer> config_updated_timer_;
- // Number of times an attempt to read the configuration file failed.
- int retries_;
- // Monitors the host configuration file.
- std::unique_ptr<base::FilePathWatcher> config_watcher_;
- raw_ptr<ConfigWatcher::Delegate> delegate_;
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
- base::WeakPtrFactory<ConfigFileWatcherImpl> weak_factory_{this};
- };
- ConfigFileWatcher::ConfigFileWatcher(
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- const base::FilePath& config_path)
- : impl_(new ConfigFileWatcherImpl(main_task_runner,
- io_task_runner, config_path)) {
- }
- ConfigFileWatcher::~ConfigFileWatcher() {
- impl_->StopWatching();
- impl_ = nullptr;
- }
- void ConfigFileWatcher::Watch(ConfigWatcher::Delegate* delegate) {
- impl_->Watch(delegate);
- }
- ConfigFileWatcherImpl::ConfigFileWatcherImpl(
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- const base::FilePath& config_path)
- : config_path_(config_path),
- retries_(0),
- delegate_(nullptr),
- main_task_runner_(main_task_runner),
- io_task_runner_(io_task_runner) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- }
- void ConfigFileWatcherImpl::Watch(ConfigWatcher::Delegate* delegate) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- DCHECK(!delegate_);
- delegate_ = delegate;
- io_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::WatchOnIoThread, this));
- }
- void ConfigFileWatcherImpl::WatchOnIoThread() {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- DCHECK(!config_updated_timer_);
- DCHECK(!config_watcher_);
- // Create the timer that will be used for delayed-reading the configuration
- // file.
- config_updated_timer_ = std::make_unique<base::DelayTimer>(
- FROM_HERE, base::Seconds(2), this, &ConfigFileWatcherImpl::ReloadConfig);
- // Start watching the configuration file.
- config_watcher_ = std::make_unique<base::FilePathWatcher>();
- if (!config_watcher_->Watch(
- config_path_, base::FilePathWatcher::Type::kNonRecursive,
- base::BindRepeating(&ConfigFileWatcherImpl::OnConfigUpdated, this))) {
- PLOG(ERROR) << "Couldn't watch file '" << config_path_.value() << "'";
- main_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::NotifyError,
- weak_factory_.GetWeakPtr()));
- return;
- }
- // Force reloading of the configuration file at least once.
- ReloadConfig();
- }
- void ConfigFileWatcherImpl::StopWatching() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- weak_factory_.InvalidateWeakPtrs();
- io_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::FinishStopping, this));
- }
- ConfigFileWatcherImpl::~ConfigFileWatcherImpl() {
- DCHECK(!config_updated_timer_);
- DCHECK(!config_watcher_);
- }
- void ConfigFileWatcherImpl::FinishStopping() {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- config_updated_timer_.reset();
- config_watcher_.reset();
- }
- void ConfigFileWatcherImpl::OnConfigUpdated(const base::FilePath& path,
- bool error) {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- // Call ReloadConfig() after a short delay, so that we will not try to read
- // the updated configuration file before it has been completely written.
- // If the writer moves the new configuration file into place atomically,
- // this delay may not be necessary.
- if (!error && config_path_ == path)
- config_updated_timer_->Reset();
- }
- void ConfigFileWatcherImpl::NotifyError() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- delegate_->OnConfigWatcherError();
- }
- void ConfigFileWatcherImpl::NotifyUpdate(const std::string& config) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- delegate_->OnConfigUpdated(config_);
- }
- void ConfigFileWatcherImpl::ReloadConfig() {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- std::string config;
- if (!base::ReadFileToString(config_path_, &config)) {
- #if BUILDFLAG(IS_WIN)
- // EACCESS may indicate a locking or sharing violation. Retry a few times
- // before reporting an error.
- if (errno == EACCES && retries_ < kMaxRetries) {
- PLOG(WARNING) << "Failed to read '" << config_path_.value() << "'";
- retries_ += 1;
- config_updated_timer_->Reset();
- return;
- }
- #endif // BUILDFLAG(IS_WIN)
- PLOG(ERROR) << "Failed to read '" << config_path_.value() << "'";
- main_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::NotifyError,
- weak_factory_.GetWeakPtr()));
- return;
- }
- retries_ = 0;
- // Post an updated configuration only if it has actually changed.
- if (config_ != config) {
- config_ = config;
- main_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::NotifyUpdate,
- weak_factory_.GetWeakPtr(), config_));
- }
- }
- } // namespace remoting
|