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

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

Issue 3156040: Reland 56832 - Support change detection and reloading in ConfigDirPolicyProvider. (Closed)
Patch Set: Created 10 years, 4 months 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
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" 10 #include "base/logging.h"
11 #include "base/scoped_ptr.h"
12 #include "base/utf_string_conversions.h" 11 #include "base/utf_string_conversions.h"
13 #include "base/values.h" 12 #include "base/values.h"
14 #include "chrome/common/json_value_serializer.h" 13 #include "chrome/common/json_value_serializer.h"
15 14
16 ConfigDirPolicyProvider::ConfigDirPolicyProvider(const FilePath& config_dir) 15 namespace {
17 : config_dir_(config_dir) { 16
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());
18 } 44 }
19 45
20 bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) { 46 void PolicyDirLoader::Stop() {
21 scoped_ptr<DictionaryValue> policy(ReadPolicies()); 47 if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) {
22 DecodePolicyValueTree(policy.get(), store); 48 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
23 return true; 49 NewRunnableMethod(this, &PolicyDirLoader::Stop));
50 return;
51 }
52
53 if (reload_task_) {
54 reload_task_->Cancel();
55 reload_task_ = NULL;
56 }
24 } 57 }
25 58
26 DictionaryValue* ConfigDirPolicyProvider::ReadPolicies() { 59 void PolicyDirLoader::Reload() {
60 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
61
62 // Check the directory time in order to see whether a reload is required.
63 base::TimeDelta delay;
64 base::Time now = base::Time::Now();
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() {
27 // Enumerate the files and sort them lexicographically. 114 // Enumerate the files and sort them lexicographically.
28 std::set<FilePath> files; 115 std::set<FilePath> files;
29 file_util::FileEnumerator file_enumerator(config_dir_, false, 116 file_util::FileEnumerator file_enumerator(config_dir_, false,
30 file_util::FileEnumerator::FILES); 117 file_util::FileEnumerator::FILES);
31 for (FilePath config_file_path = file_enumerator.Next(); 118 for (FilePath config_file_path = file_enumerator.Next();
32 !config_file_path.empty(); config_file_path = file_enumerator.Next()) 119 !config_file_path.empty(); config_file_path = file_enumerator.Next())
33 files.insert(config_file_path); 120 files.insert(config_file_path);
34 121
35 // Start with an empty dictionary and merge the files' contents. 122 // Start with an empty dictionary and merge the files' contents.
36 DictionaryValue* policy = new DictionaryValue; 123 DictionaryValue* policy = new DictionaryValue;
(...skipping 12 matching lines...) Expand all
49 LOG(WARNING) << "Expected JSON dictionary in configuration file " 136 LOG(WARNING) << "Expected JSON dictionary in configuration file "
50 << config_file_iter->value(); 137 << config_file_iter->value();
51 continue; 138 continue;
52 } 139 }
53 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get())); 140 policy->MergeDictionary(static_cast<DictionaryValue*>(value.get()));
54 } 141 }
55 142
56 return policy; 143 return policy;
57 } 144 }
58 145
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 last_modification_file_ = base::Time();
155 return true;
156 }
157
158 // If there was a change since the last recorded modification, wait some more.
159 base::TimeDelta settleInterval(
160 base::TimeDelta::FromSeconds(settle_interval_seconds_));
161 if (dir_info.last_modified != last_modification_file_) {
162 last_modification_file_ = dir_info.last_modified;
163 last_modification_clock_ = now;
164 *delay = settleInterval;
165 return false;
166 }
167
168 // Check whether the settle interval has elapsed.
169 base::TimeDelta age = now - last_modification_clock_;
170 if (age < settleInterval) {
171 *delay = settleInterval - age;
172 return false;
173 }
174
175 return true;
176 }
177
178 void PolicyDirLoader::ScheduleReloadTask(const base::TimeDelta& delay) {
179 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
180
181 if (reload_task_)
182 reload_task_->Cancel();
183
184 reload_task_ = NewRunnableMethod(this, &PolicyDirLoader::ReloadFromTask);
185 ChromeThread::PostDelayedTask(ChromeThread::FILE, FROM_HERE, reload_task_,
186 delay.InMilliseconds());
187 }
188
189 void PolicyDirLoader::NotifyPolicyChanged() {
190 DCHECK_EQ(origin_loop_, MessageLoop::current());
191 if (provider_)
192 provider_->NotifyStoreOfPolicyChange();
193 }
194
195 void PolicyDirLoader::ReloadFromTask() {
196 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
197
198 // Drop the reference to the reload task, since the task might be the only
199 // referer that keeps us alive, so we should not Cancel() it.
200 reload_task_ = NULL;
201
202 Reload();
203 }
204
205 // PolicyDirWatcher implementation:
206
207 void PolicyDirWatcher::Init(PolicyDirLoader* loader) {
208 // Initialization can happen early when the file thread is not yet available.
209 // So post a task to ourselves on th UI thread which will run after threading
210 // is up and schedule watch initialization on the file thread.
211 ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
212 NewRunnableMethod(this,
213 &PolicyDirWatcher::InitWatcher,
214 scoped_refptr<PolicyDirLoader>(loader)));
215 }
216
217 void PolicyDirWatcher::InitWatcher(
218 const scoped_refptr<PolicyDirLoader>& loader) {
219 if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) {
220 ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
221 NewRunnableMethod(this, &PolicyDirWatcher::InitWatcher, loader));
222 return;
223 }
224
225 if (!Watch(loader->config_dir(), loader.get()))
226 loader->OnError();
227
228 // There might have been changes to the directory in the time between
229 // construction of the loader and initialization of the watcher. Call reload
230 // to detect if that is the case.
231 loader->Reload();
232 }
233
234 // ConfigDirPolicyProvider implementation:
235
236 ConfigDirPolicyProvider::ConfigDirPolicyProvider(const FilePath& config_dir) {
237 loader_ = new PolicyDirLoader(AsWeakPtr(), config_dir, kSettleIntervalSeconds,
238 kReloadIntervalMinutes);
239 watcher_ = new PolicyDirWatcher;
240 watcher_->Init(loader_.get());
241 }
242
243 ConfigDirPolicyProvider::~ConfigDirPolicyProvider() {
244 loader_->Stop();
245 }
246
247 bool ConfigDirPolicyProvider::Provide(ConfigurationPolicyStore* store) {
248 scoped_ptr<DictionaryValue> policy(loader_->GetPolicy());
249 DCHECK(policy.get());
250 DecodePolicyValueTree(policy.get(), store);
251 return true;
252 }
253
59 void ConfigDirPolicyProvider::DecodePolicyValueTree( 254 void ConfigDirPolicyProvider::DecodePolicyValueTree(
60 DictionaryValue* policies, 255 DictionaryValue* policies,
61 ConfigurationPolicyStore* store) { 256 ConfigurationPolicyStore* store) {
62 const PolicyValueMap* mapping = PolicyValueMapping(); 257 const PolicyValueMap* mapping = PolicyValueMapping();
63 for (PolicyValueMap::const_iterator i = mapping->begin(); 258 for (PolicyValueMap::const_iterator i = mapping->begin();
64 i != mapping->end(); ++i) { 259 i != mapping->end(); ++i) {
65 const PolicyValueMapEntry& entry(*i); 260 const PolicyValueMapEntry& entry(*i);
66 Value* value; 261 Value* value;
67 if (policies->Get(entry.name, &value) && value->IsType(entry.value_type)) 262 if (policies->Get(entry.name, &value) && value->IsType(entry.value_type))
68 store->Apply(entry.policy_type, value->DeepCopy()); 263 store->Apply(entry.policy_type, value->DeepCopy());
69 } 264 }
70 265
71 // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore| 266 // TODO(mnissler): Handle preference overrides once |ConfigurationPolicyStore|
72 // supports it. 267 // supports it.
73 } 268 }
74
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