// 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 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 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 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(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