Home | History | Annotate | Download | only in chromeos
      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/chromeos/system_access.h"
      6 
      7 #include "base/file_path.h"
      8 #include "base/file_util.h"
      9 #include "base/logging.h"
     10 #include "base/observer_list.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/utf_string_conversions.h"
     14 #include "chrome/browser/chromeos/name_value_pairs_parser.h"
     15 
     16 namespace chromeos {  // NOLINT
     17 
     18 namespace { // NOLINT
     19 
     20 // The filepath to the timezone file that symlinks to the actual timezone file.
     21 const char kTimezoneSymlink[] = "/var/lib/timezone/localtime";
     22 const char kTimezoneSymlink2[] = "/var/lib/timezone/localtime2";
     23 
     24 // The directory that contains all the timezone files. So for timezone
     25 // "US/Pacific", the actual timezone file is: "/usr/share/zoneinfo/US/Pacific"
     26 const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/";
     27 
     28 // The system command that returns the hardware class.
     29 const char kHardwareClassKey[] = "hardware_class";
     30 const char* kHardwareClassTool[] = { "/usr/bin/hardware_class" };
     31 const char kUnknownHardwareClass[] = "unknown";
     32 
     33 // Command to get machine hardware info and key/value delimiters.
     34 // /tmp/machine-info is generated by platform/init/chromeos_startup.
     35 const char* kMachineHardwareInfoTool[] = { "cat", "/tmp/machine-info" };
     36 const char kMachineHardwareInfoEq[] = "=";
     37 const char kMachineHardwareInfoDelim[] = " \n";
     38 
     39 // Command to get machine OS info and key/value delimiters.
     40 const char* kMachineOSInfoTool[] = { "cat", "/etc/lsb-release" };
     41 const char kMachineOSInfoEq[] = "=";
     42 const char kMachineOSInfoDelim[] = "\n";
     43 
     44 // Command to get  HWID and key.
     45 const char kHwidKey[] = "hwid";
     46 const char* kHwidTool[] = { "cat", "/sys/devices/platform/chromeos_acpi/HWID" };
     47 
     48 // Command to get VPD info and key/value delimiters.
     49 const char* kVpdTool[] = { "cat", "/var/log/vpd_2.0.txt" };
     50 const char kVpdEq[] = "=";
     51 const char kVpdDelim[] = "\n";
     52 
     53 // Fallback time zone ID used in case of an unexpected error.
     54 const char kFallbackTimeZoneId[] = "America/Los_Angeles";
     55 
     56 class SystemAccessImpl : public SystemAccess {
     57  public:
     58   // SystemAccess.implementation:
     59   virtual const icu::TimeZone& GetTimezone();
     60   virtual void SetTimezone(const icu::TimeZone& timezone);
     61   virtual bool GetMachineStatistic(const std::string& name,
     62                                    std::string* result);
     63   virtual void AddObserver(Observer* observer);
     64   virtual void RemoveObserver(Observer* observer);
     65 
     66   static SystemAccessImpl* GetInstance();
     67 
     68  private:
     69   friend struct DefaultSingletonTraits<SystemAccessImpl>;
     70 
     71   SystemAccessImpl();
     72 
     73   // Updates the machine statistcs by examining the system.
     74   void UpdateMachineStatistics();
     75 
     76   scoped_ptr<icu::TimeZone> timezone_;
     77   ObserverList<Observer> observers_;
     78   NameValuePairsParser::NameValueMap machine_info_;
     79 
     80   DISALLOW_COPY_AND_ASSIGN(SystemAccessImpl);
     81 };
     82 
     83 std::string GetTimezoneIDAsString() {
     84   // Look at kTimezoneSymlink, see which timezone we are symlinked to.
     85   char buf[256];
     86   const ssize_t len = readlink(kTimezoneSymlink, buf,
     87                                sizeof(buf)-1);
     88   if (len == -1) {
     89     LOG(ERROR) << "GetTimezoneID: Cannot read timezone symlink "
     90                << kTimezoneSymlink;
     91     return std::string();
     92   }
     93 
     94   std::string timezone(buf, len);
     95   // Remove kTimezoneFilesDir from the beginning.
     96   if (timezone.find(kTimezoneFilesDir) != 0) {
     97     LOG(ERROR) << "GetTimezoneID: Timezone symlink is wrong "
     98                << timezone;
     99     return std::string();
    100   }
    101 
    102   return timezone.substr(strlen(kTimezoneFilesDir));
    103 }
    104 
    105 void SetTimezoneIDFromString(const std::string& id) {
    106   // Change the kTimezoneSymlink symlink to the path for this timezone.
    107   // We want to do this in an atomic way. So we are going to create the symlink
    108   // at kTimezoneSymlink2 and then move it to kTimezoneSymlink
    109 
    110   FilePath timezone_symlink(kTimezoneSymlink);
    111   FilePath timezone_symlink2(kTimezoneSymlink2);
    112   FilePath timezone_file(kTimezoneFilesDir + id);
    113 
    114   // Make sure timezone_file exists.
    115   if (!file_util::PathExists(timezone_file)) {
    116     LOG(ERROR) << "SetTimezoneID: Cannot find timezone file "
    117                << timezone_file.value();
    118     return;
    119   }
    120 
    121   // Delete old symlink2 if it exists.
    122   file_util::Delete(timezone_symlink2, false);
    123 
    124   // Create new symlink2.
    125   if (symlink(timezone_file.value().c_str(),
    126               timezone_symlink2.value().c_str()) == -1) {
    127     LOG(ERROR) << "SetTimezoneID: Unable to create symlink "
    128                << timezone_symlink2.value() << " to " << timezone_file.value();
    129     return;
    130   }
    131 
    132   // Move symlink2 to symlink.
    133   if (!file_util::ReplaceFile(timezone_symlink2, timezone_symlink)) {
    134     LOG(ERROR) << "SetTimezoneID: Unable to move symlink "
    135                << timezone_symlink2.value() << " to "
    136                << timezone_symlink.value();
    137   }
    138 }
    139 
    140 const icu::TimeZone& SystemAccessImpl::GetTimezone() {
    141   return *timezone_.get();
    142 }
    143 
    144 void SystemAccessImpl::SetTimezone(const icu::TimeZone& timezone) {
    145   timezone_.reset(timezone.clone());
    146   icu::UnicodeString unicode;
    147   timezone.getID(unicode);
    148   std::string id;
    149   UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id);
    150   VLOG(1) << "Setting timezone to " << id;
    151   chromeos::SetTimezoneIDFromString(id);
    152   icu::TimeZone::setDefault(timezone);
    153   FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(timezone));
    154 }
    155 
    156 bool SystemAccessImpl::GetMachineStatistic(
    157     const std::string& name, std::string* result) {
    158   NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name);
    159   if (iter != machine_info_.end()) {
    160     *result = iter->second;
    161     return true;
    162   }
    163   return false;
    164 }
    165 
    166 void SystemAccessImpl::AddObserver(Observer* observer) {
    167   observers_.AddObserver(observer);
    168 }
    169 
    170 void SystemAccessImpl::RemoveObserver(Observer* observer) {
    171   observers_.RemoveObserver(observer);
    172 }
    173 
    174 SystemAccessImpl::SystemAccessImpl() {
    175   // Get Statistics
    176   UpdateMachineStatistics();
    177   // Get Timezone
    178   std::string id = GetTimezoneIDAsString();
    179   if (id.empty()) {
    180     id = kFallbackTimeZoneId;
    181     LOG(ERROR) << "Got an empty string for timezone, default to " << id;
    182   }
    183   icu::TimeZone* timezone =
    184       icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id));
    185   timezone_.reset(timezone);
    186   icu::TimeZone::setDefault(*timezone);
    187   VLOG(1) << "Timezone is " << id;
    188 }
    189 
    190 void SystemAccessImpl::UpdateMachineStatistics() {
    191   NameValuePairsParser parser(&machine_info_);
    192   if (!parser.GetSingleValueFromTool(arraysize(kHardwareClassTool),
    193                                      kHardwareClassTool,
    194                                      kHardwareClassKey)) {
    195     // Use kUnknownHardwareClass if the hardware class command fails.
    196     parser.AddNameValuePair(kHardwareClassKey, kUnknownHardwareClass);
    197   }
    198   parser.ParseNameValuePairsFromTool(arraysize(kMachineHardwareInfoTool),
    199                                      kMachineHardwareInfoTool,
    200                                      kMachineHardwareInfoEq,
    201                                      kMachineHardwareInfoDelim);
    202   parser.ParseNameValuePairsFromTool(arraysize(kMachineOSInfoTool),
    203                                      kMachineOSInfoTool,
    204                                      kMachineOSInfoEq,
    205                                      kMachineOSInfoDelim);
    206   parser.GetSingleValueFromTool(arraysize(kHwidTool), kHwidTool, kHwidKey);
    207   parser.ParseNameValuePairsFromTool(
    208       arraysize(kVpdTool), kVpdTool, kVpdEq, kVpdDelim);
    209 }
    210 
    211 SystemAccessImpl* SystemAccessImpl::GetInstance() {
    212   return Singleton<SystemAccessImpl,
    213                    DefaultSingletonTraits<SystemAccessImpl> >::get();
    214 }
    215 
    216 }  // namespace
    217 
    218 SystemAccess* SystemAccess::GetInstance() {
    219   return SystemAccessImpl::GetInstance();
    220 }
    221 
    222 }  // namespace chromeos
    223