Chromium Code Reviews| 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 |