| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/policy/file_based_policy_provider.h" | 5 #include "chrome/browser/policy/file_based_policy_provider.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include "chrome/browser/policy/file_based_policy_loader.h" |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/message_loop.h" | |
| 11 #include "base/task.h" | |
| 12 #include "base/utf_string_conversions.h" | |
| 13 #include "base/values.h" | |
| 14 #include "chrome/browser/browser_thread.h" | |
| 15 #include "chrome/common/json_value_serializer.h" | |
| 16 | 8 |
| 17 namespace policy { | 9 namespace policy { |
| 18 | 10 |
| 19 // Amount of time we wait for the files on disk to settle before trying to load | 11 FileBasedPolicyProvider::ProviderDelegate::ProviderDelegate( |
| 20 // it. This alleviates the problem of reading partially written files and allows | 12 const FilePath& config_file_path) |
| 21 // to batch quasi-simultaneous changes. | 13 : config_file_path_(config_file_path) {} |
| 22 const int kSettleIntervalSeconds = 5; | |
| 23 | 14 |
| 24 // The time interval for rechecking policy. This is our fallback in case the | 15 FileBasedPolicyProvider::ProviderDelegate::~ProviderDelegate() {} |
| 25 // file path watch fails or doesn't report a change. | |
| 26 const int kReloadIntervalMinutes = 15; | |
| 27 | |
| 28 // FileBasedPolicyProvider implementation: | |
| 29 | |
| 30 FileBasedPolicyProvider::Delegate::~Delegate() { | |
| 31 } | |
| 32 | |
| 33 FileBasedPolicyProvider::Delegate::Delegate(const FilePath& config_file_path) | |
| 34 : config_file_path_(config_file_path) { | |
| 35 } | |
| 36 | 16 |
| 37 FileBasedPolicyProvider::FileBasedPolicyProvider( | 17 FileBasedPolicyProvider::FileBasedPolicyProvider( |
| 38 const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, | 18 const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, |
| 39 FileBasedPolicyProvider::Delegate* delegate) | 19 FileBasedPolicyProvider::ProviderDelegate* delegate) |
| 40 : ConfigurationPolicyProvider(policy_list) { | 20 : AsynchronousPolicyProvider( |
| 41 loader_ = new FileBasedPolicyLoader(AsWeakPtr(), | 21 policy_list, |
| 42 delegate, | 22 new FileBasedPolicyLoader(delegate)) {} |
| 43 kSettleIntervalSeconds, | |
| 44 kReloadIntervalMinutes); | |
| 45 watcher_ = new FileBasedPolicyWatcher; | |
| 46 watcher_->Init(loader_.get()); | |
| 47 } | |
| 48 | |
| 49 FileBasedPolicyProvider::~FileBasedPolicyProvider() { | |
| 50 loader_->Stop(); | |
| 51 } | |
| 52 | |
| 53 bool FileBasedPolicyProvider::Provide( | |
| 54 ConfigurationPolicyStoreInterface* store) { | |
| 55 scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); | |
| 56 DCHECK(policy.get()); | |
| 57 DecodePolicyValueTree(policy.get(), store); | |
| 58 return true; | |
| 59 } | |
| 60 | |
| 61 // FileBasedPolicyLoader implementation: | |
| 62 | |
| 63 FileBasedPolicyLoader::FileBasedPolicyLoader( | |
| 64 base::WeakPtr<ConfigurationPolicyProvider> provider, | |
| 65 FileBasedPolicyProvider::Delegate* delegate, | |
| 66 int settle_interval_seconds, | |
| 67 int reload_interval_minutes) | |
| 68 : delegate_(delegate), | |
| 69 provider_(provider), | |
| 70 origin_loop_(MessageLoop::current()), | |
| 71 reload_task_(NULL), | |
| 72 settle_interval_seconds_(settle_interval_seconds), | |
| 73 reload_interval_minutes_(reload_interval_minutes) { | |
| 74 // Force an initial load, so GetPolicy() works. | |
| 75 policy_.reset(delegate_->Load()); | |
| 76 DCHECK(policy_.get()); | |
| 77 } | |
| 78 | |
| 79 void FileBasedPolicyLoader::Stop() { | |
| 80 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | |
| 81 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
| 82 NewRunnableMethod(this, &FileBasedPolicyLoader::Stop)); | |
| 83 return; | |
| 84 } | |
| 85 | |
| 86 if (reload_task_) { | |
| 87 reload_task_->Cancel(); | |
| 88 reload_task_ = NULL; | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 void FileBasedPolicyLoader::Reload() { | |
| 93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 94 | |
| 95 // Check the directory time in order to see whether a reload is required. | |
| 96 base::TimeDelta delay; | |
| 97 base::Time now = base::Time::Now(); | |
| 98 if (!IsSafeToReloadPolicy(now, &delay)) { | |
| 99 ScheduleReloadTask(delay); | |
| 100 return; | |
| 101 } | |
| 102 | |
| 103 // Load the policy definitions. | |
| 104 scoped_ptr<DictionaryValue> new_policy(delegate_->Load()); | |
| 105 | |
| 106 // Check again in case the directory has changed while reading it. | |
| 107 if (!IsSafeToReloadPolicy(now, &delay)) { | |
| 108 ScheduleReloadTask(delay); | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 // Replace policy definition. | |
| 113 bool changed = false; | |
| 114 { | |
| 115 AutoLock lock(lock_); | |
| 116 changed = !policy_->Equals(new_policy.get()); | |
| 117 policy_.reset(new_policy.release()); | |
| 118 } | |
| 119 | |
| 120 // There's a change, report it! | |
| 121 if (changed) { | |
| 122 VLOG(0) << "Policy reload from " << config_file_path().value() | |
| 123 << " succeeded."; | |
| 124 origin_loop_->PostTask(FROM_HERE, | |
| 125 NewRunnableMethod(this, &FileBasedPolicyLoader::NotifyPolicyChanged)); | |
| 126 } | |
| 127 | |
| 128 // As a safeguard in case the file watcher fails, schedule a reload task | |
| 129 // that'll make us recheck after a reasonable interval. | |
| 130 ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); | |
| 131 } | |
| 132 | |
| 133 DictionaryValue* FileBasedPolicyLoader::GetPolicy() { | |
| 134 AutoLock lock(lock_); | |
| 135 return static_cast<DictionaryValue*>(policy_->DeepCopy()); | |
| 136 } | |
| 137 | |
| 138 void FileBasedPolicyLoader::OnFilePathChanged(const FilePath& path) { | |
| 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 140 Reload(); | |
| 141 } | |
| 142 | |
| 143 void FileBasedPolicyLoader::OnError() { | |
| 144 LOG(ERROR) << "FilePathWatcher on " << config_file_path().value() | |
| 145 << " failed."; | |
| 146 } | |
| 147 | |
| 148 FileBasedPolicyLoader::~FileBasedPolicyLoader() { | |
| 149 } | |
| 150 | |
| 151 bool FileBasedPolicyLoader::IsSafeToReloadPolicy(const base::Time& now, | |
| 152 base::TimeDelta* delay) { | |
| 153 DCHECK(delay); | |
| 154 | |
| 155 // A null modification time indicates there's no data. | |
| 156 base::Time last_modification(delegate_->GetLastModification()); | |
| 157 if (last_modification.is_null()) | |
| 158 return true; | |
| 159 | |
| 160 // If there was a change since the last recorded modification, wait some more. | |
| 161 base::TimeDelta settleInterval( | |
| 162 base::TimeDelta::FromSeconds(settle_interval_seconds_)); | |
| 163 if (last_modification != last_modification_file_) { | |
| 164 last_modification_file_ = last_modification; | |
| 165 last_modification_clock_ = now; | |
| 166 *delay = settleInterval; | |
| 167 return false; | |
| 168 } | |
| 169 | |
| 170 // Check whether the settle interval has elapsed. | |
| 171 base::TimeDelta age = now - last_modification_clock_; | |
| 172 if (age < settleInterval) { | |
| 173 *delay = settleInterval - age; | |
| 174 return false; | |
| 175 } | |
| 176 | |
| 177 return true; | |
| 178 } | |
| 179 | |
| 180 void FileBasedPolicyLoader::ScheduleReloadTask(const base::TimeDelta& delay) { | |
| 181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 182 | |
| 183 if (reload_task_) | |
| 184 reload_task_->Cancel(); | |
| 185 | |
| 186 reload_task_ = | |
| 187 NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask); | |
| 188 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, | |
| 189 delay.InMilliseconds()); | |
| 190 } | |
| 191 | |
| 192 void FileBasedPolicyLoader::NotifyPolicyChanged() { | |
| 193 DCHECK_EQ(origin_loop_, MessageLoop::current()); | |
| 194 if (provider_) | |
| 195 provider_->NotifyStoreOfPolicyChange(); | |
| 196 } | |
| 197 | |
| 198 void FileBasedPolicyLoader::ReloadFromTask() { | |
| 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 200 | |
| 201 // Drop the reference to the reload task, since the task might be the only | |
| 202 // referer that keeps us alive, so we should not Cancel() it. | |
| 203 reload_task_ = NULL; | |
| 204 | |
| 205 Reload(); | |
| 206 } | |
| 207 | |
| 208 // FileBasedPolicyWatcher implementation: | |
| 209 | |
| 210 FileBasedPolicyWatcher::FileBasedPolicyWatcher() { | |
| 211 } | |
| 212 | |
| 213 void FileBasedPolicyWatcher::Init(FileBasedPolicyLoader* loader) { | |
| 214 // Initialization can happen early when the file thread is not yet available. | |
| 215 // So post a task to ourselves on the UI thread which will run after threading | |
| 216 // is up and schedule watch initialization on the file thread. | |
| 217 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 218 NewRunnableMethod(this, | |
| 219 &FileBasedPolicyWatcher::InitWatcher, | |
| 220 scoped_refptr<FileBasedPolicyLoader>(loader))); | |
| 221 } | |
| 222 | |
| 223 FileBasedPolicyWatcher::~FileBasedPolicyWatcher() { | |
| 224 } | |
| 225 | |
| 226 void FileBasedPolicyWatcher::InitWatcher( | |
| 227 const scoped_refptr<FileBasedPolicyLoader>& loader) { | |
| 228 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | |
| 229 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
| 230 NewRunnableMethod(this, &FileBasedPolicyWatcher::InitWatcher, loader)); | |
| 231 return; | |
| 232 } | |
| 233 | |
| 234 if (!loader->config_file_path().empty() && | |
| 235 !watcher_.Watch(loader->config_file_path(), loader.get())) | |
| 236 loader->OnError(); | |
| 237 | |
| 238 // There might have been changes to the directory in the time between | |
| 239 // construction of the loader and initialization of the watcher. Call reload | |
| 240 // to detect if that is the case. | |
| 241 loader->Reload(); | |
| 242 } | |
| 243 | 23 |
| 244 } // namespace policy | 24 } // namespace policy |
| OLD | NEW |