summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy/file_based_policy_provider.cc
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-11-18 18:32:45 +0000
committerBen Murdoch <benm@google.com>2010-11-18 18:38:07 +0000
commit513209b27ff55e2841eac0e4120199c23acce758 (patch)
treeaeba30bb08c5f47c57003544e378a377c297eee6 /chrome/browser/policy/file_based_policy_provider.cc
parent164f7496de0fbee436b385a79ead9e3cb81a50c1 (diff)
downloadexternal_chromium-513209b27ff55e2841eac0e4120199c23acce758.zip
external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.gz
external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.bz2
Merge Chromium at r65505: Initial merge by git.
Change-Id: I31d8f1d8cd33caaf7f47ffa7350aef42d5fbdb45
Diffstat (limited to 'chrome/browser/policy/file_based_policy_provider.cc')
-rw-r--r--chrome/browser/policy/file_based_policy_provider.cc259
1 files changed, 259 insertions, 0 deletions
diff --git a/chrome/browser/policy/file_based_policy_provider.cc b/chrome/browser/policy/file_based_policy_provider.cc
new file mode 100644
index 0000000..5a92105
--- /dev/null
+++ b/chrome/browser/policy/file_based_policy_provider.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/file_based_policy_provider.h"
+
+#include <set>
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/common/json_value_serializer.h"
+
+namespace policy {
+
+// Amount of time we wait for the files on disk 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
+// file path watch fails or doesn't report a change.
+const int kReloadIntervalMinutes = 15;
+
+// FileBasedPolicyProvider implementation:
+
+FileBasedPolicyProvider::Delegate::~Delegate() {
+}
+
+FileBasedPolicyProvider::Delegate::Delegate(const FilePath& config_file_path)
+ : config_file_path_(config_file_path) {
+}
+
+FileBasedPolicyProvider::FileBasedPolicyProvider(
+ const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list,
+ FileBasedPolicyProvider::Delegate* delegate)
+ : ConfigurationPolicyProvider(policy_list) {
+ loader_ = new FileBasedPolicyLoader(AsWeakPtr(),
+ delegate,
+ kSettleIntervalSeconds,
+ kReloadIntervalMinutes);
+ watcher_ = new FileBasedPolicyWatcher;
+ watcher_->Init(loader_.get());
+}
+
+FileBasedPolicyProvider::~FileBasedPolicyProvider() {
+ loader_->Stop();
+}
+
+bool FileBasedPolicyProvider::Provide(
+ ConfigurationPolicyStoreInterface* store) {
+ scoped_ptr<DictionaryValue> policy(loader_->GetPolicy());
+ DCHECK(policy.get());
+ DecodePolicyValueTree(policy.get(), store);
+ return true;
+}
+
+void FileBasedPolicyProvider::DecodePolicyValueTree(
+ DictionaryValue* policies,
+ ConfigurationPolicyStoreInterface* store) {
+ const PolicyDefinitionList* policy_list(policy_definition_list());
+ for (const PolicyDefinitionList::Entry* i = policy_list->begin;
+ i != policy_list->end; ++i) {
+ Value* value;
+ if (policies->Get(i->name, &value) && value->IsType(i->value_type))
+ store->Apply(i->policy_type, value->DeepCopy());
+ }
+
+ // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore|
+ // supports it.
+}
+
+// FileBasedPolicyLoader implementation:
+
+FileBasedPolicyLoader::FileBasedPolicyLoader(
+ base::WeakPtr<ConfigurationPolicyProvider> provider,
+ FileBasedPolicyProvider::Delegate* delegate,
+ int settle_interval_seconds,
+ int reload_interval_minutes)
+ : delegate_(delegate),
+ provider_(provider),
+ origin_loop_(MessageLoop::current()),
+ reload_task_(NULL),
+ settle_interval_seconds_(settle_interval_seconds),
+ reload_interval_minutes_(reload_interval_minutes) {
+ // Force an initial load, so GetPolicy() works.
+ policy_.reset(delegate_->Load());
+ DCHECK(policy_.get());
+}
+
+void FileBasedPolicyLoader::Stop() {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &FileBasedPolicyLoader::Stop));
+ return;
+ }
+
+ if (reload_task_) {
+ reload_task_->Cancel();
+ reload_task_ = NULL;
+ }
+}
+
+void FileBasedPolicyLoader::Reload() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // Check the directory time in order to see whether a reload is required.
+ base::TimeDelta delay;
+ base::Time now = base::Time::Now();
+ if (!IsSafeToReloadPolicy(now, &delay)) {
+ ScheduleReloadTask(delay);
+ return;
+ }
+
+ // Load the policy definitions.
+ scoped_ptr<DictionaryValue> new_policy(delegate_->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) {
+ VLOG(0) << "Policy reload from " << config_file_path().value()
+ << " succeeded.";
+ origin_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &FileBasedPolicyLoader::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* FileBasedPolicyLoader::GetPolicy() {
+ AutoLock lock(lock_);
+ return static_cast<DictionaryValue*>(policy_->DeepCopy());
+}
+
+void FileBasedPolicyLoader::OnFilePathChanged(const FilePath& path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Reload();
+}
+
+void FileBasedPolicyLoader::OnError() {
+ LOG(ERROR) << "FilePathWatcher on " << config_file_path().value()
+ << " failed.";
+}
+
+FileBasedPolicyLoader::~FileBasedPolicyLoader() {
+}
+
+bool FileBasedPolicyLoader::IsSafeToReloadPolicy(const base::Time& now,
+ base::TimeDelta* delay) {
+ DCHECK(delay);
+
+ // A null modification time indicates there's no data.
+ base::Time last_modification(delegate_->GetLastModification());
+ if (last_modification.is_null())
+ return true;
+
+ // If there was a change since the last recorded modification, wait some more.
+ base::TimeDelta settleInterval(
+ base::TimeDelta::FromSeconds(settle_interval_seconds_));
+ if (last_modification != last_modification_file_) {
+ last_modification_file_ = last_modification;
+ last_modification_clock_ = now;
+ *delay = settleInterval;
+ return false;
+ }
+
+ // Check whether the settle interval has elapsed.
+ base::TimeDelta age = now - last_modification_clock_;
+ if (age < settleInterval) {
+ *delay = settleInterval - age;
+ return false;
+ }
+
+ return true;
+}
+
+void FileBasedPolicyLoader::ScheduleReloadTask(const base::TimeDelta& delay) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ if (reload_task_)
+ reload_task_->Cancel();
+
+ reload_task_ =
+ NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask);
+ BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_,
+ delay.InMilliseconds());
+}
+
+void FileBasedPolicyLoader::NotifyPolicyChanged() {
+ DCHECK_EQ(origin_loop_, MessageLoop::current());
+ if (provider_)
+ provider_->NotifyStoreOfPolicyChange();
+}
+
+void FileBasedPolicyLoader::ReloadFromTask() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::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();
+}
+
+// FileBasedPolicyWatcher implementation:
+
+FileBasedPolicyWatcher::FileBasedPolicyWatcher() {
+}
+
+void FileBasedPolicyWatcher::Init(FileBasedPolicyLoader* loader) {
+ // Initialization can happen early when the file thread is not yet available.
+ // So post a task to ourselves on the UI thread which will run after threading
+ // is up and schedule watch initialization on the file thread.
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &FileBasedPolicyWatcher::InitWatcher,
+ scoped_refptr<FileBasedPolicyLoader>(loader)));
+}
+
+FileBasedPolicyWatcher::~FileBasedPolicyWatcher() {
+}
+
+void FileBasedPolicyWatcher::InitWatcher(
+ const scoped_refptr<FileBasedPolicyLoader>& loader) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &FileBasedPolicyWatcher::InitWatcher, loader));
+ return;
+ }
+
+ if (!loader->config_file_path().empty() &&
+ !watcher_.Watch(loader->config_file_path(), 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();
+}
+
+} // namespace policy