| Index: chrome/browser/policy/file_based_policy_loader.cc
|
| diff --git a/chrome/browser/policy/file_based_policy_loader.cc b/chrome/browser/policy/file_based_policy_loader.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4c33b91213b8d109c6ffcaf7dfadb0820f5d47ee
|
| --- /dev/null
|
| +++ b/chrome/browser/policy/file_based_policy_loader.cc
|
| @@ -0,0 +1,205 @@
|
| +// 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_loader.h"
|
| +
|
| +namespace {
|
| +
|
| +// Amount of time we wait for the files on disk to settle before trying to load
|
| +// them. This alleviates the problem of reading partially written files and
|
| +// makes it possible to batch quasi-simultaneous changes.
|
| +const int kSettleIntervalSeconds = 5;
|
| +
|
| +// The time interval for rechecking policy. This is our fallback in case the
|
| +// delegate never reports a change to the ReloadObserver.
|
| +const int kReloadIntervalMinutes = 15;
|
| +
|
| +}
|
| +
|
| +namespace policy {
|
| +
|
| +FileBasedPolicyLoader::FileBasedPolicyLoader(
|
| + FileBasedPolicyProvider::ProviderDelegate* provider_delegate)
|
| + : AsynchronousPolicyLoader(provider_delegate),
|
| + config_file_path_(provider_delegate->config_file_path()),
|
| + reload_task_(NULL),
|
| + reload_interval_(base::TimeDelta::FromMinutes(kReloadIntervalMinutes)),
|
| + settle_interval_(base::TimeDelta::FromSeconds(kSettleIntervalSeconds)) {
|
| +}
|
| +
|
| +class FileBasedPolicyWatcherDelegate : public FilePathWatcher::Delegate {
|
| + public:
|
| + explicit FileBasedPolicyWatcherDelegate(
|
| + scoped_refptr<FileBasedPolicyLoader> loader)
|
| + : loader_(loader) {}
|
| + virtual ~FileBasedPolicyWatcherDelegate() {}
|
| +
|
| + // FilePathWatcher::Delegate implementation:
|
| + void OnFilePathChanged(const FilePath& path) {
|
| + loader_->OnFilePathChanged(path);
|
| + }
|
| +
|
| + void OnError() {
|
| + loader_->OnError();
|
| + }
|
| +
|
| + private:
|
| + scoped_refptr<FileBasedPolicyLoader> loader_;
|
| + DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcherDelegate);
|
| +};
|
| +
|
| +void FileBasedPolicyLoader::Init() {
|
| + AsynchronousPolicyLoader::Init();
|
| +
|
| + // Initialization can happen early when the file thread is not yet available,
|
| + // but the watcher's initialization must be done on the file thread. Posting
|
| + // to a to the file directly before the file thread is initialized will cause
|
| + // the task to be forgotten. Instead, post a task to the ui thread to delay
|
| + // the remainder of initialization until threading is fully initialized.
|
| + BrowserThread::PostTask(
|
| + BrowserThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &FileBasedPolicyLoader::InitAfterFileThreadAvailable));
|
| +}
|
| +
|
| +void FileBasedPolicyLoader::Stop() {
|
| + AsynchronousPolicyLoader::Stop();
|
| + BrowserThread::PostTask(
|
| + BrowserThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this, &FileBasedPolicyLoader::StopOnFileThread));
|
| +}
|
| +
|
| +void FileBasedPolicyLoader::OnFilePathChanged(
|
| + const FilePath& path) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + Reload();
|
| +}
|
| +
|
| +void FileBasedPolicyLoader::OnError() {
|
| + LOG(ERROR) << "FilePathWatcher on " << config_file_path().value()
|
| + << " failed.";
|
| +}
|
| +
|
| +void FileBasedPolicyLoader::Reload() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| +
|
| + if (!delegate())
|
| + return;
|
| +
|
| + // 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;
|
| + }
|
| +
|
| + PostUpdatePolicyTask(new_policy.release());
|
| +
|
| + ScheduleFallbackReloadTask();
|
| +}
|
| +
|
| +void FileBasedPolicyLoader::InitAfterFileThreadAvailable() {
|
| + if (provider()) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this, &FileBasedPolicyLoader::InitWatcher));
|
| +
|
| + ScheduleFallbackReloadTask();
|
| + }
|
| +}
|
| +
|
| +void FileBasedPolicyLoader::InitWatcher() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + watcher_.reset(new FilePathWatcher);
|
| + if (!config_file_path().empty() &&
|
| + !watcher_->Watch(config_file_path(),
|
| + new FileBasedPolicyWatcherDelegate(this))) {
|
| + 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.
|
| + Reload();
|
| +}
|
| +
|
| +void FileBasedPolicyLoader::StopOnFileThread() {
|
| + watcher_.reset();
|
| + if (reload_task_) {
|
| + reload_task_->Cancel();
|
| + reload_task_ = NULL;
|
| + }
|
| +}
|
| +
|
| +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::ScheduleFallbackReloadTask() {
|
| + // As a safeguard in case that the load delegate failed to timely notice a
|
| + // change in policy, schedule a reload task that'll make us recheck after a
|
| + // reasonable interval.
|
| + ScheduleReloadTask(reload_interval_);
|
| +}
|
| +
|
| +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();
|
| +}
|
| +
|
| +bool FileBasedPolicyLoader::IsSafeToReloadPolicy(
|
| + const base::Time& now,
|
| + base::TimeDelta* delay) {
|
| + DCHECK(delay);
|
| +
|
| + // A null modification time indicates there's no data.
|
| + FileBasedPolicyProvider::ProviderDelegate* provider_delegate =
|
| + static_cast<FileBasedPolicyProvider::ProviderDelegate*>(delegate());
|
| + base::Time last_modification(provider_delegate->GetLastModification());
|
| + if (last_modification.is_null())
|
| + return true;
|
| +
|
| + // If there was a change since the last recorded modification, wait some more.
|
| + if (last_modification != last_modification_file_) {
|
| + last_modification_file_ = last_modification;
|
| + last_modification_clock_ = now;
|
| + *delay = settle_interval_;
|
| + return false;
|
| + }
|
| +
|
| + // Check whether the settle interval has elapsed.
|
| + base::TimeDelta age = now - last_modification_clock_;
|
| + if (age < settle_interval_) {
|
| + *delay = settle_interval_ - age;
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace policy
|
|
|