// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/system_access.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/observer_list.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/name_value_pairs_parser.h"
namespace chromeos { // NOLINT
namespace { // NOLINT
// The filepath to the timezone file that symlinks to the actual timezone file.
const char kTimezoneSymlink[] = "/var/lib/timezone/localtime";
const char kTimezoneSymlink2[] = "/var/lib/timezone/localtime2";
// The directory that contains all the timezone files. So for timezone
// "US/Pacific", the actual timezone file is: "/usr/share/zoneinfo/US/Pacific"
const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/";
// The system command that returns the hardware class.
const char kHardwareClassKey[] = "hardware_class";
const char* kHardwareClassTool[] = { "/usr/bin/hardware_class" };
const char kUnknownHardwareClass[] = "unknown";
// Command to get machine hardware info and key/value delimiters.
// /tmp/machine-info is generated by platform/init/chromeos_startup.
const char* kMachineHardwareInfoTool[] = { "cat", "/tmp/machine-info" };
const char kMachineHardwareInfoEq[] = "=";
const char kMachineHardwareInfoDelim[] = " \n";
// Command to get machine OS info and key/value delimiters.
const char* kMachineOSInfoTool[] = { "cat", "/etc/lsb-release" };
const char kMachineOSInfoEq[] = "=";
const char kMachineOSInfoDelim[] = "\n";
// Command to get HWID and key.
const char kHwidKey[] = "hwid";
const char* kHwidTool[] = { "cat", "/sys/devices/platform/chromeos_acpi/HWID" };
// Command to get VPD info and key/value delimiters.
const char* kVpdTool[] = { "cat", "/var/log/vpd_2.0.txt" };
const char kVpdEq[] = "=";
const char kVpdDelim[] = "\n";
// Fallback time zone ID used in case of an unexpected error.
const char kFallbackTimeZoneId[] = "America/Los_Angeles";
class SystemAccessImpl : public SystemAccess {
public:
// SystemAccess.implementation:
virtual const icu::TimeZone& GetTimezone();
virtual void SetTimezone(const icu::TimeZone& timezone);
virtual bool GetMachineStatistic(const std::string& name,
std::string* result);
virtual void AddObserver(Observer* observer);
virtual void RemoveObserver(Observer* observer);
static SystemAccessImpl* GetInstance();
private:
friend struct DefaultSingletonTraits<SystemAccessImpl>;
SystemAccessImpl();
// Updates the machine statistcs by examining the system.
void UpdateMachineStatistics();
scoped_ptr<icu::TimeZone> timezone_;
ObserverList<Observer> observers_;
NameValuePairsParser::NameValueMap machine_info_;
DISALLOW_COPY_AND_ASSIGN(SystemAccessImpl);
};
std::string GetTimezoneIDAsString() {
// Look at kTimezoneSymlink, see which timezone we are symlinked to.
char buf[256];
const ssize_t len = readlink(kTimezoneSymlink, buf,
sizeof(buf)-1);
if (len == -1) {
LOG(ERROR) << "GetTimezoneID: Cannot read timezone symlink "
<< kTimezoneSymlink;
return std::string();
}
std::string timezone(buf, len);
// Remove kTimezoneFilesDir from the beginning.
if (timezone.find(kTimezoneFilesDir) != 0) {
LOG(ERROR) << "GetTimezoneID: Timezone symlink is wrong "
<< timezone;
return std::string();
}
return timezone.substr(strlen(kTimezoneFilesDir));
}
void SetTimezoneIDFromString(const std::string& id) {
// Change the kTimezoneSymlink symlink to the path for this timezone.
// We want to do this in an atomic way. So we are going to create the symlink
// at kTimezoneSymlink2 and then move it to kTimezoneSymlink
FilePath timezone_symlink(kTimezoneSymlink);
FilePath timezone_symlink2(kTimezoneSymlink2);
FilePath timezone_file(kTimezoneFilesDir + id);
// Make sure timezone_file exists.
if (!file_util::PathExists(timezone_file)) {
LOG(ERROR) << "SetTimezoneID: Cannot find timezone file "
<< timezone_file.value();
return;
}
// Delete old symlink2 if it exists.
file_util::Delete(timezone_symlink2, false);
// Create new symlink2.
if (symlink(timezone_file.value().c_str(),
timezone_symlink2.value().c_str()) == -1) {
LOG(ERROR) << "SetTimezoneID: Unable to create symlink "
<< timezone_symlink2.value() << " to " << timezone_file.value();
return;
}
// Move symlink2 to symlink.
if (!file_util::ReplaceFile(timezone_symlink2, timezone_symlink)) {
LOG(ERROR) << "SetTimezoneID: Unable to move symlink "
<< timezone_symlink2.value() << " to "
<< timezone_symlink.value();
}
}
const icu::TimeZone& SystemAccessImpl::GetTimezone() {
return *timezone_.get();
}
void SystemAccessImpl::SetTimezone(const icu::TimeZone& timezone) {
timezone_.reset(timezone.clone());
icu::UnicodeString unicode;
timezone.getID(unicode);
std::string id;
UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id);
VLOG(1) << "Setting timezone to " << id;
chromeos::SetTimezoneIDFromString(id);
icu::TimeZone::setDefault(timezone);
FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(timezone));
}
bool SystemAccessImpl::GetMachineStatistic(
const std::string& name, std::string* result) {
NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name);
if (iter != machine_info_.end()) {
*result = iter->second;
return true;
}
return false;
}
void SystemAccessImpl::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void SystemAccessImpl::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
SystemAccessImpl::SystemAccessImpl() {
// Get Statistics
UpdateMachineStatistics();
// Get Timezone
std::string id = GetTimezoneIDAsString();
if (id.empty()) {
id = kFallbackTimeZoneId;
LOG(ERROR) << "Got an empty string for timezone, default to " << id;
}
icu::TimeZone* timezone =
icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id));
timezone_.reset(timezone);
icu::TimeZone::setDefault(*timezone);
VLOG(1) << "Timezone is " << id;
}
void SystemAccessImpl::UpdateMachineStatistics() {
NameValuePairsParser parser(&machine_info_);
if (!parser.GetSingleValueFromTool(arraysize(kHardwareClassTool),
kHardwareClassTool,
kHardwareClassKey)) {
// Use kUnknownHardwareClass if the hardware class command fails.
parser.AddNameValuePair(kHardwareClassKey, kUnknownHardwareClass);
}
parser.ParseNameValuePairsFromTool(arraysize(kMachineHardwareInfoTool),
kMachineHardwareInfoTool,
kMachineHardwareInfoEq,
kMachineHardwareInfoDelim);
parser.ParseNameValuePairsFromTool(arraysize(kMachineOSInfoTool),
kMachineOSInfoTool,
kMachineOSInfoEq,
kMachineOSInfoDelim);
parser.GetSingleValueFromTool(arraysize(kHwidTool), kHwidTool, kHwidKey);
parser.ParseNameValuePairsFromTool(
arraysize(kVpdTool), kVpdTool, kVpdEq, kVpdDelim);
}
SystemAccessImpl* SystemAccessImpl::GetInstance() {
return Singleton<SystemAccessImpl,
DefaultSingletonTraits<SystemAccessImpl> >::get();
}
} // namespace
SystemAccess* SystemAccess::GetInstance() {
return SystemAccessImpl::GetInstance();
}
} // namespace chromeos