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 <set> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "base/task.h" | 11 #include "base/task.h" |
12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
13 #include "base/values.h" | 13 #include "base/values.h" |
14 #include "chrome/browser/browser_thread.h" | 14 #include "chrome/browser/browser_thread.h" |
15 #include "chrome/common/json_value_serializer.h" | 15 #include "chrome/common/json_value_serializer.h" |
16 | 16 |
17 namespace policy { | 17 namespace { |
18 | 18 |
19 // Amount of time we wait for the files on disk to settle before trying to load | 19 // Amount of time we wait for the files on disk to settle before trying to load |
20 // it. This alleviates the problem of reading partially written files and allows | 20 // it. This alleviates the problem of reading partially written files and allows |
21 // to batch quasi-simultaneous changes. | 21 // to batch quasi-simultaneous changes. |
22 const int kSettleIntervalSeconds = 5; | 22 const int kSettleIntervalSeconds = 5; |
23 | 23 |
24 // The time interval for rechecking policy. This is our fallback in case the | |
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 } | 24 } |
32 | 25 |
33 FileBasedPolicyProvider::Delegate::Delegate(const FilePath& config_file_path) | 26 namespace policy { |
34 : config_file_path_(config_file_path) { | 27 |
28 FileBasedPolicyProvider::WatcherDelegate::WatcherDelegate( | |
29 const FilePath& config_file_path, | |
30 AsynchronousPolicyProvider::PolicyChangeObserver* observer) | |
31 : config_file_path_(config_file_path), | |
32 observer_(observer) { | |
35 } | 33 } |
36 | 34 |
37 FileBasedPolicyProvider::FileBasedPolicyProvider( | 35 void FileBasedPolicyProvider::WatcherDelegate::Init() { |
38 const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, | 36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
39 FileBasedPolicyProvider::Delegate* delegate) | 37 watcher_.reset(new FilePathWatcher()); |
40 : ConfigurationPolicyProvider(policy_list) { | 38 if (!config_file_path_.empty() && |
41 loader_ = new FileBasedPolicyLoader(AsWeakPtr(), | 39 !watcher_->Watch(config_file_path_, this)) |
42 delegate, | 40 OnError(); |
43 kSettleIntervalSeconds, | 41 |
44 kReloadIntervalMinutes); | 42 // There might have been changes to the directory in the time between |
45 watcher_ = new FileBasedPolicyWatcher; | 43 // construction of the loader and initialization of the watcher. Call reload |
46 watcher_->Init(loader_.get()); | 44 // to detect if that is the case. |
45 observer_->OnPolicyChange(); | |
47 } | 46 } |
48 | 47 |
49 FileBasedPolicyProvider::~FileBasedPolicyProvider() { | 48 void FileBasedPolicyProvider::WatcherDelegate::Stop() { |
50 loader_->Stop(); | 49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
50 watcher_.reset(NULL); | |
51 observer_.reset(NULL); | |
Mattias Nissler (ping if slow)
2010/12/02 18:16:00
No need to pass the NULL literal, you can just use
danno
2010/12/03 17:05:38
Done.
| |
51 } | 52 } |
52 | 53 |
53 bool FileBasedPolicyProvider::Provide( | 54 void FileBasedPolicyProvider::WatcherDelegate::OnFilePathChanged( |
54 ConfigurationPolicyStoreInterface* store) { | 55 const FilePath& path) { |
55 scoped_ptr<DictionaryValue> policy(loader_->GetPolicy()); | 56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
56 DCHECK(policy.get()); | 57 if (observer_.get()) |
57 DecodePolicyValueTree(policy.get(), store); | 58 observer_->OnPolicyChange(); |
58 return true; | |
59 } | 59 } |
60 | 60 |
61 // FileBasedPolicyLoader implementation: | 61 void FileBasedPolicyProvider::WatcherDelegate::OnError() { |
62 | 62 LOG(ERROR) << "FilePathWatcher on " << config_file_path_.value() |
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."; | 63 << " failed."; |
146 } | 64 } |
147 | 65 |
148 FileBasedPolicyLoader::~FileBasedPolicyLoader() { | 66 FileBasedPolicyProvider::ProviderDelegate::ProviderDelegate( |
67 const FilePath& config_file_path) | |
68 : config_file_path_(config_file_path), | |
69 settle_interval_seconds_(kSettleIntervalSeconds){ | |
149 } | 70 } |
150 | 71 |
151 bool FileBasedPolicyLoader::IsSafeToReloadPolicy(const base::Time& now, | 72 FileBasedPolicyProvider::ProviderDelegate::~ProviderDelegate() { |
152 base::TimeDelta* delay) { | 73 } |
74 | |
75 bool FileBasedPolicyProvider::ProviderDelegate::IsSafeToReloadPolicy( | |
76 const base::Time& now, | |
77 base::TimeDelta* delay) { | |
153 DCHECK(delay); | 78 DCHECK(delay); |
154 | 79 |
155 // A null modification time indicates there's no data. | 80 // A null modification time indicates there's no data. |
156 base::Time last_modification(delegate_->GetLastModification()); | 81 base::Time last_modification(GetLastModification()); |
157 if (last_modification.is_null()) | 82 if (last_modification.is_null()) |
158 return true; | 83 return true; |
159 | 84 |
160 // If there was a change since the last recorded modification, wait some more. | 85 // If there was a change since the last recorded modification, wait some more. |
161 base::TimeDelta settleInterval( | 86 base::TimeDelta settleInterval( |
162 base::TimeDelta::FromSeconds(settle_interval_seconds_)); | 87 base::TimeDelta::FromSeconds(settle_interval_seconds_)); |
163 if (last_modification != last_modification_file_) { | 88 if (last_modification != last_modification_file_) { |
164 last_modification_file_ = last_modification; | 89 last_modification_file_ = last_modification; |
165 last_modification_clock_ = now; | 90 last_modification_clock_ = now; |
166 *delay = settleInterval; | 91 *delay = settleInterval; |
167 return false; | 92 return false; |
168 } | 93 } |
169 | 94 |
170 // Check whether the settle interval has elapsed. | 95 // Check whether the settle interval has elapsed. |
171 base::TimeDelta age = now - last_modification_clock_; | 96 base::TimeDelta age = now - last_modification_clock_; |
172 if (age < settleInterval) { | 97 if (age < settleInterval) { |
173 *delay = settleInterval - age; | 98 *delay = settleInterval - age; |
174 return false; | 99 return false; |
175 } | 100 } |
176 | 101 |
177 return true; | 102 return true; |
178 } | 103 } |
179 | 104 |
180 void FileBasedPolicyLoader::ScheduleReloadTask(const base::TimeDelta& delay) { | 105 void FileBasedPolicyProvider::ProviderDelegate::Init( |
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 106 AsynchronousPolicyProvider::PolicyChangeObserver* observer) { |
107 watcher_delegate_ = new FileBasedPolicyProvider::WatcherDelegate( | |
108 config_file_path_, | |
109 observer); | |
182 | 110 |
183 if (reload_task_) | 111 // Initialization can happen early when the file thread is not |
184 reload_task_->Cancel(); | 112 // yet available. So post a task on the FILE thread which will run after |
185 | 113 // threading is up and schedule watch initialization on the file thread. |
186 reload_task_ = | 114 BrowserThread::PostTask( |
187 NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask); | 115 BrowserThread::FILE, FROM_HERE, |
188 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_, | 116 NewRunnableMethod(watcher_delegate_.get(), |
189 delay.InMilliseconds()); | 117 &FileBasedPolicyProvider::WatcherDelegate::Init)); |
190 } | 118 } |
191 | 119 |
192 void FileBasedPolicyLoader::NotifyPolicyChanged() { | 120 void FileBasedPolicyProvider::ProviderDelegate::Stop() { |
193 DCHECK_EQ(origin_loop_, MessageLoop::current()); | 121 BrowserThread::PostTask( |
194 if (provider_) | 122 BrowserThread::FILE, FROM_HERE, |
195 provider_->NotifyStoreOfPolicyChange(); | 123 NewRunnableMethod(watcher_delegate_.get(), |
124 &FileBasedPolicyProvider::WatcherDelegate::Stop)); | |
196 } | 125 } |
197 | 126 |
198 void FileBasedPolicyLoader::ReloadFromTask() { | 127 FileBasedPolicyProvider::FileBasedPolicyProvider( |
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 128 const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, |
200 | 129 FileBasedPolicyProvider::ProviderDelegate* delegate) |
201 // Drop the reference to the reload task, since the task might be the only | 130 : AsynchronousPolicyProvider(policy_list, delegate) { |
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 } | 131 } |
243 | 132 |
244 } // namespace policy | 133 } // namespace policy |
OLD | NEW |