- add sources.
[platform/framework/web/crosswalk.git] / src / remoting / host / config_file_watcher.cc
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
5 #include "remoting/host/config_file_watcher.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/file_util.h"
12 #include "base/files/file_path_watcher.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/timer/timer.h"
17
18 namespace remoting {
19
20 // The name of the command-line switch used to specify the host configuration
21 // file to use.
22 const char kHostConfigSwitchName[] = "host-config";
23
24 const base::FilePath::CharType kDefaultHostConfigFile[] =
25     FILE_PATH_LITERAL("host.json");
26
27 #if defined(OS_WIN)
28 // Maximum number of times to try reading the configuration file before
29 // reporting an error.
30 const int kMaxRetries = 3;
31 #endif  // defined(OS_WIN)
32
33 class ConfigFileWatcherImpl
34     : public base::RefCountedThreadSafe<ConfigFileWatcherImpl> {
35  public:
36   // Creates a configuration file watcher that lives on the |io_task_runner|
37   // thread but posts config file updates on on |main_task_runner|.
38   ConfigFileWatcherImpl(
39       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
40       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
41       ConfigFileWatcher::Delegate* delegate);
42
43   // Starts watching |config_path|.
44   void Watch(const base::FilePath& config_path);
45
46   // Stops watching the configuration file.
47   void StopWatching();
48
49  private:
50   friend class base::RefCountedThreadSafe<ConfigFileWatcherImpl>;
51   virtual ~ConfigFileWatcherImpl();
52
53   void FinishStopping();
54
55   // Called every time the host configuration file is updated.
56   void OnConfigUpdated(const base::FilePath& path, bool error);
57
58   // Reads the configuration file and passes it to the delegate.
59   void ReloadConfig();
60
61   std::string config_;
62   base::FilePath config_path_;
63
64   scoped_ptr<base::DelayTimer<ConfigFileWatcherImpl> > config_updated_timer_;
65
66   // Number of times an attempt to read the configuration file failed.
67   int retries_;
68
69   // Monitors the host configuration file.
70   scoped_ptr<base::FilePathWatcher> config_watcher_;
71
72   base::WeakPtrFactory<ConfigFileWatcher::Delegate> delegate_weak_factory_;
73   base::WeakPtr<ConfigFileWatcher::Delegate> delegate_;
74
75   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
76   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
77
78   DISALLOW_COPY_AND_ASSIGN(ConfigFileWatcherImpl);
79 };
80
81 ConfigFileWatcher::Delegate::~Delegate() {
82 }
83
84 ConfigFileWatcher::ConfigFileWatcher(
85     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
86     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
87     Delegate* delegate)
88     : impl_(new ConfigFileWatcherImpl(main_task_runner,
89                                       io_task_runner, delegate)) {
90 }
91
92 ConfigFileWatcher::~ConfigFileWatcher() {
93   impl_->StopWatching();
94   impl_ = NULL;
95 }
96
97 void ConfigFileWatcher::Watch(const base::FilePath& config_path) {
98   impl_->Watch(config_path);
99 }
100
101 ConfigFileWatcherImpl::ConfigFileWatcherImpl(
102     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
103     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
104     ConfigFileWatcher::Delegate* delegate)
105     : retries_(0),
106       delegate_weak_factory_(delegate),
107       delegate_(delegate_weak_factory_.GetWeakPtr()),
108       main_task_runner_(main_task_runner),
109       io_task_runner_(io_task_runner) {
110   DCHECK(main_task_runner_->BelongsToCurrentThread());
111 }
112
113 void ConfigFileWatcherImpl::Watch(const base::FilePath& config_path) {
114   if (!io_task_runner_->BelongsToCurrentThread()) {
115     io_task_runner_->PostTask(
116         FROM_HERE,
117         base::Bind(&ConfigFileWatcherImpl::Watch, this, config_path));
118     return;
119   }
120
121   DCHECK(config_path_.empty());
122   DCHECK(!config_updated_timer_);
123   DCHECK(!config_watcher_);
124
125   // Create the timer that will be used for delayed-reading the configuration
126   // file.
127   config_updated_timer_.reset(new base::DelayTimer<ConfigFileWatcherImpl>(
128       FROM_HERE, base::TimeDelta::FromSeconds(2), this,
129       &ConfigFileWatcherImpl::ReloadConfig));
130
131   // Start watching the configuration file.
132   config_watcher_.reset(new base::FilePathWatcher());
133   config_path_ = config_path;
134   if (!config_watcher_->Watch(
135           config_path_, false,
136           base::Bind(&ConfigFileWatcherImpl::OnConfigUpdated, this))) {
137     PLOG(ERROR) << "Couldn't watch file '" << config_path_.value() << "'";
138     main_task_runner_->PostTask(
139         FROM_HERE,
140         base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError,
141                    delegate_));
142     return;
143   }
144
145   // Force reloading of the configuration file at least once.
146   ReloadConfig();
147 }
148
149 void ConfigFileWatcherImpl::StopWatching() {
150   DCHECK(main_task_runner_->BelongsToCurrentThread());
151
152   delegate_weak_factory_.InvalidateWeakPtrs();
153   io_task_runner_->PostTask(
154       FROM_HERE, base::Bind(&ConfigFileWatcherImpl::FinishStopping, this));
155 }
156
157 ConfigFileWatcherImpl::~ConfigFileWatcherImpl() {
158   DCHECK(!config_updated_timer_);
159   DCHECK(!config_watcher_);
160 }
161
162 void ConfigFileWatcherImpl::FinishStopping() {
163   DCHECK(io_task_runner_->BelongsToCurrentThread());
164
165   config_updated_timer_.reset();
166   config_watcher_.reset();
167 }
168
169 void ConfigFileWatcherImpl::OnConfigUpdated(const base::FilePath& path,
170                                             bool error) {
171   DCHECK(io_task_runner_->BelongsToCurrentThread());
172
173   // Call ReloadConfig() after a short delay, so that we will not try to read
174   // the updated configuration file before it has been completely written.
175   // If the writer moves the new configuration file into place atomically,
176   // this delay may not be necessary.
177   if (!error && config_path_ == path)
178     config_updated_timer_->Reset();
179 }
180
181 void ConfigFileWatcherImpl::ReloadConfig() {
182   DCHECK(io_task_runner_->BelongsToCurrentThread());
183
184   std::string config;
185   if (!base::ReadFileToString(config_path_, &config)) {
186 #if defined(OS_WIN)
187     // EACCESS may indicate a locking or sharing violation. Retry a few times
188     // before reporting an error.
189     if (errno == EACCES && retries_ < kMaxRetries) {
190       PLOG(WARNING) << "Failed to read '" << config_path_.value() << "'";
191
192       retries_ += 1;
193       config_updated_timer_->Reset();
194       return;
195     }
196 #endif  // defined(OS_WIN)
197
198     PLOG(ERROR) << "Failed to read '" << config_path_.value() << "'";
199     main_task_runner_->PostTask(
200         FROM_HERE,
201         base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError,
202                    delegate_));
203     return;
204   }
205
206   retries_ = 0;
207
208   // Post an updated configuration only if it has actually changed.
209   if (config_ != config) {
210     config_ = config;
211     main_task_runner_->PostTask(
212         FROM_HERE,
213         base::Bind(&ConfigFileWatcher::Delegate::OnConfigUpdated, delegate_,
214                    config_));
215   }
216 }
217
218 }  // namespace remoting