• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FileBasedPolicyLoader(FileBasedPolicyProvider::ProviderDelegate * provider_delegate)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 
~FileBasedPolicyLoader()35 FileBasedPolicyLoader::~FileBasedPolicyLoader() {}
36 
37 class FileBasedPolicyWatcherDelegate : public FilePathWatcher::Delegate {
38  public:
FileBasedPolicyWatcherDelegate(scoped_refptr<FileBasedPolicyLoader> loader)39   explicit FileBasedPolicyWatcherDelegate(
40       scoped_refptr<FileBasedPolicyLoader> loader)
41       : loader_(loader) {}
~FileBasedPolicyWatcherDelegate()42   virtual ~FileBasedPolicyWatcherDelegate() {}
43 
44   // FilePathWatcher::Delegate implementation:
OnFilePathChanged(const FilePath & path)45   virtual void OnFilePathChanged(const FilePath& path) OVERRIDE {
46     loader_->OnFilePathChanged(path);
47   }
48 
OnFilePathError(const FilePath & path)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 
OnFilePathChanged(const FilePath & path)58 void FileBasedPolicyLoader::OnFilePathChanged(
59     const FilePath& path) {
60   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
61   Reload();
62 }
63 
OnFilePathError(const FilePath & path)64 void FileBasedPolicyLoader::OnFilePathError(const FilePath& path) {
65   LOG(ERROR) << "FilePathWatcher on " << path.value()
66              << " failed.";
67 }
68 
Reload()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 
InitOnFileThread()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 
StopOnFileThread()114 void FileBasedPolicyLoader::StopOnFileThread() {
115   watcher_.reset();
116   AsynchronousPolicyLoader::StopOnFileThread();
117 }
118 
IsSafeToReloadPolicy(const base::Time & now,base::TimeDelta * delay)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