diff options
Diffstat (limited to 'chrome/browser/policy/config_dir_policy_provider.cc')
-rw-r--r-- | chrome/browser/policy/config_dir_policy_provider.cc | 203 |
1 files changed, 194 insertions, 9 deletions
diff --git a/chrome/browser/policy/config_dir_policy_provider.cc b/chrome/browser/policy/config_dir_policy_provider.cc index 59509fc..8c64efa 100644 --- a/chrome/browser/policy/config_dir_policy_provider.cc +++ b/chrome/browser/policy/config_dir_policy_provider.cc @@ -8,22 +8,109 @@ #include "base/file_util.h" #include "base/logging.h" -#include "base/scoped_ptr.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/json_value_serializer.h" -ConfigDirPolicyProvider::ConfigDirPolicyProvider(const FilePath& config_dir) - : config_dir_(config_dir) { +namespace { + +// Amount of time we wait for the files in the policy directory to settle before +// trying to load it. This alleviates the problem of reading partially written +// files and allows to batch quasi-simultaneous changes. +const int kSettleIntervalSeconds = 5; + +// The time interval for rechecking policy. This is our fallback in case the +// directory watch fails or doesn't report a change. +const int kReloadIntervalMinutes = 15; + +} // namespace + +// PolicyDirLoader implementation: + +PolicyDirLoader::PolicyDirLoader( + base::WeakPtr<ConfigDirPolicyProvider> provider, + const FilePath& config_dir, + int settle_interval_seconds, + int reload_interval_minutes) + : provider_(provider), + origin_loop_(MessageLoop::current()), + config_dir_(config_dir), + reload_task_(NULL), + settle_interval_seconds_(settle_interval_seconds), + reload_interval_minutes_(reload_interval_minutes) { + // Force an initial load, so GetPolicy() works. + policy_.reset(Load()); + DCHECK(policy_.get()); } -bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) { - scoped_ptr<DictionaryValue> policy(ReadPolicies()); - DecodePolicyValueTree(policy.get(), store); - return true; +void PolicyDirLoader::Stop() { + if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) { + ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(this, &PolicyDirLoader::Stop)); + return; + } + + if (reload_task_) { + reload_task_->Cancel(); + reload_task_ = NULL; + } } -DictionaryValue* ConfigDirPolicyProvider::ReadPolicies() { +void PolicyDirLoader::Reload() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + + // Check the directory time in order to see whether a reload is required. + base::Time now = base::Time::Now(); + base::TimeDelta delay; + if (!IsSafeToReloadPolicy(now, &delay)) { + ScheduleReloadTask(delay); + return; + } + + // Load the policy definitions. + scoped_ptr<DictionaryValue> new_policy(Load()); + + // Check again in case the directory has changed while reading it. + if (!IsSafeToReloadPolicy(now, &delay)) { + ScheduleReloadTask(delay); + return; + } + + // Replace policy definition. + bool changed = false; + { + AutoLock lock(lock_); + changed = !policy_->Equals(new_policy.get()); + policy_.reset(new_policy.release()); + } + + // There's a change, report it! + if (changed) { + LOG(INFO) << "Policy reload from " << config_dir_.value() << " succeeded."; + origin_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PolicyDirLoader::NotifyPolicyChanged)); + } + + // As a safeguard in case the file watcher fails, schedule a reload task + // that'll make us recheck after a reasonable interval. + ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); +} + +DictionaryValue* PolicyDirLoader::GetPolicy() { + AutoLock lock(lock_); + return static_cast<DictionaryValue*>(policy_->DeepCopy()); +} + +void PolicyDirLoader::OnFilePathChanged(const FilePath& path) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + Reload(); +} + +void PolicyDirLoader::OnError() { + LOG(ERROR) << "FileWatcher on " << config_dir_.value() << " failed."; +} + +DictionaryValue* PolicyDirLoader::Load() { // Enumerate the files and sort them lexicographically. std::set<FilePath> files; file_util::FileEnumerator file_enumerator(config_dir_, false, @@ -56,6 +143,105 @@ DictionaryValue* ConfigDirPolicyProvider::ReadPolicies() { return policy; } +bool PolicyDirLoader::IsSafeToReloadPolicy(const base::Time& now, + base::TimeDelta* delay) { + DCHECK(delay); + file_util::FileInfo dir_info; + + // Reading an empty directory or a file is always safe. + if (!file_util::GetFileInfo(config_dir_, &dir_info) || + !dir_info.is_directory) { + return true; + } + + // Check for too recent changes. + base::TimeDelta settleInterval( + base::TimeDelta::FromSeconds(settle_interval_seconds_)); + base::TimeDelta age = now - dir_info.last_modified; + if (age < settleInterval) { + *delay = settleInterval - age; + return false; + } + + return true; +} + +void PolicyDirLoader::ScheduleReloadTask(const base::TimeDelta& delay) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + + if (reload_task_) + reload_task_->Cancel(); + + reload_task_ = NewRunnableMethod(this, &PolicyDirLoader::ReloadFromTask); + ChromeThread::PostDelayedTask(ChromeThread::FILE, FROM_HERE, reload_task_, + delay.InMilliseconds()); +} + +void PolicyDirLoader::NotifyPolicyChanged() { + DCHECK_EQ(origin_loop_, MessageLoop::current()); + if (provider_) + provider_->NotifyStoreOfPolicyChange(); +} + +void PolicyDirLoader::ReloadFromTask() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + + // Drop the reference to the reload task, since the task might be the only + // referer that keeps us alive, so we should not Cancel() it. + reload_task_ = NULL; + + Reload(); +} + +// PolicyDirWatcher implementation: + +void PolicyDirWatcher::Init(PolicyDirLoader* loader) { + // Initialization can happen early when the file thread is not yet available. + // So post a task to ourselves on th UI thread which will run after threading + // is up and schedule watch initialization on the file thread. + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &PolicyDirWatcher::InitWatcher, + scoped_refptr<PolicyDirLoader>(loader))); +} + +void PolicyDirWatcher::InitWatcher( + const scoped_refptr<PolicyDirLoader>& loader) { + if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) { + ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(this, &PolicyDirWatcher::InitWatcher, loader)); + return; + } + + if (!Watch(loader->config_dir(), loader.get())) + loader->OnError(); + + // There might have been changes to the directory in the time between + // construction of the loader and initialization of the watcher. Call reload + // to detect if that is the case. + loader->Reload(); +} + +// ConfigDirPolicyProvider implementation: + +ConfigDirPolicyProvider::ConfigDirPolicyProvider(const FilePath& config_dir) { + loader_ = new PolicyDirLoader(AsWeakPtr(), config_dir, kSettleIntervalSeconds, + kReloadIntervalMinutes); + watcher_ = new PolicyDirWatcher; + watcher_->Init(loader_.get()); +} + +ConfigDirPolicyProvider::~ConfigDirPolicyProvider() { + loader_->Stop(); +} + +bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) { + scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); + DCHECK(policy.get()); + DecodePolicyValueTree(policy.get(), store); + return true; +} + void ConfigDirPolicyProvider::DecodePolicyValueTree( DictionaryValue* policies, ConfigurationPolicyStore* store) { @@ -71,4 +257,3 @@ void ConfigDirPolicyProvider::DecodePolicyValueTree( // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| // supports it. } - |