• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "chromeos/system/statistics_provider.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/memory/singleton.h"
13 #include "base/path_service.h"
14 #include "base/synchronization/cancellation_flag.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/sys_info.h"
17 #include "base/task_runner.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/time/time.h"
20 #include "chromeos/app_mode/kiosk_oem_manifest_parser.h"
21 #include "chromeos/chromeos_constants.h"
22 #include "chromeos/chromeos_switches.h"
23 #include "chromeos/system/name_value_pairs_parser.h"
24 
25 namespace chromeos {
26 namespace system {
27 
28 namespace {
29 
30 // Path to the tool used to get system info, and delimiters for the output
31 // format of the tool.
32 const char* kCrosSystemTool[] = { "/usr/bin/crossystem" };
33 const char kCrosSystemEq[] = "=";
34 const char kCrosSystemDelim[] = "\n";
35 const char kCrosSystemCommentDelim[] = "#";
36 const char kCrosSystemUnknownValue[] = "(error)";
37 
38 const char kHardwareClassCrosSystemKey[] = "hwid";
39 const char kUnknownHardwareClass[] = "unknown";
40 
41 // File to get machine hardware info from, and key/value delimiters of
42 // the file.
43 // /tmp/machine-info is generated by platform/init/chromeos_startup.
44 const char kMachineHardwareInfoFile[] = "/tmp/machine-info";
45 const char kMachineHardwareInfoEq[] = "=";
46 const char kMachineHardwareInfoDelim[] = " \n";
47 
48 // File to get ECHO coupon info from, and key/value delimiters of
49 // the file.
50 const char kEchoCouponFile[] = "/var/cache/echo/vpd_echo.txt";
51 const char kEchoCouponEq[] = "=";
52 const char kEchoCouponDelim[] = "\n";
53 
54 // File to get VPD info from, and key/value delimiters of the file.
55 const char kVpdFile[] = "/var/log/vpd_2.0.txt";
56 const char kVpdEq[] = "=";
57 const char kVpdDelim[] = "\n";
58 
59 // Timeout that we should wait for statistics to get loaded
60 const int kTimeoutSecs = 3;
61 
62 // The location of OEM manifest file used to trigger OOBE flow for kiosk mode.
63 const CommandLine::CharType kOemManifestFilePath[] =
64     FILE_PATH_LITERAL("/usr/share/oem/oobe/manifest.json");
65 
66 }  // namespace
67 
68 // Key values for GetMachineStatistic()/GetMachineFlag() calls.
69 const char kDevSwitchBootMode[] = "devsw_boot";
70 const char kHardwareClassKey[] = "hardware_class";
71 const char kOffersCouponCodeKey[] = "ubind_attribute";
72 const char kOffersGroupCodeKey[] = "gbind_attribute";
73 
74 // OEM specific statistics. Must be prefixed with "oem_".
75 const char kOemCanExitEnterpriseEnrollmentKey[] = "oem_can_exit_enrollment";
76 const char kOemDeviceRequisitionKey[] = "oem_device_requisition";
77 const char kOemIsEnterpriseManagedKey[] = "oem_enterprise_managed";
78 const char kOemKeyboardDrivenOobeKey[] = "oem_keyboard_driven_oobe";
79 
HasOemPrefix(const std::string & name)80 bool HasOemPrefix(const std::string& name) {
81   return name.substr(0, 4) == "oem_";
82 }
83 
84 // The StatisticsProvider implementation used in production.
85 class StatisticsProviderImpl : public StatisticsProvider {
86  public:
87   // StatisticsProvider implementation:
88   virtual void StartLoadingMachineStatistics(
89       const scoped_refptr<base::TaskRunner>& file_task_runner,
90       bool load_oem_manifest) OVERRIDE;
91   virtual bool GetMachineStatistic(const std::string& name,
92                                    std::string* result) OVERRIDE;
93   virtual bool GetMachineFlag(const std::string& name, bool* result) OVERRIDE;
94   virtual void Shutdown() OVERRIDE;
95 
96   static StatisticsProviderImpl* GetInstance();
97 
98  protected:
99   typedef std::map<std::string, bool> MachineFlags;
100   friend struct DefaultSingletonTraits<StatisticsProviderImpl>;
101 
102   StatisticsProviderImpl();
103   virtual ~StatisticsProviderImpl();
104 
105   // Waits up to |kTimeoutSecs| for statistics to be loaded. Returns true if
106   // they were loaded successfully.
107   bool WaitForStatisticsLoaded();
108 
109   // Loads the machine statistics off of disk. Runs on the file thread.
110   void LoadMachineStatistics(bool load_oem_manifest);
111 
112   // Loads the OEM statistics off of disk. Runs on the file thread.
113   void LoadOemManifestFromFile(const base::FilePath& file);
114 
115   bool load_statistics_started_;
116   NameValuePairsParser::NameValueMap machine_info_;
117   MachineFlags machine_flags_;
118   base::CancellationFlag cancellation_flag_;
119   // |on_statistics_loaded_| protects |machine_info_| and |machine_flags_|.
120   base::WaitableEvent on_statistics_loaded_;
121   bool oem_manifest_loaded_;
122 
123  private:
124   DISALLOW_COPY_AND_ASSIGN(StatisticsProviderImpl);
125 };
126 
WaitForStatisticsLoaded()127 bool StatisticsProviderImpl::WaitForStatisticsLoaded() {
128   CHECK(load_statistics_started_);
129   if (on_statistics_loaded_.IsSignaled())
130     return true;
131 
132   // Block if the statistics are not loaded yet. Normally this shouldn't
133   // happen excpet during OOBE.
134   base::Time start_time = base::Time::Now();
135   base::ThreadRestrictions::ScopedAllowWait allow_wait;
136   on_statistics_loaded_.TimedWait(base::TimeDelta::FromSeconds(kTimeoutSecs));
137 
138   base::TimeDelta dtime = base::Time::Now() - start_time;
139   if (on_statistics_loaded_.IsSignaled()) {
140     LOG(ERROR) << "Statistics loaded after waiting "
141                << dtime.InMilliseconds() << "ms. ";
142     return true;
143   }
144 
145   LOG(ERROR) << "Statistics not loaded after waiting "
146              << dtime.InMilliseconds() << "ms. ";
147   return false;
148 }
149 
GetMachineStatistic(const std::string & name,std::string * result)150 bool StatisticsProviderImpl::GetMachineStatistic(const std::string& name,
151                                                  std::string* result) {
152   VLOG(1) << "Machine Statistic requested: " << name;
153   if (!WaitForStatisticsLoaded()) {
154     LOG(ERROR) << "GetMachineStatistic called before load started: " << name;
155     return false;
156   }
157 
158   NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name);
159   if (iter == machine_info_.end()) {
160     if (base::SysInfo::IsRunningOnChromeOS() &&
161         (oem_manifest_loaded_ || !HasOemPrefix(name))) {
162       LOG(WARNING) << "Requested statistic not found: " << name;
163     }
164     return false;
165   }
166   *result = iter->second;
167   return true;
168 }
169 
GetMachineFlag(const std::string & name,bool * result)170 bool StatisticsProviderImpl::GetMachineFlag(const std::string& name,
171                                             bool* result) {
172   VLOG(1) << "Machine Flag requested: " << name;
173   if (!WaitForStatisticsLoaded()) {
174     LOG(ERROR) << "GetMachineFlag called before load started: " << name;
175     return false;
176   }
177 
178   MachineFlags::const_iterator iter = machine_flags_.find(name);
179   if (iter == machine_flags_.end()) {
180     if (base::SysInfo::IsRunningOnChromeOS() &&
181         (oem_manifest_loaded_ || !HasOemPrefix(name))) {
182       LOG(WARNING) << "Requested machine flag not found: " << name;
183     }
184     return false;
185   }
186   *result = iter->second;
187   return true;
188 }
189 
Shutdown()190 void StatisticsProviderImpl::Shutdown() {
191   cancellation_flag_.Set();  // Cancel any pending loads
192 }
193 
StatisticsProviderImpl()194 StatisticsProviderImpl::StatisticsProviderImpl()
195     : load_statistics_started_(false),
196       on_statistics_loaded_(true  /* manual_reset */,
197                             false /* initially_signaled */),
198       oem_manifest_loaded_(false) {
199 }
200 
~StatisticsProviderImpl()201 StatisticsProviderImpl::~StatisticsProviderImpl() {
202 }
203 
StartLoadingMachineStatistics(const scoped_refptr<base::TaskRunner> & file_task_runner,bool load_oem_manifest)204 void StatisticsProviderImpl::StartLoadingMachineStatistics(
205     const scoped_refptr<base::TaskRunner>& file_task_runner,
206     bool load_oem_manifest) {
207   CHECK(!load_statistics_started_);
208   load_statistics_started_ = true;
209 
210   VLOG(1) << "Started loading statistics. Load OEM Manifest: "
211           << load_oem_manifest;
212 
213   file_task_runner->PostTask(
214       FROM_HERE,
215       base::Bind(&StatisticsProviderImpl::LoadMachineStatistics,
216                  base::Unretained(this),
217                  load_oem_manifest));
218 }
219 
LoadMachineStatistics(bool load_oem_manifest)220 void StatisticsProviderImpl::LoadMachineStatistics(bool load_oem_manifest) {
221   // Run from the file task runner. StatisticsProviderImpl is a Singleton<> and
222   // will not be destroyed until after threads have been stopped, so this test
223   // is always safe.
224   if (cancellation_flag_.IsSet())
225     return;
226 
227   if (base::SysInfo::IsRunningOnChromeOS()) {
228     // Parse all of the key/value pairs from the crossystem tool.
229     NameValuePairsParser parser(&machine_info_);
230     if (!parser.ParseNameValuePairsFromTool(arraysize(kCrosSystemTool),
231                                             kCrosSystemTool,
232                                             kCrosSystemEq,
233                                             kCrosSystemDelim,
234                                             kCrosSystemCommentDelim)) {
235       LOG(ERROR) << "Errors parsing output from: " << kCrosSystemTool;
236     }
237 
238     parser.GetNameValuePairsFromFile(base::FilePath(kMachineHardwareInfoFile),
239                                      kMachineHardwareInfoEq,
240                                      kMachineHardwareInfoDelim);
241     parser.GetNameValuePairsFromFile(base::FilePath(kEchoCouponFile),
242                                      kEchoCouponEq,
243                                      kEchoCouponDelim);
244     parser.GetNameValuePairsFromFile(base::FilePath(kVpdFile),
245                                      kVpdEq,
246                                      kVpdDelim);
247   }
248 
249   // Ensure that the hardware class key is present with the expected
250   // key name, and if it couldn't be retrieved, that the value is "unknown".
251   std::string hardware_class = machine_info_[kHardwareClassCrosSystemKey];
252   if (hardware_class.empty() || hardware_class == kCrosSystemUnknownValue)
253     machine_info_[kHardwareClassKey] = kUnknownHardwareClass;
254   else
255     machine_info_[kHardwareClassKey] = hardware_class;
256 
257   if (load_oem_manifest) {
258     // If kAppOemManifestFile switch is specified, load OEM Manifest file.
259     CommandLine* command_line = CommandLine::ForCurrentProcess();
260     if (command_line->HasSwitch(switches::kAppOemManifestFile)) {
261       LoadOemManifestFromFile(
262           command_line->GetSwitchValuePath(switches::kAppOemManifestFile));
263     } else if (base::SysInfo::IsRunningOnChromeOS()) {
264       LoadOemManifestFromFile(base::FilePath(kOemManifestFilePath));
265     }
266     oem_manifest_loaded_ = true;
267   }
268 
269   // Finished loading the statistics.
270   on_statistics_loaded_.Signal();
271   VLOG(1) << "Finished loading statistics.";
272 }
273 
LoadOemManifestFromFile(const base::FilePath & file)274 void StatisticsProviderImpl::LoadOemManifestFromFile(
275     const base::FilePath& file) {
276   // Called from LoadMachineStatistics. Check cancellation_flag_ again here.
277   if (cancellation_flag_.IsSet())
278     return;
279 
280   KioskOemManifestParser::Manifest oem_manifest;
281   if (!KioskOemManifestParser::Load(file, &oem_manifest)) {
282     LOG(WARNING) << "Unable to load OEM Manifest file: " << file.value();
283     return;
284   }
285   machine_info_[kOemDeviceRequisitionKey] =
286       oem_manifest.device_requisition;
287   machine_flags_[kOemIsEnterpriseManagedKey] =
288       oem_manifest.enterprise_managed;
289   machine_flags_[kOemCanExitEnterpriseEnrollmentKey] =
290       oem_manifest.can_exit_enrollment;
291   machine_flags_[kOemKeyboardDrivenOobeKey] =
292       oem_manifest.keyboard_driven_oobe;
293 
294   VLOG(1) << "Loaded OEM Manifest statistics from " << file.value();
295 }
296 
GetInstance()297 StatisticsProviderImpl* StatisticsProviderImpl::GetInstance() {
298   return Singleton<StatisticsProviderImpl,
299                    DefaultSingletonTraits<StatisticsProviderImpl> >::get();
300 }
301 
302 static StatisticsProvider* g_test_statistics_provider = NULL;
303 
304 // static
GetInstance()305 StatisticsProvider* StatisticsProvider::GetInstance() {
306   if (g_test_statistics_provider)
307     return g_test_statistics_provider;
308   return StatisticsProviderImpl::GetInstance();
309 }
310 
311 // static
SetTestProvider(StatisticsProvider * test_provider)312 void StatisticsProvider::SetTestProvider(StatisticsProvider* test_provider) {
313   g_test_statistics_provider = test_provider;
314 }
315 
316 }  // namespace system
317 }  // namespace chromeos
318