summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy/config_dir_policy_provider.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/policy/config_dir_policy_provider.cc')
-rw-r--r--chrome/browser/policy/config_dir_policy_provider.cc203
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.
}
-