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

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

Issue 7599017: Framework to allow Chromoting host to respect NAT traversal policy in linux. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 1-char comment typo Created 9 years, 4 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
OLDNEW
(Empty)
1 // Copyright (c) 2011 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.
Wez 2011/08/11 20:00:27 Is this brand-new code, or cribbed from the main C
awong 2011/08/11 23:54:54 Mostly cribbed, but it's an amalgamation of a bunc
Wez 2011/08/12 00:54:42 Perhaps add a comment indicating where it's cribbe
awong 2011/08/12 00:57:03 Done.
4
5 #include <set>
6
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path_watcher.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop.h"
14 #include "base/scoped_ptr.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/time.h"
17 #include "base/values.h"
18 #include "content/common/json_value_serializer.h"
19 #include "remoting/host/plugin/policy_hack/nat_policy.h"
dmac 2011/08/11 18:42:55 should this include be at the top (given that it i
awong 2011/08/11 19:04:07 Done.
20
21 namespace remoting {
22 namespace policy_hack {
23
24 namespace {
25
26 #if defined(GOOGLE_CHROME_BUILD)
27 const FilePath::CharType kPolicyDir[] =
28 FILE_PATH_LITERAL("/etc/opt/chrome/policies/managed");
29 #else
30 const FilePath::CharType kPolicyDir[] =
31 FILE_PATH_LITERAL("/etc/chromium/policies/managed");
32 #endif
33
34 // The time interval for rechecking policy. This is our fallback in case the
35 // delegate never reports a change to the ReloadObserver.
36 const int kFallbackReloadDelayMinutes = 15;
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(MessageLoop* message_loop,
48 const FilePath& config_dir,
49 const NatPolicy::NatEnabledCallback& nat_enabled_cb)
50 : message_loop_(message_loop),
51 config_dir_(config_dir),
52 nat_enabled_cb_(nat_enabled_cb),
53 current_nat_enabled_state_(false),
54 first_state_published_(false),
55 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
56 // Detach the factory cause we handle ensure that only the message_loop_
dmac 2011/08/11 18:42:55 s/cause/because/ handle ensure? s/uses/threads/?
awong 2011/08/11 19:04:07 Rewrote.
57 // thread ever calls methods on this, and that all uses are ended before
58 // we destroy this object. This allows us to create/destroy
59 // NatPolicyLinux on any thread.
60 weak_factory_.DetachFromThread();
61 }
62
63 virtual ~NatPolicyLinux() {
64 }
dmac 2011/08/11 18:42:55 NIT: standard seems to be to put {} on one line...
awong 2011/08/11 19:04:07 I don't think there is actually a standard on this
65
66 virtual void StartWatching() OVERRIDE {
67 if (MessageLoop::current() != message_loop_) {
68 message_loop_->PostTask(FROM_HERE,
69 base::Bind(&NatPolicyLinux::StartWatching,
70 base::Unretained(this)));
71 return;
72 }
73
74 watcher_.reset(new base::files::FilePathWatcher());
75
76 if (!config_dir_.empty() &&
77 !watcher_->Watch(
78 config_dir_,
79 new FilePathWatcherDelegate(weak_factory_.GetWeakPtr()))) {
80 OnFilePathError(config_dir_);
81 }
82
83 // There might have been changes to the directory in the time between
84 // construction of the loader and initialization of the watcher. Call reload
85 // to detect if that is the case.
86 Reload();
87
88 ScheduleFallbackReloadTask();
89 }
90
91 virtual void StopWatching(base::WaitableEvent* done) OVERRIDE {
92 if (MessageLoop::current() != message_loop_) {
93 message_loop_->PostTask(FROM_HERE,
94 base::Bind(&NatPolicyLinux::StopWatching,
95 base::Unretained(this), done));
96 return;
97 }
98
99 // Cancel any inflight requests.
100 weak_factory_.InvalidateWeakPtrs();
101 watcher_.reset();
102
103 done->Signal();
104 }
105
106 // Called by FilePathWatcherDelegate.
107 virtual void OnFilePathError(const FilePath& path) {
108 LOG(ERROR) << "NatPolicyLinux on " << path.value()
109 << " failed.";
110 }
111
112 // Called by FilePathWatcherDelegate.
113 virtual void OnFilePathChanged(const FilePath& path) {
114 DCHECK_EQ(MessageLoop::current(), message_loop_);
115
116 Reload();
117 }
118
119 private:
120 // Needed since NatPolicyLinux ends up getting its refcount from
dmac 2011/08/11 18:42:55 NatPolicyLinux ends up getting its refcount from N
awong 2011/08/11 19:04:07 Again stale. Originally, itw as getting its refco
121 // NatPolicyLinux.
122 class FilePathWatcherDelegate :
123 public base::files::FilePathWatcher::Delegate {
124 public:
125 FilePathWatcherDelegate(base::WeakPtr<NatPolicyLinux> policy_watcher)
126 : policy_watcher_(policy_watcher) {
127 }
128
129 virtual void OnFilePathError(const FilePath& path) {
130 if (policy_watcher_) {
131 policy_watcher_->OnFilePathError(path);
132 }
133 }
134
135 virtual void OnFilePathChanged(const FilePath& path) {
136 if (policy_watcher_) {
137 policy_watcher_->OnFilePathChanged(path);
138 }
139 }
140
141 private:
142 base::WeakPtr<NatPolicyLinux> policy_watcher_;
143 };
144
145 void ScheduleFallbackReloadTask() {
146 DCHECK_EQ(MessageLoop::current(), message_loop_);
147 ScheduleReloadTask(
148 base::TimeDelta::FromMinutes(kFallbackReloadDelayMinutes));
149 }
150
151 void ScheduleReloadTask(const base::TimeDelta& delay) {
152 DCHECK_EQ(MessageLoop::current(), message_loop_);
153 message_loop_->PostDelayedTask(
154 FROM_HERE,
155 base::Bind(&NatPolicyLinux::Reload, weak_factory_.GetWeakPtr()),
156 delay.InMilliseconds());
157 }
158
159 base::Time GetLastModification() {
160 DCHECK_EQ(MessageLoop::current(), message_loop_);
161 base::Time last_modification = base::Time();
162 base::PlatformFileInfo file_info;
163
164 // If the path does not exist or points to a directory, it's safe to load.
165 if (!file_util::GetFileInfo(config_dir_, &file_info) ||
166 !file_info.is_directory) {
167 return last_modification;
168 }
169
170 // Enumerate the files and find the most recent modification timestamp.
171 file_util::FileEnumerator file_enumerator(config_dir_,
172 false,
173 file_util::FileEnumerator::FILES);
174 for (FilePath config_file = file_enumerator.Next();
175 !config_file.empty();
176 config_file = file_enumerator.Next()) {
177 if (file_util::GetFileInfo(config_file, &file_info) &&
178 !file_info.is_directory) {
179 last_modification = std::max(last_modification,
180 file_info.last_modified);
181 }
182 }
183
184 return last_modification;
185 }
186
187 // Caller owns the value.
188 DictionaryValue* Load() {
189 DCHECK_EQ(MessageLoop::current(), message_loop_);
190 // Enumerate the files and sort them lexicographically.
191 std::set<FilePath> files;
192 file_util::FileEnumerator file_enumerator(config_dir_, false,
193 file_util::FileEnumerator::FILES);
194 for (FilePath config_file_path = file_enumerator.Next();
195 !config_file_path.empty(); config_file_path = file_enumerator.Next())
196 files.insert(config_file_path);
197
198 // Start with an empty dictionary and merge the files' contents.
199 DictionaryValue* policy = new DictionaryValue;
200 for (std::set<FilePath>::iterator config_file_iter = files.begin();
201 config_file_iter != files.end(); ++config_file_iter) {
202 JSONFileValueSerializer deserializer(*config_file_iter);
203 int error_code = 0;
204 std::string error_msg;
205 scoped_ptr<Value> value(
206 deserializer.Deserialize(&error_code, &error_msg));
207 if (!value.get()) {
208 LOG(WARNING) << "Failed to read configuration file "
209 << config_file_iter->value() << ": " << error_msg;
210 continue;
211 }
212 if (!value->IsType(Value::TYPE_DICTIONARY)) {
213 LOG(WARNING) << "Expected JSON dictionary in configuration file "
214 << config_file_iter->value();
215 continue;
216 }
217 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get()));
218 }
219
220 return policy;
221 }
222
223 void Reload() {
224 DCHECK_EQ(MessageLoop::current(), message_loop_);
225 // Check the directory time in order to see whether a reload is required.
226 base::TimeDelta delay;
227 base::Time now = base::Time::Now();
228 if (!IsSafeToReloadPolicy(now, &delay)) {
229 ScheduleReloadTask(delay);
230 return;
231 }
232
233 // Load the policy definitions.
234 scoped_ptr<DictionaryValue> new_policy(Load());
235
236 // Check again in case the directory has changed while reading it.
237 if (!IsSafeToReloadPolicy(now, &delay)) {
238 ScheduleReloadTask(delay);
239 return;
240 }
241
242 // Read out just the host firewall traversal policy. Name of policy taken
243 // from the generated policy/policy_constants.h file.
244 bool new_nat_enabled_state = false;
245 if (!new_policy->HasKey("RemoteAccessHostFirewallTraversal")) {
246 // If unspecified, the default value of this policy is true.
247 new_nat_enabled_state = true;
248 } else {
249 // Otherwise, try to parse the value and only change from false if we get
250 // a successful read.
251 base::Value* value;
252 if (new_policy->Get("RemoteAccessHostFirewallTraversal", &value) &&
253 value->IsType(base::Value::TYPE_BOOLEAN)) {
254 CHECK(value->GetAsBoolean(&new_nat_enabled_state));
255 }
256 }
257
258 if (!first_state_published_ ||
259 (new_nat_enabled_state != current_nat_enabled_state_)) {
260 first_state_published_ = true;
261 current_nat_enabled_state_ = new_nat_enabled_state;
262 nat_enabled_cb_.Run(current_nat_enabled_state_);
263 }
264
265 ScheduleFallbackReloadTask();
266 }
267
268 bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay) {
269 DCHECK_EQ(MessageLoop::current(), message_loop_);
270 DCHECK(delay);
271 const base::TimeDelta kSettleInterval =
272 base::TimeDelta::FromSeconds(kSettleIntervalSeconds);
273
274 base::Time last_modification = GetLastModification();
275 if (last_modification.is_null())
276 return true;
277
278 // If there was a change since the last recorded modification, wait some
279 // more.
280 if (last_modification != last_modification_file_) {
281 last_modification_file_ = last_modification;
282 last_modification_clock_ = now;
283 *delay = kSettleInterval;
284 return false;
285 }
286
287 // Check whether the settle interval has elapsed.
288 base::TimeDelta age = now - last_modification_clock_;
289 if (age < kSettleInterval) {
290 *delay = kSettleInterval - age;
291 return false;
292 }
293
294 return true;
295 }
296
297 // Managed with a scoped_ptr rather than being declared as an inline member to
298 // decouple the watcher's life cycle from the NatPolicyLinux. This decoupling
299 // makes it possible to destroy the watcher before the loader's destructor is
300 // called (e.g. during Stop), since |watcher_| internally holds a reference to
301 // the loader and keeps it alive.
302 scoped_ptr<base::files::FilePathWatcher> watcher_;
303
304 // Records last known modification timestamp of |config_dir_|.
305 base::Time last_modification_file_;
306
307 // The wall clock time at which the last modification timestamp was
308 // recorded. It's better to not assume the file notification time and the
309 // wall clock times come from the same source, just in case there is some
310 // non-local filesystem involved.
311 base::Time last_modification_clock_;
312
313 MessageLoop* message_loop_;
314 const FilePath config_dir_;
315 NatEnabledCallback nat_enabled_cb_;
316
317 bool current_nat_enabled_state_;
318 bool first_state_published_;
319
320 // Allows us to cancel any inflight FileWatcher events or scheduled reloads.
321 base::WeakPtrFactory<NatPolicyLinux> weak_factory_;
322 };
323
324 NatPolicy* NatPolicy::Create(MessageLoop* message_loop,
325 const NatEnabledCallback& nat_enabled_cb) {
326 FilePath policy_dir(kPolicyDir);
327 return new NatPolicyLinux(message_loop, policy_dir, nat_enabled_cb);
328 }
329
330 } // namespace policy_hack
331 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698