Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(242)

Side by Side Diff: chrome/browser/policy/config_dir_policy_provider.cc

Issue 4062002: Dynamic policy refresh support for the Mac. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: s/map/list/, nits. Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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"
11 #include "base/message_loop.h"
12 #include "base/task.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/values.h" 10 #include "base/values.h"
15 #include "chrome/browser/browser_thread.h"
16 #include "chrome/common/json_value_serializer.h" 11 #include "chrome/common/json_value_serializer.h"
17 12
18 namespace policy { 13 namespace policy {
19 14
20 // Amount of time we wait for the files in the policy directory to settle before 15 ConfigDirPolicyLoader::ConfigDirPolicyLoader(const FilePath& config_dir)
21 // trying to load it. This alleviates the problem of reading partially written 16 : FileBasedPolicyProvider::Delegate(config_dir) {
22 // files and allows to batch quasi-simultaneous changes.
23 const int kSettleIntervalSeconds = 5;
24
25 // The time interval for rechecking policy. This is our fallback in case the
26 // directory watch fails or doesn't report a change.
27 const int kReloadIntervalMinutes = 15;
28
29 // PolicyDirLoader implementation:
30
31 PolicyDirLoader::PolicyDirLoader(
32 base::WeakPtr<ConfigDirPolicyProvider> provider,
33 const FilePath& config_dir,
34 int settle_interval_seconds,
35 int reload_interval_minutes)
36 : provider_(provider),
37 origin_loop_(MessageLoop::current()),
38 config_dir_(config_dir),
39 reload_task_(NULL),
40 settle_interval_seconds_(settle_interval_seconds),
41 reload_interval_minutes_(reload_interval_minutes) {
42 // Force an initial load, so GetPolicy() works.
43 policy_.reset(Load());
44 DCHECK(policy_.get());
45 } 17 }
46 18
47 PolicyDirLoader::~PolicyDirLoader() {} 19 DictionaryValue* ConfigDirPolicyLoader::Load() {
48
49 void PolicyDirLoader::Stop() {
50 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
51 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
52 NewRunnableMethod(this, &PolicyDirLoader::Stop));
53 return;
54 }
55
56 if (reload_task_) {
57 reload_task_->Cancel();
58 reload_task_ = NULL;
59 }
60 }
61
62 void PolicyDirLoader::Reload() {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
64
65 // Check the directory time in order to see whether a reload is required.
66 base::TimeDelta delay;
67 base::Time now = base::Time::Now();
68 if (!IsSafeToReloadPolicy(now, &delay)) {
69 ScheduleReloadTask(delay);
70 return;
71 }
72
73 // Load the policy definitions.
74 scoped_ptr<DictionaryValue> new_policy(Load());
75
76 // Check again in case the directory has changed while reading it.
77 if (!IsSafeToReloadPolicy(now, &delay)) {
78 ScheduleReloadTask(delay);
79 return;
80 }
81
82 // Replace policy definition.
83 bool changed = false;
84 {
85 AutoLock lock(lock_);
86 changed = !policy_->Equals(new_policy.get());
87 policy_.reset(new_policy.release());
88 }
89
90 // There's a change, report it!
91 if (changed) {
92 VLOG(1) << "Policy reload from " << config_dir_.value() << " succeeded.";
93 origin_loop_->PostTask(FROM_HERE,
94 NewRunnableMethod(this, &PolicyDirLoader::NotifyPolicyChanged));
95 }
96
97 // As a safeguard in case the file watcher fails, schedule a reload task
98 // that'll make us recheck after a reasonable interval.
99 ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_));
100 }
101
102 DictionaryValue* PolicyDirLoader::GetPolicy() {
103 AutoLock lock(lock_);
104 return static_cast<DictionaryValue*>(policy_->DeepCopy());
105 }
106
107 void PolicyDirLoader::OnFilePathChanged(const FilePath& path) {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
109 Reload();
110 }
111
112 void PolicyDirLoader::OnError() {
113 LOG(ERROR) << "FileWatcher on " << config_dir_.value() << " failed.";
114 }
115
116 DictionaryValue* PolicyDirLoader::Load() {
117 // Enumerate the files and sort them lexicographically. 20 // Enumerate the files and sort them lexicographically.
118 std::set<FilePath> files; 21 std::set<FilePath> files;
119 file_util::FileEnumerator file_enumerator(config_dir_, false, 22 file_util::FileEnumerator file_enumerator(config_file_path(), false,
120 file_util::FileEnumerator::FILES); 23 file_util::FileEnumerator::FILES);
121 for (FilePath config_file_path = file_enumerator.Next(); 24 for (FilePath config_file_path = file_enumerator.Next();
122 !config_file_path.empty(); config_file_path = file_enumerator.Next()) 25 !config_file_path.empty(); config_file_path = file_enumerator.Next())
123 files.insert(config_file_path); 26 files.insert(config_file_path);
124 27
125 // Start with an empty dictionary and merge the files' contents. 28 // Start with an empty dictionary and merge the files' contents.
126 DictionaryValue* policy = new DictionaryValue; 29 DictionaryValue* policy = new DictionaryValue;
127 for (std::set<FilePath>::iterator config_file_iter = files.begin(); 30 for (std::set<FilePath>::iterator config_file_iter = files.begin();
128 config_file_iter != files.end(); ++config_file_iter) { 31 config_file_iter != files.end(); ++config_file_iter) {
129 JSONFileValueSerializer deserializer(*config_file_iter); 32 JSONFileValueSerializer deserializer(*config_file_iter);
130 int error_code = 0; 33 int error_code = 0;
131 std::string error_msg; 34 std::string error_msg;
132 scoped_ptr<Value> value(deserializer.Deserialize(&error_code, &error_msg)); 35 scoped_ptr<Value> value(deserializer.Deserialize(&error_code, &error_msg));
133 if (!value.get()) { 36 if (!value.get()) {
134 LOG(WARNING) << "Failed to read configuration file " 37 LOG(WARNING) << "Failed to read configuration file "
135 << config_file_iter->value() << ": " << error_msg; 38 << config_file_iter->value() << ": " << error_msg;
136 continue; 39 continue;
137 } 40 }
138 if (!value->IsType(Value::TYPE_DICTIONARY)) { 41 if (!value->IsType(Value::TYPE_DICTIONARY)) {
139 LOG(WARNING) << "Expected JSON dictionary in configuration file " 42 LOG(WARNING) << "Expected JSON dictionary in configuration file "
140 << config_file_iter->value(); 43 << config_file_iter->value();
141 continue; 44 continue;
142 } 45 }
143 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get())); 46 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get()));
144 } 47 }
145 48
146 return policy; 49 return policy;
147 } 50 }
148 51
149 bool PolicyDirLoader::IsSafeToReloadPolicy(const base::Time& now, 52 base::Time ConfigDirPolicyLoader::GetLastModification() {
150 base::TimeDelta* delay) { 53 base::Time last_modification = base::Time();
151 DCHECK(delay); 54 base::PlatformFileInfo file_info;
152 base::PlatformFileInfo dir_info;
153 55
154 // Reading an empty directory or a file is always safe. 56 // If the path does not exist or points to a directory, it's safe to load.
155 if (!file_util::GetFileInfo(config_dir_, &dir_info) || 57 if (!file_util::GetFileInfo(config_file_path(), &file_info) ||
156 !dir_info.is_directory) { 58 !file_info.is_directory) {
157 last_modification_file_ = base::Time(); 59 return last_modification;
158 return true;
159 } 60 }
160 61
161 // If there was a change since the last recorded modification, wait some more. 62 // Enumerate the files and find the most recent modification timestamp.
162 base::TimeDelta settleInterval( 63 file_util::FileEnumerator file_enumerator(config_file_path(),
163 base::TimeDelta::FromSeconds(settle_interval_seconds_)); 64 false,
164 if (dir_info.last_modified != last_modification_file_) { 65 file_util::FileEnumerator::FILES);
165 last_modification_file_ = dir_info.last_modified; 66 for (FilePath config_file = file_enumerator.Next();
166 last_modification_clock_ = now; 67 !config_file.empty();
167 *delay = settleInterval; 68 config_file = file_enumerator.Next()) {
168 return false; 69 if (file_util::GetFileInfo(config_file, &file_info) &&
70 !file_info.is_directory) {
71 last_modification = std::min(last_modification, file_info.last_modified);
72 }
169 } 73 }
170 74
171 // Check whether the settle interval has elapsed. 75 return last_modification;
172 base::TimeDelta age = now - last_modification_clock_;
173 if (age < settleInterval) {
174 *delay = settleInterval - age;
175 return false;
176 }
177
178 return true;
179 } 76 }
180 77
181 void PolicyDirLoader::ScheduleReloadTask(const base::TimeDelta& delay) { 78 ConfigDirPolicyProvider::ConfigDirPolicyProvider(
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 79 const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list,
183 80 const FilePath& config_dir)
184 if (reload_task_) 81 : FileBasedPolicyProvider(policy_list,
185 reload_task_->Cancel(); 82 new ConfigDirPolicyLoader(config_dir)) {
186
187 reload_task_ = NewRunnableMethod(this, &PolicyDirLoader::ReloadFromTask);
188 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_,
189 delay.InMilliseconds());
190 } 83 }
191 84
192 void PolicyDirLoader::NotifyPolicyChanged() {
193 DCHECK_EQ(origin_loop_, MessageLoop::current());
194 if (provider_)
195 provider_->NotifyStoreOfPolicyChange();
196 }
197
198 void PolicyDirLoader::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 // PolicyDirWatcher implementation:
209
210 PolicyDirWatcher::PolicyDirWatcher() {}
211
212 void PolicyDirWatcher::Init(PolicyDirLoader* loader) {
213 // Initialization can happen early when the file thread is not yet available.
214 // So post a task to ourselves on the UI thread which will run after threading
215 // is up and schedule watch initialization on the file thread.
216 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
217 NewRunnableMethod(this,
218 &PolicyDirWatcher::InitWatcher,
219 scoped_refptr<PolicyDirLoader>(loader)));
220 }
221
222 PolicyDirWatcher::~PolicyDirWatcher() {}
223
224 void PolicyDirWatcher::InitWatcher(
225 const scoped_refptr<PolicyDirLoader>& loader) {
226 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
227 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
228 NewRunnableMethod(this, &PolicyDirWatcher::InitWatcher, loader));
229 return;
230 }
231
232 if (!watcher_.Watch(loader->config_dir(), loader.get()))
233 loader->OnError();
234
235 // There might have been changes to the directory in the time between
236 // construction of the loader and initialization of the watcher. Call reload
237 // to detect if that is the case.
238 loader->Reload();
239 }
240
241 // ConfigDirPolicyProvider implementation:
242
243 ConfigDirPolicyProvider::ConfigDirPolicyProvider(
244 const ConfigurationPolicyProvider::StaticPolicyValueMap& policy_map,
245 const FilePath& config_dir)
246 : ConfigurationPolicyProvider(policy_map) {
247 loader_ = new PolicyDirLoader(AsWeakPtr(), config_dir, kSettleIntervalSeconds,
248 kReloadIntervalMinutes);
249 watcher_ = new PolicyDirWatcher;
250 watcher_->Init(loader_.get());
251 }
252
253 ConfigDirPolicyProvider::~ConfigDirPolicyProvider() {
254 loader_->Stop();
255 }
256
257 bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) {
258 scoped_ptr<DictionaryValue> policy(loader_->GetPolicy());
259 DCHECK(policy.get());
260 DecodePolicyValueTree(policy.get(), store);
261 return true;
262 }
263
264 void ConfigDirPolicyProvider::DecodePolicyValueTree(
265 DictionaryValue* policies,
266 ConfigurationPolicyStore* store) {
267 const PolicyValueMap& mapping(policy_value_map());
268 for (PolicyValueMap::const_iterator i = mapping.begin();
269 i != mapping.end(); ++i) {
270 const PolicyValueMapEntry& entry(*i);
271 Value* value;
272 if (policies->Get(entry.name, &value) && value->IsType(entry.value_type))
273 store->Apply(entry.policy_type, value->DeepCopy());
274 }
275
276 // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore|
277 // supports it.
278 }
279 85
280 } // namespace policy 86 } // namespace policy
OLDNEW
« no previous file with comments | « chrome/browser/policy/config_dir_policy_provider.h ('k') | chrome/browser/policy/config_dir_policy_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698