blob: e97d065b95b90634d36272e20b58f04f4b6d8220 [file] [log] [blame]
// Copyright (c) 2011 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"
#include "base/compiler_specific.h"
#include "content/browser/browser_thread.h"
using ::base::files::FilePathWatcher;
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
namespace policy {
FileBasedPolicyLoader::FileBasedPolicyLoader(
FileBasedPolicyProvider::ProviderDelegate* provider_delegate)
: AsynchronousPolicyLoader(provider_delegate,
kReloadIntervalMinutes),
config_file_path_(provider_delegate->config_file_path()),
settle_interval_(base::TimeDelta::FromSeconds(kSettleIntervalSeconds)) {
}
FileBasedPolicyLoader::~FileBasedPolicyLoader() {}
class FileBasedPolicyWatcherDelegate : public FilePathWatcher::Delegate {
public:
explicit FileBasedPolicyWatcherDelegate(
scoped_refptr<FileBasedPolicyLoader> loader)
: loader_(loader) {}
virtual ~FileBasedPolicyWatcherDelegate() {}
// FilePathWatcher::Delegate implementation:
virtual void OnFilePathChanged(const FilePath& path) OVERRIDE {
loader_->OnFilePathChanged(path);
}
virtual void OnFilePathError(const FilePath& path) OVERRIDE {
loader_->OnFilePathError(path);
}
private:
scoped_refptr<FileBasedPolicyLoader> loader_;
DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcherDelegate);
};
void FileBasedPolicyLoader::OnFilePathChanged(
const FilePath& path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
Reload();
}
void FileBasedPolicyLoader::OnFilePathError(const FilePath& path) {
LOG(ERROR) << "FilePathWatcher on " << 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::InitOnFileThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
watcher_.reset(new FilePathWatcher);
const FilePath& path = config_file_path();
if (!path.empty() &&
!watcher_->Watch(path, new FileBasedPolicyWatcherDelegate(this))) {
OnFilePathError(path);
}
// 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();
ScheduleFallbackReloadTask();
}
void FileBasedPolicyLoader::StopOnFileThread() {
watcher_.reset();
AsynchronousPolicyLoader::StopOnFileThread();
}
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