Home | History | Annotate | Download | only in system
      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 
     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 
    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 
    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 
    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 
    190 void StatisticsProviderImpl::Shutdown() {
    191   cancellation_flag_.Set();  // Cancel any pending loads
    192 }
    193 
    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 
    201 StatisticsProviderImpl::~StatisticsProviderImpl() {
    202 }
    203 
    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 
    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 
    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 
    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
    305 StatisticsProvider* StatisticsProvider::GetInstance() {
    306   if (g_test_statistics_provider)
    307     return g_test_statistics_provider;
    308   return StatisticsProviderImpl::GetInstance();
    309 }
    310 
    311 // static
    312 void StatisticsProvider::SetTestProvider(StatisticsProvider* test_provider) {
    313   g_test_statistics_provider = test_provider;
    314 }
    315 
    316 }  // namespace system
    317 }  // namespace chromeos
    318