普通文本  |  150行  |  4.42 KB

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