Home | History | Annotate | Download | only in policy
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/policy/file_based_policy_loader.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "content/browser/browser_thread.h"
      9 
     10 using ::base::files::FilePathWatcher;
     11 
     12 namespace {
     13 
     14 // Amount of time we wait for the files on disk to settle before trying to load
     15 // them. This alleviates the problem of reading partially written files and
     16 // makes it possible to batch quasi-simultaneous changes.
     17 const int kSettleIntervalSeconds = 5;
     18 
     19 // The time interval for rechecking policy. This is our fallback in case the
     20 // delegate never reports a change to the ReloadObserver.
     21 const int kReloadIntervalMinutes = 15;
     22 
     23 }  // namespace
     24 
     25 namespace policy {
     26 
     27 FileBasedPolicyLoader::FileBasedPolicyLoader(
     28     FileBasedPolicyProvider::ProviderDelegate* provider_delegate)
     29     : AsynchronousPolicyLoader(provider_delegate,
     30                                kReloadIntervalMinutes),
     31       config_file_path_(provider_delegate->config_file_path()),
     32       settle_interval_(base::TimeDelta::FromSeconds(kSettleIntervalSeconds)) {
     33 }
     34 
     35 FileBasedPolicyLoader::~FileBasedPolicyLoader() {}
     36 
     37 class FileBasedPolicyWatcherDelegate : public FilePathWatcher::Delegate {
     38  public:
     39   explicit FileBasedPolicyWatcherDelegate(
     40       scoped_refptr<FileBasedPolicyLoader> loader)
     41       : loader_(loader) {}
     42   virtual ~FileBasedPolicyWatcherDelegate() {}
     43 
     44   // FilePathWatcher::Delegate implementation:
     45   virtual void OnFilePathChanged(const FilePath& path) OVERRIDE {
     46     loader_->OnFilePathChanged(path);
     47   }
     48 
     49   virtual void OnFilePathError(const FilePath& path) OVERRIDE {
     50     loader_->OnFilePathError(path);
     51   }
     52 
     53  private:
     54   scoped_refptr<FileBasedPolicyLoader> loader_;
     55   DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcherDelegate);
     56 };
     57 
     58 void FileBasedPolicyLoader::OnFilePathChanged(
     59     const FilePath& path) {
     60   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     61   Reload();
     62 }
     63 
     64 void FileBasedPolicyLoader::OnFilePathError(const FilePath& path) {
     65   LOG(ERROR) << "FilePathWatcher on " << path.value()
     66              << " failed.";
     67 }
     68 
     69 void FileBasedPolicyLoader::Reload() {
     70   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     71 
     72   if (!delegate())
     73     return;
     74 
     75   // Check the directory time in order to see whether a reload is required.
     76   base::TimeDelta delay;
     77   base::Time now = base::Time::Now();
     78   if (!IsSafeToReloadPolicy(now, &delay)) {
     79     ScheduleReloadTask(delay);
     80     return;
     81   }
     82 
     83   // Load the policy definitions.
     84   scoped_ptr<DictionaryValue> new_policy(delegate()->Load());
     85 
     86   // Check again in case the directory has changed while reading it.
     87   if (!IsSafeToReloadPolicy(now, &delay)) {
     88     ScheduleReloadTask(delay);
     89     return;
     90   }
     91 
     92   PostUpdatePolicyTask(new_policy.release());
     93 
     94   ScheduleFallbackReloadTask();
     95 }
     96 
     97 void FileBasedPolicyLoader::InitOnFileThread() {
     98   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     99   watcher_.reset(new FilePathWatcher);
    100   const FilePath& path = config_file_path();
    101   if (!path.empty() &&
    102       !watcher_->Watch(path, new FileBasedPolicyWatcherDelegate(this))) {
    103     OnFilePathError(path);
    104   }
    105 
    106   // There might have been changes to the directory in the time between
    107   // construction of the loader and initialization of the watcher. Call reload
    108   // to detect if that is the case.
    109   Reload();
    110 
    111   ScheduleFallbackReloadTask();
    112 }
    113 
    114 void FileBasedPolicyLoader::StopOnFileThread() {
    115   watcher_.reset();
    116   AsynchronousPolicyLoader::StopOnFileThread();
    117 }
    118 
    119 bool FileBasedPolicyLoader::IsSafeToReloadPolicy(
    120     const base::Time& now,
    121     base::TimeDelta* delay) {
    122   DCHECK(delay);
    123 
    124   // A null modification time indicates there's no data.
    125   FileBasedPolicyProvider::ProviderDelegate* provider_delegate =
    126       static_cast<FileBasedPolicyProvider::ProviderDelegate*>(delegate());
    127   base::Time last_modification(provider_delegate->GetLastModification());
    128   if (last_modification.is_null())
    129     return true;
    130 
    131   // If there was a change since the last recorded modification, wait some more.
    132   if (last_modification != last_modification_file_) {
    133     last_modification_file_ = last_modification;
    134     last_modification_clock_ = now;
    135     *delay = settle_interval_;
    136     return false;
    137   }
    138 
    139   // Check whether the settle interval has elapsed.
    140   base::TimeDelta age = now - last_modification_clock_;
    141   if (age < settle_interval_) {
    142     *delay = settle_interval_ - age;
    143     return false;
    144   }
    145 
    146   return true;
    147 }
    148 
    149 }  // namespace policy
    150