1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/policy/file_based_policy_loader.h" 6 7 #include "base/compiler_specific.h" 8 #include "content/browser/browser_thread.h" 9 10 using ::base::files::FilePathWatcher; 11 12 namespace { 13 14 // Amount of time we wait for the files on disk to settle before trying to load 15 // them. This alleviates the problem of reading partially written files and 16 // makes it possible to batch quasi-simultaneous changes. 17 const int kSettleIntervalSeconds = 5; 18 19 // The time interval for rechecking policy. This is our fallback in case the 20 // delegate never reports a change to the ReloadObserver. 21 const int kReloadIntervalMinutes = 15; 22 23 } // namespace 24 25 namespace policy { 26 27 FileBasedPolicyLoader::FileBasedPolicyLoader( 28 FileBasedPolicyProvider::ProviderDelegate* provider_delegate) 29 : AsynchronousPolicyLoader(provider_delegate, 30 kReloadIntervalMinutes), 31 config_file_path_(provider_delegate->config_file_path()), 32 settle_interval_(base::TimeDelta::FromSeconds(kSettleIntervalSeconds)) { 33 } 34 35 FileBasedPolicyLoader::~FileBasedPolicyLoader() {} 36 37 class FileBasedPolicyWatcherDelegate : public FilePathWatcher::Delegate { 38 public: 39 explicit FileBasedPolicyWatcherDelegate( 40 scoped_refptr<FileBasedPolicyLoader> loader) 41 : loader_(loader) {} 42 virtual ~FileBasedPolicyWatcherDelegate() {} 43 44 // FilePathWatcher::Delegate implementation: 45 virtual void OnFilePathChanged(const FilePath& path) OVERRIDE { 46 loader_->OnFilePathChanged(path); 47 } 48 49 virtual void OnFilePathError(const FilePath& path) OVERRIDE { 50 loader_->OnFilePathError(path); 51 } 52 53 private: 54 scoped_refptr<FileBasedPolicyLoader> loader_; 55 DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcherDelegate); 56 }; 57 58 void FileBasedPolicyLoader::OnFilePathChanged( 59 const FilePath& path) { 60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 61 Reload(); 62 } 63 64 void FileBasedPolicyLoader::OnFilePathError(const FilePath& path) { 65 LOG(ERROR) << "FilePathWatcher on " << path.value() 66 << " failed."; 67 } 68 69 void FileBasedPolicyLoader::Reload() { 70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 71 72 if (!delegate()) 73 return; 74 75 // Check the directory time in order to see whether a reload is required. 76 base::TimeDelta delay; 77 base::Time now = base::Time::Now(); 78 if (!IsSafeToReloadPolicy(now, &delay)) { 79 ScheduleReloadTask(delay); 80 return; 81 } 82 83 // Load the policy definitions. 84 scoped_ptr<DictionaryValue> new_policy(delegate()->Load()); 85 86 // Check again in case the directory has changed while reading it. 87 if (!IsSafeToReloadPolicy(now, &delay)) { 88 ScheduleReloadTask(delay); 89 return; 90 } 91 92 PostUpdatePolicyTask(new_policy.release()); 93 94 ScheduleFallbackReloadTask(); 95 } 96 97 void FileBasedPolicyLoader::InitOnFileThread() { 98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 99 watcher_.reset(new FilePathWatcher); 100 const FilePath& path = config_file_path(); 101 if (!path.empty() && 102 !watcher_->Watch(path, new FileBasedPolicyWatcherDelegate(this))) { 103 OnFilePathError(path); 104 } 105 106 // There might have been changes to the directory in the time between 107 // construction of the loader and initialization of the watcher. Call reload 108 // to detect if that is the case. 109 Reload(); 110 111 ScheduleFallbackReloadTask(); 112 } 113 114 void FileBasedPolicyLoader::StopOnFileThread() { 115 watcher_.reset(); 116 AsynchronousPolicyLoader::StopOnFileThread(); 117 } 118 119 bool FileBasedPolicyLoader::IsSafeToReloadPolicy( 120 const base::Time& now, 121 base::TimeDelta* delay) { 122 DCHECK(delay); 123 124 // A null modification time indicates there's no data. 125 FileBasedPolicyProvider::ProviderDelegate* provider_delegate = 126 static_cast<FileBasedPolicyProvider::ProviderDelegate*>(delegate()); 127 base::Time last_modification(provider_delegate->GetLastModification()); 128 if (last_modification.is_null()) 129 return true; 130 131 // If there was a change since the last recorded modification, wait some more. 132 if (last_modification != last_modification_file_) { 133 last_modification_file_ = last_modification; 134 last_modification_clock_ = now; 135 *delay = settle_interval_; 136 return false; 137 } 138 139 // Check whether the settle interval has elapsed. 140 base::TimeDelta age = now - last_modification_clock_; 141 if (age < settle_interval_) { 142 *delay = settle_interval_ - age; 143 return false; 144 } 145 146 return true; 147 } 148 149 } // namespace policy 150