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
16namespace chromeos {  // NOLINT
17
18namespace { // NOLINT
19
20// The filepath to the timezone file that symlinks to the actual timezone file.
21const char kTimezoneSymlink[] = "/var/lib/timezone/localtime";
22const 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"
26const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/";
27
28// The system command that returns the hardware class.
29const char kHardwareClassKey[] = "hardware_class";
30const char* kHardwareClassTool[] = { "/usr/bin/hardware_class" };
31const 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.
35const char* kMachineHardwareInfoTool[] = { "cat", "/tmp/machine-info" };
36const char kMachineHardwareInfoEq[] = "=";
37const char kMachineHardwareInfoDelim[] = " \n";
38
39// Command to get machine OS info and key/value delimiters.
40const char* kMachineOSInfoTool[] = { "cat", "/etc/lsb-release" };
41const char kMachineOSInfoEq[] = "=";
42const char kMachineOSInfoDelim[] = "\n";
43
44// Command to get  HWID and key.
45const char kHwidKey[] = "hwid";
46const char* kHwidTool[] = { "cat", "/sys/devices/platform/chromeos_acpi/HWID" };
47
48// Command to get VPD info and key/value delimiters.
49const char* kVpdTool[] = { "cat", "/var/log/vpd_2.0.txt" };
50const char kVpdEq[] = "=";
51const char kVpdDelim[] = "\n";
52
53// Fallback time zone ID used in case of an unexpected error.
54const char kFallbackTimeZoneId[] = "America/Los_Angeles";
55
56class 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
83std::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
105void 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
140const icu::TimeZone& SystemAccessImpl::GetTimezone() {
141  return *timezone_.get();
142}
143
144void 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
156bool 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
166void SystemAccessImpl::AddObserver(Observer* observer) {
167  observers_.AddObserver(observer);
168}
169
170void SystemAccessImpl::RemoveObserver(Observer* observer) {
171  observers_.RemoveObserver(observer);
172}
173
174SystemAccessImpl::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
190void 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
211SystemAccessImpl* SystemAccessImpl::GetInstance() {
212  return Singleton<SystemAccessImpl,
213                   DefaultSingletonTraits<SystemAccessImpl> >::get();
214}
215
216}  // namespace
217
218SystemAccess* SystemAccess::GetInstance() {
219  return SystemAccessImpl::GetInstance();
220}
221
222}  // namespace chromeos
223