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