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
GetTimezoneIDAsString()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
SetTimezoneIDFromString(const std::string & id)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
GetTimezone()140 const icu::TimeZone& SystemAccessImpl::GetTimezone() {
141 return *timezone_.get();
142 }
143
SetTimezone(const icu::TimeZone & timezone)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
GetMachineStatistic(const std::string & name,std::string * result)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
AddObserver(Observer * observer)166 void SystemAccessImpl::AddObserver(Observer* observer) {
167 observers_.AddObserver(observer);
168 }
169
RemoveObserver(Observer * observer)170 void SystemAccessImpl::RemoveObserver(Observer* observer) {
171 observers_.RemoveObserver(observer);
172 }
173
SystemAccessImpl()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
UpdateMachineStatistics()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
GetInstance()211 SystemAccessImpl* SystemAccessImpl::GetInstance() {
212 return Singleton<SystemAccessImpl,
213 DefaultSingletonTraits<SystemAccessImpl> >::get();
214 }
215
216 } // namespace
217
GetInstance()218 SystemAccess* SystemAccess::GetInstance() {
219 return SystemAccessImpl::GetInstance();
220 }
221
222 } // namespace chromeos
223