config_file_watcher.cc 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/host/config_file_watcher.h"
  5. #include <memory>
  6. #include <string>
  7. #include "base/bind.h"
  8. #include "base/callback_helpers.h"
  9. #include "base/files/file_path_watcher.h"
  10. #include "base/files/file_util.h"
  11. #include "base/logging.h"
  12. #include "base/memory/raw_ptr.h"
  13. #include "base/memory/weak_ptr.h"
  14. #include "base/task/single_thread_task_runner.h"
  15. #include "base/timer/timer.h"
  16. #include "build/build_config.h"
  17. namespace remoting {
  18. // The name of the command-line switch used to specify the host configuration
  19. // file to use.
  20. const char kHostConfigSwitchName[] = "host-config";
  21. const base::FilePath::CharType kDefaultHostConfigFile[] =
  22. FILE_PATH_LITERAL("host.json");
  23. #if BUILDFLAG(IS_WIN)
  24. // Maximum number of times to try reading the configuration file before
  25. // reporting an error.
  26. const int kMaxRetries = 3;
  27. #endif // BUILDFLAG(IS_WIN)
  28. class ConfigFileWatcherImpl
  29. : public base::RefCountedThreadSafe<ConfigFileWatcherImpl> {
  30. public:
  31. // Creates a configuration file watcher that lives on the |io_task_runner|
  32. // thread but posts config file updates on on |main_task_runner|.
  33. ConfigFileWatcherImpl(
  34. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
  35. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
  36. const base::FilePath& config_path);
  37. ConfigFileWatcherImpl(const ConfigFileWatcherImpl&) = delete;
  38. ConfigFileWatcherImpl& operator=(const ConfigFileWatcherImpl&) = delete;
  39. // Notify |delegate| of config changes.
  40. void Watch(ConfigWatcher::Delegate* delegate);
  41. // Stops watching the configuration file.
  42. void StopWatching();
  43. private:
  44. friend class base::RefCountedThreadSafe<ConfigFileWatcherImpl>;
  45. virtual ~ConfigFileWatcherImpl();
  46. void FinishStopping();
  47. void WatchOnIoThread();
  48. // Called every time the host configuration file is updated.
  49. void OnConfigUpdated(const base::FilePath& path, bool error);
  50. // Called to notify the delegate of updates/errors in the main thread.
  51. void NotifyUpdate(const std::string& config);
  52. void NotifyError();
  53. // Reads the configuration file and passes it to the delegate.
  54. void ReloadConfig();
  55. std::string config_;
  56. base::FilePath config_path_;
  57. std::unique_ptr<base::DelayTimer> config_updated_timer_;
  58. // Number of times an attempt to read the configuration file failed.
  59. int retries_;
  60. // Monitors the host configuration file.
  61. std::unique_ptr<base::FilePathWatcher> config_watcher_;
  62. raw_ptr<ConfigWatcher::Delegate> delegate_;
  63. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
  64. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
  65. base::WeakPtrFactory<ConfigFileWatcherImpl> weak_factory_{this};
  66. };
  67. ConfigFileWatcher::ConfigFileWatcher(
  68. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
  69. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
  70. const base::FilePath& config_path)
  71. : impl_(new ConfigFileWatcherImpl(main_task_runner,
  72. io_task_runner, config_path)) {
  73. }
  74. ConfigFileWatcher::~ConfigFileWatcher() {
  75. impl_->StopWatching();
  76. impl_ = nullptr;
  77. }
  78. void ConfigFileWatcher::Watch(ConfigWatcher::Delegate* delegate) {
  79. impl_->Watch(delegate);
  80. }
  81. ConfigFileWatcherImpl::ConfigFileWatcherImpl(
  82. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
  83. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
  84. const base::FilePath& config_path)
  85. : config_path_(config_path),
  86. retries_(0),
  87. delegate_(nullptr),
  88. main_task_runner_(main_task_runner),
  89. io_task_runner_(io_task_runner) {
  90. DCHECK(main_task_runner_->BelongsToCurrentThread());
  91. }
  92. void ConfigFileWatcherImpl::Watch(ConfigWatcher::Delegate* delegate) {
  93. DCHECK(main_task_runner_->BelongsToCurrentThread());
  94. DCHECK(!delegate_);
  95. delegate_ = delegate;
  96. io_task_runner_->PostTask(
  97. FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::WatchOnIoThread, this));
  98. }
  99. void ConfigFileWatcherImpl::WatchOnIoThread() {
  100. DCHECK(io_task_runner_->BelongsToCurrentThread());
  101. DCHECK(!config_updated_timer_);
  102. DCHECK(!config_watcher_);
  103. // Create the timer that will be used for delayed-reading the configuration
  104. // file.
  105. config_updated_timer_ = std::make_unique<base::DelayTimer>(
  106. FROM_HERE, base::Seconds(2), this, &ConfigFileWatcherImpl::ReloadConfig);
  107. // Start watching the configuration file.
  108. config_watcher_ = std::make_unique<base::FilePathWatcher>();
  109. if (!config_watcher_->Watch(
  110. config_path_, base::FilePathWatcher::Type::kNonRecursive,
  111. base::BindRepeating(&ConfigFileWatcherImpl::OnConfigUpdated, this))) {
  112. PLOG(ERROR) << "Couldn't watch file '" << config_path_.value() << "'";
  113. main_task_runner_->PostTask(
  114. FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::NotifyError,
  115. weak_factory_.GetWeakPtr()));
  116. return;
  117. }
  118. // Force reloading of the configuration file at least once.
  119. ReloadConfig();
  120. }
  121. void ConfigFileWatcherImpl::StopWatching() {
  122. DCHECK(main_task_runner_->BelongsToCurrentThread());
  123. weak_factory_.InvalidateWeakPtrs();
  124. io_task_runner_->PostTask(
  125. FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::FinishStopping, this));
  126. }
  127. ConfigFileWatcherImpl::~ConfigFileWatcherImpl() {
  128. DCHECK(!config_updated_timer_);
  129. DCHECK(!config_watcher_);
  130. }
  131. void ConfigFileWatcherImpl::FinishStopping() {
  132. DCHECK(io_task_runner_->BelongsToCurrentThread());
  133. config_updated_timer_.reset();
  134. config_watcher_.reset();
  135. }
  136. void ConfigFileWatcherImpl::OnConfigUpdated(const base::FilePath& path,
  137. bool error) {
  138. DCHECK(io_task_runner_->BelongsToCurrentThread());
  139. // Call ReloadConfig() after a short delay, so that we will not try to read
  140. // the updated configuration file before it has been completely written.
  141. // If the writer moves the new configuration file into place atomically,
  142. // this delay may not be necessary.
  143. if (!error && config_path_ == path)
  144. config_updated_timer_->Reset();
  145. }
  146. void ConfigFileWatcherImpl::NotifyError() {
  147. DCHECK(main_task_runner_->BelongsToCurrentThread());
  148. delegate_->OnConfigWatcherError();
  149. }
  150. void ConfigFileWatcherImpl::NotifyUpdate(const std::string& config) {
  151. DCHECK(main_task_runner_->BelongsToCurrentThread());
  152. delegate_->OnConfigUpdated(config_);
  153. }
  154. void ConfigFileWatcherImpl::ReloadConfig() {
  155. DCHECK(io_task_runner_->BelongsToCurrentThread());
  156. std::string config;
  157. if (!base::ReadFileToString(config_path_, &config)) {
  158. #if BUILDFLAG(IS_WIN)
  159. // EACCESS may indicate a locking or sharing violation. Retry a few times
  160. // before reporting an error.
  161. if (errno == EACCES && retries_ < kMaxRetries) {
  162. PLOG(WARNING) << "Failed to read '" << config_path_.value() << "'";
  163. retries_ += 1;
  164. config_updated_timer_->Reset();
  165. return;
  166. }
  167. #endif // BUILDFLAG(IS_WIN)
  168. PLOG(ERROR) << "Failed to read '" << config_path_.value() << "'";
  169. main_task_runner_->PostTask(
  170. FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::NotifyError,
  171. weak_factory_.GetWeakPtr()));
  172. return;
  173. }
  174. retries_ = 0;
  175. // Post an updated configuration only if it has actually changed.
  176. if (config_ != config) {
  177. config_ = config;
  178. main_task_runner_->PostTask(
  179. FROM_HERE, base::BindOnce(&ConfigFileWatcherImpl::NotifyUpdate,
  180. weak_factory_.GetWeakPtr(), config_));
  181. }
  182. }
  183. } // namespace remoting