OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/policy/file_based_policy_provider.h" | 5 #include "chrome/browser/policy/file_based_policy_provider.h" |
6 | 6 |
7 #include <set> | 7 #include "chrome/browser/policy/file_based_policy_loader.h" |
8 | |
9 #include "base/logging.h" | |
10 #include "base/message_loop.h" | |
11 #include "base/task.h" | |
12 #include "base/utf_string_conversions.h" | |
13 #include "base/values.h" | |
14 #include "chrome/browser/browser_thread.h" | |
15 #include "chrome/common/json_value_serializer.h" | |
16 | 8 |
17 namespace policy { | 9 namespace policy { |
18 | 10 |
19 // Amount of time we wait for the files on disk to settle before trying to load | 11 FileBasedPolicyProvider::ProviderDelegate::ProviderDelegate( |
20 // it. This alleviates the problem of reading partially written files and allows | 12 const FilePath& config_file_path) |
21 // to batch quasi-simultaneous changes. | 13 : config_file_path_(config_file_path) {} |
22 const int kSettleIntervalSeconds = 5; | |
23 | 14 |
24 // The time interval for rechecking policy. This is our fallback in case the | 15 FileBasedPolicyProvider::ProviderDelegate::~ProviderDelegate() {} |
25 // file path watch fails or doesn't report a change. | |
26 const int kReloadIntervalMinutes = 15; | |
27 | |
28 // FileBasedPolicyProvider implementation: | |
29 | |
30 FileBasedPolicyProvider::Delegate::~Delegate() { | |
31 } | |
32 | |
33 FileBasedPolicyProvider::Delegate::Delegate(const FilePath& config_file_path) | |
34 : config_file_path_(config_file_path) { | |
35 } | |
36 | 16 |
37 FileBasedPolicyProvider::FileBasedPolicyProvider( | 17 FileBasedPolicyProvider::FileBasedPolicyProvider( |
38 const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, | 18 const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, |
39 FileBasedPolicyProvider::Delegate* delegate) | 19 FileBasedPolicyProvider::ProviderDelegate* delegate) |
40 : ConfigurationPolicyProvider(policy_list) { | 20 : AsynchronousPolicyProvider( |
41 loader_ = new FileBasedPolicyLoader(AsWeakPtr(), | 21 policy_list, |
42 delegate, | 22 new FileBasedPolicyLoader(delegate)) {} |
43 kSettleIntervalSeconds, | |
44 kReloadIntervalMinutes); | |
45 watcher_ = new FileBasedPolicyWatcher; | |
46 watcher_->Init(loader_.get()); | |
47 } | |
48 | |
49 FileBasedPolicyProvider::~FileBasedPolicyProvider() { | |
50 loader_->Stop(); | |
51 } | |
52 | |
53 bool FileBasedPolicyProvider::Provide( | |
54 ConfigurationPolicyStoreInterface* store) { | |
55 scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); | |
56 DCHECK(policy.get()); | |
57 DecodePolicyValueTree(policy.get(), store); | |
58 return true; | |
59 } | |
60 | |
61 // FileBasedPolicyLoader implementation: | |
62 | |
63 FileBasedPolicyLoader::FileBasedPolicyLoader( | |
64 base::WeakPtr<ConfigurationPolicyProvider> provider, | |
65 FileBasedPolicyProvider::Delegate* delegate, | |
66 int settle_interval_seconds, | |
67 int reload_interval_minutes) | |
68 : delegate_(delegate), | |
69 provider_(provider), | |
70 origin_loop_(MessageLoop::current()), | |
71 reload_task_(NULL), | |
72 settle_interval_seconds_(settle_interval_seconds), | |
73 reload_interval_minutes_(reload_interval_minutes) { | |
74 // Force an initial load, so GetPolicy() works. | |
75 policy_.reset(delegate_->Load()); | |
76 DCHECK(policy_.get()); | |
77 } | |
78 | |
79 void FileBasedPolicyLoader::Stop() { | |
80 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | |
81 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
82 NewRunnableMethod(this, &FileBasedPolicyLoader::Stop)); | |
83 return; | |
84 } | |
85 | |
86 if (reload_task_) { | |
87 reload_task_->Cancel(); | |
88 reload_task_ = NULL; | |
89 } | |
90 } | |
91 | |
92 void FileBasedPolicyLoader::Reload() { | |
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
94 | |
95 // Check the directory time in order to see whether a reload is required. | |
96 base::TimeDelta delay; | |
97 base::Time now = base::Time::Now(); | |
98 if (!IsSafeToReloadPolicy(now, &delay)) { | |
99 ScheduleReloadTask(delay); | |
100 return; | |
101 } | |
102 | |
103 // Load the policy definitions. | |
104 scoped_ptr<DictionaryValue> new_policy(delegate_->Load()); | |
105 | |
106 // Check again in case the directory has changed while reading it. | |
107 if (!IsSafeToReloadPolicy(now, &delay)) { | |
108 ScheduleReloadTask(delay); | |
109 return; | |
110 } | |
111 | |
112 // Replace policy definition. | |
113 bool changed = false; | |
114 { | |
115 AutoLock lock(lock_); | |
116 changed = !policy_->Equals(new_policy.get()); | |
117 policy_.reset(new_policy.release()); | |
118 } | |
119 | |
120 // There's a change, report it! | |
121 if (changed) { | |
122 VLOG(0) << "Policy reload from " << config_file_path().value() | |
123 << " succeeded."; | |
124 origin_loop_->PostTask(FROM_HERE, | |
125 NewRunnableMethod(this, &FileBasedPolicyLoader::NotifyPolicyChanged)); | |
126 } | |
127 | |
128 // As a safeguard in case the file watcher fails, schedule a reload task | |
129 // that'll make us recheck after a reasonable interval. | |
130 ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); | |
131 } | |
132 | |
133 DictionaryValue* FileBasedPolicyLoader::GetPolicy() { | |
134 AutoLock lock(lock_); | |
135 return static_cast<DictionaryValue*>(policy_->DeepCopy()); | |
136 } | |
137 | |
138 void FileBasedPolicyLoader::OnFilePathChanged(const FilePath& path) { | |
139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
140 Reload(); | |
141 } | |
142 | |
143 void FileBasedPolicyLoader::OnError() { | |
144 LOG(ERROR) << "FilePathWatcher on " << config_file_path().value() | |
145 << " failed."; | |
146 } | |
147 | |
148 FileBasedPolicyLoader::~FileBasedPolicyLoader() { | |
149 } | |
150 | |
151 bool FileBasedPolicyLoader::IsSafeToReloadPolicy(const base::Time& now, | |
152 base::TimeDelta* delay) { | |
153 DCHECK(delay); | |
154 | |
155 // A null modification time indicates there's no data. | |
156 base::Time last_modification(delegate_->GetLastModification()); | |
157 if (last_modification.is_null()) | |
158 return true; | |
159 | |
160 // If there was a change since the last recorded modification, wait some more. | |
161 base::TimeDelta settleInterval( | |
162 base::TimeDelta::FromSeconds(settle_interval_seconds_)); | |
163 if (last_modification != last_modification_file_) { | |
164 last_modification_file_ = last_modification; | |
165 last_modification_clock_ = now; | |
166 *delay = settleInterval; | |
167 return false; | |
168 } | |
169 | |
170 // Check whether the settle interval has elapsed. | |
171 base::TimeDelta age = now - last_modification_clock_; | |
172 if (age < settleInterval) { | |
173 *delay = settleInterval - age; | |
174 return false; | |
175 } | |
176 | |
177 return true; | |
178 } | |
179 | |
180 void FileBasedPolicyLoader::ScheduleReloadTask(const base::TimeDelta& delay) { | |
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
182 | |
183 if (reload_task_) | |
184 reload_task_->Cancel(); | |
185 | |
186 reload_task_ = | |
187 NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask); | |
188 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, | |
189 delay.InMilliseconds()); | |
190 } | |
191 | |
192 void FileBasedPolicyLoader::NotifyPolicyChanged() { | |
193 DCHECK_EQ(origin_loop_, MessageLoop::current()); | |
194 if (provider_) | |
195 provider_->NotifyStoreOfPolicyChange(); | |
196 } | |
197 | |
198 void FileBasedPolicyLoader::ReloadFromTask() { | |
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
200 | |
201 // Drop the reference to the reload task, since the task might be the only | |
202 // referer that keeps us alive, so we should not Cancel() it. | |
203 reload_task_ = NULL; | |
204 | |
205 Reload(); | |
206 } | |
207 | |
208 // FileBasedPolicyWatcher implementation: | |
209 | |
210 FileBasedPolicyWatcher::FileBasedPolicyWatcher() { | |
211 } | |
212 | |
213 void FileBasedPolicyWatcher::Init(FileBasedPolicyLoader* loader) { | |
214 // Initialization can happen early when the file thread is not yet available. | |
215 // So post a task to ourselves on the UI thread which will run after threading | |
216 // is up and schedule watch initialization on the file thread. | |
217 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
218 NewRunnableMethod(this, | |
219 &FileBasedPolicyWatcher::InitWatcher, | |
220 scoped_refptr<FileBasedPolicyLoader>(loader))); | |
221 } | |
222 | |
223 FileBasedPolicyWatcher::~FileBasedPolicyWatcher() { | |
224 } | |
225 | |
226 void FileBasedPolicyWatcher::InitWatcher( | |
227 const scoped_refptr<FileBasedPolicyLoader>& loader) { | |
228 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | |
229 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
230 NewRunnableMethod(this, &FileBasedPolicyWatcher::InitWatcher, loader)); | |
231 return; | |
232 } | |
233 | |
234 if (!loader->config_file_path().empty() && | |
235 !watcher_.Watch(loader->config_file_path(), loader.get())) | |
236 loader->OnError(); | |
237 | |
238 // There might have been changes to the directory in the time between | |
239 // construction of the loader and initialization of the watcher. Call reload | |
240 // to detect if that is the case. | |
241 loader->Reload(); | |
242 } | |
243 | 23 |
244 } // namespace policy | 24 } // namespace policy |
OLD | NEW |