Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: remoting/host/policy_hack/nat_policy_linux.cc

Issue 10804040: [Chromoting] Refactor the host policy watcher so that policies can easily be added. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix for Windows. Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/host/policy_hack/nat_policy.cc ('k') | remoting/host/policy_hack/nat_policy_mac.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 // Most of this code is copied from various classes in
6 // src/chrome/browser/policy. In particular, look at
7 //
8 // file_based_policy_loader.{h,cc}
9 // config_dir_policy_provider.{h,cc}
10 //
11 // This is a reduction of the functionality in those classes.
12
13 #include <set>
14
15 #include "remoting/host/policy_hack/nat_policy.h"
16
17 #include "base/bind.h"
18 #include "base/compiler_specific.h"
19 #include "base/file_path.h"
20 #include "base/file_util.h"
21 #include "base/files/file_path_watcher.h"
22 #include "base/json/json_file_value_serializer.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/memory/weak_ptr.h"
25 #include "base/single_thread_task_runner.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "base/time.h"
28 #include "base/values.h"
29
30 namespace remoting {
31 namespace policy_hack {
32
33 namespace {
34
35 const FilePath::CharType kPolicyDir[] =
36 FILE_PATH_LITERAL("/etc/opt/chrome/policies/managed");
37
38 // Amount of time we wait for the files on disk to settle before trying to load
39 // them. This alleviates the problem of reading partially written files and
40 // makes it possible to batch quasi-simultaneous changes.
41 const int kSettleIntervalSeconds = 5;
42
43 } // namespace
44
45 class NatPolicyLinux : public NatPolicy {
46 public:
47 NatPolicyLinux(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
48 const FilePath& config_dir)
49 : NatPolicy(task_runner),
50 config_dir_(config_dir),
51 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
52 // Detach the factory because we ensure that only the policy thread ever
53 // calls methods on this. Also, the API contract of having to call
54 // StopWatching() (which signals completion) after StartWatching()
55 // before this object can be destructed ensures there are no users of
56 // this object before it is destructed.
57 weak_factory_.DetachFromThread();
58 }
59
60 virtual ~NatPolicyLinux() {}
61
62 protected:
63 virtual void StartWatchingInternal() OVERRIDE {
64 DCHECK(OnPolicyThread());
65 watcher_.reset(new base::files::FilePathWatcher());
66
67 if (!config_dir_.empty() &&
68 !watcher_->Watch(
69 config_dir_,
70 new FilePathWatcherDelegate(weak_factory_.GetWeakPtr()))) {
71 OnFilePathError(config_dir_);
72 }
73
74 // There might have been changes to the directory in the time between
75 // construction of the loader and initialization of the watcher. Call reload
76 // to detect if that is the case.
77 Reload();
78
79 ScheduleFallbackReloadTask();
80 }
81
82 virtual void StopWatchingInternal() OVERRIDE {
83 DCHECK(OnPolicyThread());
84 // Cancel any inflight requests.
85 watcher_.reset();
86 }
87
88 // Called by FilePathWatcherDelegate.
89 virtual void OnFilePathError(const FilePath& path) {
90 LOG(ERROR) << "NatPolicyLinux on " << path.value()
91 << " failed.";
92 }
93
94 // Called by FilePathWatcherDelegate.
95 virtual void OnFilePathChanged(const FilePath& path) {
96 DCHECK(OnPolicyThread());
97
98 Reload();
99 }
100
101 private:
102 // Needed to avoid refcounting NatPolicyLinux.
103 class FilePathWatcherDelegate :
104 public base::files::FilePathWatcher::Delegate {
105 public:
106 FilePathWatcherDelegate(base::WeakPtr<NatPolicyLinux> policy_watcher)
107 : policy_watcher_(policy_watcher) {
108 }
109
110 virtual void OnFilePathError(const FilePath& path) {
111 if (policy_watcher_) {
112 policy_watcher_->OnFilePathError(path);
113 }
114 }
115
116 virtual void OnFilePathChanged(const FilePath& path) {
117 if (policy_watcher_) {
118 policy_watcher_->OnFilePathChanged(path);
119 }
120 }
121
122 protected:
123 virtual ~FilePathWatcherDelegate() {}
124
125 private:
126 base::WeakPtr<NatPolicyLinux> policy_watcher_;
127 };
128
129 base::Time GetLastModification() {
130 DCHECK(OnPolicyThread());
131 base::Time last_modification = base::Time();
132 base::PlatformFileInfo file_info;
133
134 // If the path does not exist or points to a directory, it's safe to load.
135 if (!file_util::GetFileInfo(config_dir_, &file_info) ||
136 !file_info.is_directory) {
137 return last_modification;
138 }
139
140 // Enumerate the files and find the most recent modification timestamp.
141 file_util::FileEnumerator file_enumerator(config_dir_,
142 false,
143 file_util::FileEnumerator::FILES);
144 for (FilePath config_file = file_enumerator.Next();
145 !config_file.empty();
146 config_file = file_enumerator.Next()) {
147 if (file_util::GetFileInfo(config_file, &file_info) &&
148 !file_info.is_directory) {
149 last_modification = std::max(last_modification,
150 file_info.last_modified);
151 }
152 }
153
154 return last_modification;
155 }
156
157 // Caller owns the value.
158 DictionaryValue* Load() {
159 DCHECK(OnPolicyThread());
160 // Enumerate the files and sort them lexicographically.
161 std::set<FilePath> files;
162 file_util::FileEnumerator file_enumerator(config_dir_, false,
163 file_util::FileEnumerator::FILES);
164 for (FilePath config_file_path = file_enumerator.Next();
165 !config_file_path.empty(); config_file_path = file_enumerator.Next())
166 files.insert(config_file_path);
167
168 // Start with an empty dictionary and merge the files' contents.
169 DictionaryValue* policy = new DictionaryValue();
170 for (std::set<FilePath>::iterator config_file_iter = files.begin();
171 config_file_iter != files.end(); ++config_file_iter) {
172 JSONFileValueSerializer deserializer(*config_file_iter);
173 deserializer.set_allow_trailing_comma(true);
174 int error_code = 0;
175 std::string error_msg;
176 scoped_ptr<Value> value(
177 deserializer.Deserialize(&error_code, &error_msg));
178 if (!value.get()) {
179 LOG(WARNING) << "Failed to read configuration file "
180 << config_file_iter->value() << ": " << error_msg;
181 continue;
182 }
183 if (!value->IsType(Value::TYPE_DICTIONARY)) {
184 LOG(WARNING) << "Expected JSON dictionary in configuration file "
185 << config_file_iter->value();
186 continue;
187 }
188 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get()));
189 }
190
191 return policy;
192 }
193
194 void Reload() {
195 DCHECK(OnPolicyThread());
196 // Check the directory time in order to see whether a reload is required.
197 base::TimeDelta delay;
198 base::Time now = base::Time::Now();
199 if (!IsSafeToReloadPolicy(now, &delay)) {
200 ScheduleReloadTask(delay);
201 return;
202 }
203
204 // Check again in case the directory has changed while reading it.
205 if (!IsSafeToReloadPolicy(now, &delay)) {
206 ScheduleReloadTask(delay);
207 return;
208 }
209
210 // Load the policy definitions.
211 scoped_ptr<DictionaryValue> new_policy(Load());
212 UpdateNatPolicy(new_policy.get());
213
214 ScheduleFallbackReloadTask();
215 }
216
217 bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay) {
218 DCHECK(OnPolicyThread());
219 DCHECK(delay);
220 const base::TimeDelta kSettleInterval =
221 base::TimeDelta::FromSeconds(kSettleIntervalSeconds);
222
223 base::Time last_modification = GetLastModification();
224 if (last_modification.is_null())
225 return true;
226
227 if (last_modification_file_.is_null())
228 last_modification_file_ = last_modification;
229
230 // If there was a change since the last recorded modification, wait some
231 // more.
232 if (last_modification != last_modification_file_) {
233 last_modification_file_ = last_modification;
234 last_modification_clock_ = now;
235 *delay = kSettleInterval;
236 return false;
237 }
238
239 // Check whether the settle interval has elapsed.
240 base::TimeDelta age = now - last_modification_clock_;
241 if (age < kSettleInterval) {
242 *delay = kSettleInterval - age;
243 return false;
244 }
245
246 return true;
247 }
248
249 // Managed with a scoped_ptr rather than being declared as an inline member to
250 // decouple the watcher's life cycle from the NatPolicyLinux. This decoupling
251 // makes it possible to destroy the watcher before the loader's destructor is
252 // called (e.g. during Stop), since |watcher_| internally holds a reference to
253 // the loader and keeps it alive.
254 scoped_ptr<base::files::FilePathWatcher> watcher_;
255
256 // Records last known modification timestamp of |config_dir_|.
257 base::Time last_modification_file_;
258
259 // The wall clock time at which the last modification timestamp was
260 // recorded. It's better to not assume the file notification time and the
261 // wall clock times come from the same source, just in case there is some
262 // non-local filesystem involved.
263 base::Time last_modification_clock_;
264
265 const FilePath config_dir_;
266
267 // Allows us to cancel any inflight FileWatcher events or scheduled reloads.
268 base::WeakPtrFactory<NatPolicyLinux> weak_factory_;
269 };
270
271 NatPolicy* NatPolicy::Create(
272 scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
273 FilePath policy_dir(kPolicyDir);
274 return new NatPolicyLinux(task_runner, policy_dir);
275 }
276
277 } // namespace policy_hack
278 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/policy_hack/nat_policy.cc ('k') | remoting/host/policy_hack/nat_policy_mac.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698