1// Copyright (c) 2012 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 "base/sys_info.h" 6 7#include "base/basictypes.h" 8#include "base/environment.h" 9#include "base/files/file.h" 10#include "base/files/file_path.h" 11#include "base/files/file_util.h" 12#include "base/lazy_instance.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/strings/string_piece.h" 15#include "base/strings/string_split.h" 16#include "base/strings/string_tokenizer.h" 17#include "base/strings/string_util.h" 18#include "base/threading/thread_restrictions.h" 19 20namespace base { 21 22namespace { 23 24const char* kLinuxStandardBaseVersionKeys[] = { 25 "CHROMEOS_RELEASE_VERSION", 26 "GOOGLE_RELEASE", 27 "DISTRIB_RELEASE", 28}; 29 30const char kChromeOsReleaseNameKey[] = "CHROMEOS_RELEASE_NAME"; 31 32const char* const kChromeOsReleaseNames[] = { 33 "Chrome OS", 34 "Chromium OS", 35}; 36 37const char kLinuxStandardBaseReleaseFile[] = "/etc/lsb-release"; 38 39const char kLsbReleaseKey[] = "LSB_RELEASE"; 40const char kLsbReleaseTimeKey[] = "LSB_RELEASE_TIME"; // Seconds since epoch 41 42const char kLsbReleaseSourceKey[] = "lsb-release"; 43const char kLsbReleaseSourceEnv[] = "env"; 44const char kLsbReleaseSourceFile[] = "file"; 45 46class ChromeOSVersionInfo { 47 public: 48 ChromeOSVersionInfo() { 49 Parse(); 50 } 51 52 void Parse() { 53 lsb_release_map_.clear(); 54 major_version_ = 0; 55 minor_version_ = 0; 56 bugfix_version_ = 0; 57 is_running_on_chromeos_ = false; 58 59 std::string lsb_release, lsb_release_time_str; 60 scoped_ptr<Environment> env(Environment::Create()); 61 bool parsed_from_env = 62 env->GetVar(kLsbReleaseKey, &lsb_release) && 63 env->GetVar(kLsbReleaseTimeKey, &lsb_release_time_str); 64 if (parsed_from_env) { 65 double us = 0; 66 if (StringToDouble(lsb_release_time_str, &us)) 67 lsb_release_time_ = Time::FromDoubleT(us); 68 } else { 69 // If the LSB_RELEASE and LSB_RELEASE_TIME environment variables are not 70 // set, fall back to a blocking read of the lsb_release file. This should 71 // only happen in non Chrome OS environments. 72 ThreadRestrictions::ScopedAllowIO allow_io; 73 FilePath path(kLinuxStandardBaseReleaseFile); 74 ReadFileToString(path, &lsb_release); 75 File::Info fileinfo; 76 if (GetFileInfo(path, &fileinfo)) 77 lsb_release_time_ = fileinfo.creation_time; 78 } 79 ParseLsbRelease(lsb_release); 80 // For debugging: 81 lsb_release_map_[kLsbReleaseSourceKey] = 82 parsed_from_env ? kLsbReleaseSourceEnv : kLsbReleaseSourceFile; 83 } 84 85 bool GetLsbReleaseValue(const std::string& key, std::string* value) { 86 SysInfo::LsbReleaseMap::const_iterator iter = lsb_release_map_.find(key); 87 if (iter == lsb_release_map_.end()) 88 return false; 89 *value = iter->second; 90 return true; 91 } 92 93 void GetVersionNumbers(int32* major_version, 94 int32* minor_version, 95 int32* bugfix_version) { 96 *major_version = major_version_; 97 *minor_version = minor_version_; 98 *bugfix_version = bugfix_version_; 99 } 100 101 const Time& lsb_release_time() const { return lsb_release_time_; } 102 const SysInfo::LsbReleaseMap& lsb_release_map() const { 103 return lsb_release_map_; 104 } 105 bool is_running_on_chromeos() const { return is_running_on_chromeos_; } 106 107 private: 108 void ParseLsbRelease(const std::string& lsb_release) { 109 // Parse and cache lsb_release key pairs. There should only be a handful 110 // of entries so the overhead for this will be small, and it can be 111 // useful for debugging. 112 std::vector<std::pair<std::string, std::string> > pairs; 113 SplitStringIntoKeyValuePairs(lsb_release, '=', '\n', &pairs); 114 for (size_t i = 0; i < pairs.size(); ++i) { 115 std::string key, value; 116 TrimWhitespaceASCII(pairs[i].first, TRIM_ALL, &key); 117 TrimWhitespaceASCII(pairs[i].second, TRIM_ALL, &value); 118 if (key.empty()) 119 continue; 120 lsb_release_map_[key] = value; 121 } 122 // Parse the version from the first matching recognized version key. 123 std::string version; 124 for (size_t i = 0; i < arraysize(kLinuxStandardBaseVersionKeys); ++i) { 125 std::string key = kLinuxStandardBaseVersionKeys[i]; 126 if (GetLsbReleaseValue(key, &version) && !version.empty()) 127 break; 128 } 129 StringTokenizer tokenizer(version, "."); 130 if (tokenizer.GetNext()) { 131 StringToInt(StringPiece(tokenizer.token_begin(), tokenizer.token_end()), 132 &major_version_); 133 } 134 if (tokenizer.GetNext()) { 135 StringToInt(StringPiece(tokenizer.token_begin(), tokenizer.token_end()), 136 &minor_version_); 137 } 138 if (tokenizer.GetNext()) { 139 StringToInt(StringPiece(tokenizer.token_begin(), tokenizer.token_end()), 140 &bugfix_version_); 141 } 142 143 // Check release name for Chrome OS. 144 std::string release_name; 145 if (GetLsbReleaseValue(kChromeOsReleaseNameKey, &release_name)) { 146 for (size_t i = 0; i < arraysize(kChromeOsReleaseNames); ++i) { 147 if (release_name == kChromeOsReleaseNames[i]) { 148 is_running_on_chromeos_ = true; 149 break; 150 } 151 } 152 } 153 } 154 155 Time lsb_release_time_; 156 SysInfo::LsbReleaseMap lsb_release_map_; 157 int32 major_version_; 158 int32 minor_version_; 159 int32 bugfix_version_; 160 bool is_running_on_chromeos_; 161}; 162 163static LazyInstance<ChromeOSVersionInfo> 164 g_chrome_os_version_info = LAZY_INSTANCE_INITIALIZER; 165 166ChromeOSVersionInfo& GetChromeOSVersionInfo() { 167 return g_chrome_os_version_info.Get(); 168} 169 170} // namespace 171 172// static 173void SysInfo::OperatingSystemVersionNumbers(int32* major_version, 174 int32* minor_version, 175 int32* bugfix_version) { 176 return GetChromeOSVersionInfo().GetVersionNumbers( 177 major_version, minor_version, bugfix_version); 178} 179 180// static 181const SysInfo::LsbReleaseMap& SysInfo::GetLsbReleaseMap() { 182 return GetChromeOSVersionInfo().lsb_release_map(); 183} 184 185// static 186bool SysInfo::GetLsbReleaseValue(const std::string& key, std::string* value) { 187 return GetChromeOSVersionInfo().GetLsbReleaseValue(key, value); 188} 189 190// static 191std::string SysInfo::GetLsbReleaseBoard() { 192 const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD"; 193 std::string board; 194 if (!GetLsbReleaseValue(kMachineInfoBoard, &board)) 195 board = "unknown"; 196 return board; 197} 198 199// static 200Time SysInfo::GetLsbReleaseTime() { 201 return GetChromeOSVersionInfo().lsb_release_time(); 202} 203 204// static 205bool SysInfo::IsRunningOnChromeOS() { 206 return GetChromeOSVersionInfo().is_running_on_chromeos(); 207} 208 209// static 210void SysInfo::SetChromeOSVersionInfoForTest(const std::string& lsb_release, 211 const Time& lsb_release_time) { 212 scoped_ptr<Environment> env(Environment::Create()); 213 env->SetVar(kLsbReleaseKey, lsb_release); 214 env->SetVar(kLsbReleaseTimeKey, 215 DoubleToString(lsb_release_time.ToDoubleT())); 216 g_chrome_os_version_info.Get().Parse(); 217} 218 219} // namespace base 220