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

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

Powered by Google App Engine
This is Rietveld 408576698