OLD | NEW |
---|---|
(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 | |
OLD | NEW |