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/config_dir_policy_provider.h" | 5 #include "chrome/browser/policy/config_dir_policy_provider.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 | 8 |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/scoped_ptr.h" |
11 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
12 #include "base/values.h" | 13 #include "base/values.h" |
13 #include "chrome/common/json_value_serializer.h" | 14 #include "chrome/common/json_value_serializer.h" |
14 | 15 |
15 namespace { | 16 ConfigDirPolicyProvider::ConfigDirPolicyProvider(const FilePath& config_dir) |
16 | 17 : config_dir_(config_dir) { |
17 // Amount of time we wait for the files in the policy directory to settle before | |
18 // trying to load it. This alleviates the problem of reading partially written | |
19 // files and allows to batch quasi-simultaneous changes. | |
20 const int kSettleIntervalSeconds = 5; | |
21 | |
22 // The time interval for rechecking policy. This is our fallback in case the | |
23 // directory watch fails or doesn't report a change. | |
24 const int kReloadIntervalMinutes = 15; | |
25 | |
26 } // namespace | |
27 | |
28 // PolicyDirLoader implementation: | |
29 | |
30 PolicyDirLoader::PolicyDirLoader( | |
31 base::WeakPtr<ConfigDirPolicyProvider> provider, | |
32 const FilePath& config_dir, | |
33 int settle_interval_seconds, | |
34 int reload_interval_minutes) | |
35 : provider_(provider), | |
36 origin_loop_(MessageLoop::current()), | |
37 config_dir_(config_dir), | |
38 reload_task_(NULL), | |
39 settle_interval_seconds_(settle_interval_seconds), | |
40 reload_interval_minutes_(reload_interval_minutes) { | |
41 // Force an initial load, so GetPolicy() works. | |
42 policy_.reset(Load()); | |
43 DCHECK(policy_.get()); | |
44 } | 18 } |
45 | 19 |
46 void PolicyDirLoader::Stop() { | 20 bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) { |
47 if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) { | 21 scoped_ptr<DictionaryValue> policy(ReadPolicies()); |
48 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, | 22 DecodePolicyValueTree(policy.get(), store); |
49 NewRunnableMethod(this, &PolicyDirLoader::Stop)); | 23 return true; |
50 return; | |
51 } | |
52 | |
53 if (reload_task_) { | |
54 reload_task_->Cancel(); | |
55 reload_task_ = NULL; | |
56 } | |
57 } | 24 } |
58 | 25 |
59 void PolicyDirLoader::Reload() { | 26 DictionaryValue* ConfigDirPolicyProvider::ReadPolicies() { |
60 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | |
61 | |
62 // Check the directory time in order to see whether a reload is required. | |
63 base::Time now = base::Time::Now(); | |
64 base::TimeDelta delay; | |
65 if (!IsSafeToReloadPolicy(now, &delay)) { | |
66 ScheduleReloadTask(delay); | |
67 return; | |
68 } | |
69 | |
70 // Load the policy definitions. | |
71 scoped_ptr<DictionaryValue> new_policy(Load()); | |
72 | |
73 // Check again in case the directory has changed while reading it. | |
74 if (!IsSafeToReloadPolicy(now, &delay)) { | |
75 ScheduleReloadTask(delay); | |
76 return; | |
77 } | |
78 | |
79 // Replace policy definition. | |
80 bool changed = false; | |
81 { | |
82 AutoLock lock(lock_); | |
83 changed = !policy_->Equals(new_policy.get()); | |
84 policy_.reset(new_policy.release()); | |
85 } | |
86 | |
87 // There's a change, report it! | |
88 if (changed) { | |
89 LOG(INFO) << "Policy reload from " << config_dir_.value() << " succeeded."; | |
90 origin_loop_->PostTask(FROM_HERE, | |
91 NewRunnableMethod(this, &PolicyDirLoader::NotifyPolicyChanged)); | |
92 } | |
93 | |
94 // As a safeguard in case the file watcher fails, schedule a reload task | |
95 // that'll make us recheck after a reasonable interval. | |
96 ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_)); | |
97 } | |
98 | |
99 DictionaryValue* PolicyDirLoader::GetPolicy() { | |
100 AutoLock lock(lock_); | |
101 return static_cast<DictionaryValue*>(policy_->DeepCopy()); | |
102 } | |
103 | |
104 void PolicyDirLoader::OnFilePathChanged(const FilePath& path) { | |
105 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | |
106 Reload(); | |
107 } | |
108 | |
109 void PolicyDirLoader::OnError() { | |
110 LOG(ERROR) << "FileWatcher on " << config_dir_.value() << " failed."; | |
111 } | |
112 | |
113 DictionaryValue* PolicyDirLoader::Load() { | |
114 // Enumerate the files and sort them lexicographically. | 27 // Enumerate the files and sort them lexicographically. |
115 std::set<FilePath> files; | 28 std::set<FilePath> files; |
116 file_util::FileEnumerator file_enumerator(config_dir_, false, | 29 file_util::FileEnumerator file_enumerator(config_dir_, false, |
117 file_util::FileEnumerator::FILES); | 30 file_util::FileEnumerator::FILES); |
118 for (FilePath config_file_path = file_enumerator.Next(); | 31 for (FilePath config_file_path = file_enumerator.Next(); |
119 !config_file_path.empty(); config_file_path = file_enumerator.Next()) | 32 !config_file_path.empty(); config_file_path = file_enumerator.Next()) |
120 files.insert(config_file_path); | 33 files.insert(config_file_path); |
121 | 34 |
122 // Start with an empty dictionary and merge the files' contents. | 35 // Start with an empty dictionary and merge the files' contents. |
123 DictionaryValue* policy = new DictionaryValue; | 36 DictionaryValue* policy = new DictionaryValue; |
(...skipping 12 matching lines...) Expand all Loading... |
136 LOG(WARNING) << "Expected JSON dictionary in configuration file " | 49 LOG(WARNING) << "Expected JSON dictionary in configuration file " |
137 << config_file_iter->value(); | 50 << config_file_iter->value(); |
138 continue; | 51 continue; |
139 } | 52 } |
140 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get())); | 53 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get())); |
141 } | 54 } |
142 | 55 |
143 return policy; | 56 return policy; |
144 } | 57 } |
145 | 58 |
146 bool PolicyDirLoader::IsSafeToReloadPolicy(const base::Time& now, | |
147 base::TimeDelta* delay) { | |
148 DCHECK(delay); | |
149 file_util::FileInfo dir_info; | |
150 | |
151 // Reading an empty directory or a file is always safe. | |
152 if (!file_util::GetFileInfo(config_dir_, &dir_info) || | |
153 !dir_info.is_directory) { | |
154 return true; | |
155 } | |
156 | |
157 // Check for too recent changes. | |
158 base::TimeDelta settleInterval( | |
159 base::TimeDelta::FromSeconds(settle_interval_seconds_)); | |
160 base::TimeDelta age = now - dir_info.last_modified; | |
161 if (age < settleInterval) { | |
162 *delay = settleInterval - age; | |
163 return false; | |
164 } | |
165 | |
166 return true; | |
167 } | |
168 | |
169 void PolicyDirLoader::ScheduleReloadTask(const base::TimeDelta& delay) { | |
170 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | |
171 | |
172 if (reload_task_) | |
173 reload_task_->Cancel(); | |
174 | |
175 reload_task_ = NewRunnableMethod(this, &PolicyDirLoader::ReloadFromTask); | |
176 ChromeThread::PostDelayedTask(ChromeThread::FILE, FROM_HERE, reload_task_, | |
177 delay.InMilliseconds()); | |
178 } | |
179 | |
180 void PolicyDirLoader::NotifyPolicyChanged() { | |
181 DCHECK_EQ(origin_loop_, MessageLoop::current()); | |
182 if (provider_) | |
183 provider_->NotifyStoreOfPolicyChange(); | |
184 } | |
185 | |
186 void PolicyDirLoader::ReloadFromTask() { | |
187 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | |
188 | |
189 // Drop the reference to the reload task, since the task might be the only | |
190 // referer that keeps us alive, so we should not Cancel() it. | |
191 reload_task_ = NULL; | |
192 | |
193 Reload(); | |
194 } | |
195 | |
196 // PolicyDirWatcher implementation: | |
197 | |
198 void PolicyDirWatcher::Init(PolicyDirLoader* loader) { | |
199 // Initialization can happen early when the file thread is not yet available. | |
200 // So post a task to ourselves on th UI thread which will run after threading | |
201 // is up and schedule watch initialization on the file thread. | |
202 ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, | |
203 NewRunnableMethod(this, | |
204 &PolicyDirWatcher::InitWatcher, | |
205 scoped_refptr<PolicyDirLoader>(loader))); | |
206 } | |
207 | |
208 void PolicyDirWatcher::InitWatcher( | |
209 const scoped_refptr<PolicyDirLoader>& loader) { | |
210 if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) { | |
211 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, | |
212 NewRunnableMethod(this, &PolicyDirWatcher::InitWatcher, loader)); | |
213 return; | |
214 } | |
215 | |
216 if (!Watch(loader->config_dir(), loader.get())) | |
217 loader->OnError(); | |
218 | |
219 // There might have been changes to the directory in the time between | |
220 // construction of the loader and initialization of the watcher. Call reload | |
221 // to detect if that is the case. | |
222 loader->Reload(); | |
223 } | |
224 | |
225 // ConfigDirPolicyProvider implementation: | |
226 | |
227 ConfigDirPolicyProvider::ConfigDirPolicyProvider(const FilePath& config_dir) { | |
228 loader_ = new PolicyDirLoader(AsWeakPtr(), config_dir, kSettleIntervalSeconds, | |
229 kReloadIntervalMinutes); | |
230 watcher_ = new PolicyDirWatcher; | |
231 watcher_->Init(loader_.get()); | |
232 } | |
233 | |
234 ConfigDirPolicyProvider::~ConfigDirPolicyProvider() { | |
235 loader_->Stop(); | |
236 } | |
237 | |
238 bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) { | |
239 scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); | |
240 DCHECK(policy.get()); | |
241 DecodePolicyValueTree(policy.get(), store); | |
242 return true; | |
243 } | |
244 | |
245 void ConfigDirPolicyProvider::DecodePolicyValueTree( | 59 void ConfigDirPolicyProvider::DecodePolicyValueTree( |
246 DictionaryValue* policies, | 60 DictionaryValue* policies, |
247 ConfigurationPolicyStore* store) { | 61 ConfigurationPolicyStore* store) { |
248 const PolicyValueMap* mapping = PolicyValueMapping(); | 62 const PolicyValueMap* mapping = PolicyValueMapping(); |
249 for (PolicyValueMap::const_iterator i = mapping->begin(); | 63 for (PolicyValueMap::const_iterator i = mapping->begin(); |
250 i != mapping->end(); ++i) { | 64 i != mapping->end(); ++i) { |
251 const PolicyValueMapEntry& entry(*i); | 65 const PolicyValueMapEntry& entry(*i); |
252 Value* value; | 66 Value* value; |
253 if (policies->Get(entry.name, &value) && value->IsType(entry.value_type)) | 67 if (policies->Get(entry.name, &value) && value->IsType(entry.value_type)) |
254 store->Apply(entry.policy_type, value->DeepCopy()); | 68 store->Apply(entry.policy_type, value->DeepCopy()); |
255 } | 69 } |
256 | 70 |
257 // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| | 71 // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| |
258 // supports it. | 72 // supports it. |
259 } | 73 } |
| 74 |
OLD | NEW |