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

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: silly compiler. 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 "remoting/host/plugin/policy_hack/nat_policy_linux.h"
6
7 #include <set>
8
9 #include "base/bind.h"
10 #include "base/message_loop_proxy.h"
11 #include "base/files/file_path_watcher.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "remoting/host/plugin/policy_hack/json_value_serializer.h"
14
15 namespace remoting {
16 namespace policy_hack {
17
18 namespace {
19
20 #if defined(GOOGLE_CHROME_BUILD)
21 const FilePath::CharType kPolicyDir[] =
22 FILE_PATH_LITERAL("/etc/opt/chrome/policies/managed");
23 #else
24 const FilePath::CharType kPolicyDir[] =
25 FILE_PATH_LITERAL("/etc/chromium/policies/managed");
26 #endif
27
28 // The time interval for rechecking policy. This is our fallback in case the
29 // delegate never reports a change to the ReloadObserver.
30 static int kFallbackReloadDelayMinutes = 15;
dmac 2011/08/10 21:39:00 no need to mark static in the anonymous namespace.
awong 2011/08/11 01:23:29 consted instead.
31
32 // Amount of time we wait for the files on disk to settle before trying to load
33 // them. This alleviates the problem of reading partially written files and
34 // makes it possible to batch quasi-simultaneous changes.
35 const int kSettleIntervalSeconds = 5;
36
37 class FileWatcherDelegate : public base::files::FilePathWatcher::Delegate {
38 public:
39 FileWatcherDelegate(base::WeakPtr<NatPolicyLinux> policy_watcher)
40 : message_loop_proxy_(base::MessageLoopProxy::CreateForCurrentThread()),
41 policy_watcher_(policy_watcher) {
42 }
43
44 virtual void OnFilePathChanged(const FilePath& path) {
45 if (!message_loop_proxy_->BelongsToCurrentThread()) {
46 message_loop_proxy_->PostTask(
47 FROM_HERE,
48 base::Bind(&FileWatcherDelegate::OnFilePathChanged, this, path));
49 return;
50 }
51
52 if (policy_watcher_) {
53 policy_watcher_->OnFilePathChanged(path);
54 }
55 }
56
57 virtual void OnFilePathError(const FilePath& path) {
58 if (!message_loop_proxy_->BelongsToCurrentThread()) {
59 message_loop_proxy_->PostTask(
60 FROM_HERE,
61 base::Bind(&FileWatcherDelegate::OnFilePathError, this, path));
62 return;
63 }
64
65 if (policy_watcher_) {
66 policy_watcher_->OnFilePathError(path);
67 }
68 }
69
70 private:
71 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
72 base::WeakPtr<NatPolicyLinux> policy_watcher_;
73 };
74
75 } // namespace
76
77 NatPolicyLinux::NatPolicyLinux(const FilePath& config_dir,
78 const NatEnabledCallback& nat_enabled_cb)
79 : message_loop_proxy_(base::MessageLoopProxy::CreateForCurrentThread()),
80 config_dir_(config_dir),
81 nat_enabled_cb_(nat_enabled_cb),
82 current_nat_enabled_state_(false),
83 first_state_published_(false),
84 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
85 }
86
87 NatPolicyLinux::~NatPolicyLinux() {
88 }
89
90 void NatPolicyLinux::StartWatching() {
91 if (!message_loop_proxy_->BelongsToCurrentThread()) {
92 message_loop_proxy_->PostTask(FROM_HERE,
93 base::Bind(&NatPolicyLinux::StartWatching,
94 base::Unretained(this)));
95 return;
96 }
97
98 watcher_.reset(new base::files::FilePathWatcher());
99
100 if (!config_dir_.empty() &&
101 !watcher_->Watch(config_dir_,
102 new FileWatcherDelegate(weak_factory_.GetWeakPtr()))) {
dmac 2011/08/10 21:39:00 refcounting the filewatcherdelegate may simplify s
awong 2011/08/11 01:23:29 Went through a few iterations, and decided that in
103 OnFilePathError(config_dir_);
104 }
105
106 // There might have been changes to the directory in the time between
107 // construction of the loader and initialization of the watcher. Call reload
108 // to detect if that is the case.
109 Reload();
110
111 ScheduleFallbackReloadTask();
112 }
113
114 void NatPolicyLinux::StopWatching(base::WaitableEvent* done) {
115 if (!message_loop_proxy_->BelongsToCurrentThread()) {
116 message_loop_proxy_->PostTask(FROM_HERE,
117 base::Bind(&NatPolicyLinux::StopWatching,
118 base::Unretained(this), done));
119 return;
120 }
121 watcher_.reset();
122 weak_factory_.InvalidateWeakPtrs();
123 done->Signal();
124 }
125
126 void NatPolicyLinux::ScheduleFallbackReloadTask() {
127 ScheduleReloadTask(base::TimeDelta::FromMinutes(kFallbackReloadDelayMinutes));
128 }
129
130 void NatPolicyLinux::ScheduleReloadTask(const base::TimeDelta& delay) {
131 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
132 message_loop_proxy_->PostDelayedTask(
133 FROM_HERE,
134 base::Bind(&NatPolicyLinux::Reload, weak_factory_.GetWeakPtr()),
135 delay.InMilliseconds());
136 }
137
138 void NatPolicyLinux::OnFilePathError(const FilePath& path) {
139 LOG(ERROR) << "NatPolicyLinux on " << path.value()
140 << " failed.";
141 }
142
143 void NatPolicyLinux::OnFilePathChanged(const FilePath& path) {
144 Reload();
145 }
146
147 base::Time NatPolicyLinux::GetLastModification() {
148 base::Time last_modification = base::Time();
149 base::PlatformFileInfo file_info;
150
151 // If the path does not exist or points to a directory, it's safe to load.
152 if (!file_util::GetFileInfo(config_dir_, &file_info) ||
153 !file_info.is_directory) {
154 return last_modification;
155 }
156
157 // Enumerate the files and find the most recent modification timestamp.
158 file_util::FileEnumerator file_enumerator(config_dir_,
159 false,
160 file_util::FileEnumerator::FILES);
161 for (FilePath config_file = file_enumerator.Next();
162 !config_file.empty();
163 config_file = file_enumerator.Next()) {
164 if (file_util::GetFileInfo(config_file, &file_info) &&
165 !file_info.is_directory) {
166 last_modification = std::max(last_modification, file_info.last_modified);
167 }
168 }
169
170 return last_modification;
171 }
172
173 DictionaryValue* NatPolicyLinux::Load() {
174 // Enumerate the files and sort them lexicographically.
175 std::set<FilePath> files;
176 file_util::FileEnumerator file_enumerator(config_dir_, false,
177 file_util::FileEnumerator::FILES);
178 for (FilePath config_file_path = file_enumerator.Next();
179 !config_file_path.empty(); config_file_path = file_enumerator.Next())
180 files.insert(config_file_path);
181
182 // Start with an empty dictionary and merge the files' contents.
183 DictionaryValue* policy = new DictionaryValue;
184 for (std::set<FilePath>::iterator config_file_iter = files.begin();
185 config_file_iter != files.end(); ++config_file_iter) {
186 JSONFileValueSerializer deserializer(*config_file_iter);
187 int error_code = 0;
188 std::string error_msg;
189 scoped_ptr<Value> value(deserializer.Deserialize(&error_code, &error_msg));
190 if (!value.get()) {
191 LOG(WARNING) << "Failed to read configuration file "
192 << config_file_iter->value() << ": " << error_msg;
193 continue;
194 }
195 if (!value->IsType(Value::TYPE_DICTIONARY)) {
196 LOG(WARNING) << "Expected JSON dictionary in configuration file "
197 << config_file_iter->value();
198 continue;
199 }
200 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get()));
201 }
202
203 return policy;
204 }
205
206 void NatPolicyLinux::Reload() {
207 // Check the directory time in order to see whether a reload is required.
208 base::TimeDelta delay;
209 base::Time now = base::Time::Now();
210 if (!IsSafeToReloadPolicy(now, &delay)) {
211 ScheduleReloadTask(delay);
212 return;
213 }
214
215 // Load the policy definitions.
216 scoped_ptr<DictionaryValue> new_policy(Load());
217
218 // Check again in case the directory has changed while reading it.
219 if (!IsSafeToReloadPolicy(now, &delay)) {
220 ScheduleReloadTask(delay);
221 return;
222 }
223
224 // Read out just the host firewall traversal policy. Name of policy taken
225 // from the generated policy/policy_constants.h file.
226 bool new_nat_enabled_state = false;
227 base::Value* value;
228 if (new_policy->Get("RemoteAccessHostFirewallTraversal", &value) &&
229 value->IsType(base::Value::TYPE_BOOLEAN)) {
230 CHECK(value->GetAsBoolean(&new_nat_enabled_state));
231 }
232
233 if (!first_state_published_ ||
234 (new_nat_enabled_state != current_nat_enabled_state_)) {
235 first_state_published_ = true;
236 current_nat_enabled_state_ = new_nat_enabled_state;
237 nat_enabled_cb_.Run(current_nat_enabled_state_);
238 }
239
240 ScheduleFallbackReloadTask();
241 }
242
243 bool NatPolicyLinux::IsSafeToReloadPolicy(
244 const base::Time& now,
245 base::TimeDelta* delay) {
246 DCHECK(delay);
247 const base::TimeDelta kSettleInterval =
248 base::TimeDelta::FromSeconds(kSettleIntervalSeconds);
249
250 base::Time last_modification = GetLastModification();
251 if (last_modification.is_null())
252 return true;
253
254 // If there was a change since the last recorded modification, wait some more.
255 if (last_modification != last_modification_file_) {
256 last_modification_file_ = last_modification;
257 last_modification_clock_ = now;
258 *delay = kSettleInterval;
259 return false;
260 }
261
262 // Check whether the settle interval has elapsed.
263 base::TimeDelta age = now - last_modification_clock_;
264 if (age < kSettleInterval) {
265 *delay = kSettleInterval - age;
266 return false;
267 }
268
269 return true;
270 }
271
272 NatPolicy* NatPolicy::Create(const NatEnabledCallback& nat_enabled_cb) {
273 FilePath policy_dir(kPolicyDir);
274 return new NatPolicyLinux(policy_dir, nat_enabled_cb);
275 }
276
277 } // namespace policy_hack
278 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698