• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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/system/sys_info.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <sys/utsname.h>
10 
11 #include "base/environment.h"
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/no_destructor.h"
16 #include "base/notreached.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_tokenizer.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/threading/thread_restrictions.h"
24 
25 namespace base {
26 
27 const char kLsbReleaseKey[] = "LSB_RELEASE";
28 const char kLsbReleaseTimeKey[] = "LSB_RELEASE_TIME";  // Seconds since epoch
29 
30 namespace {
31 
32 const char* const kLinuxStandardBaseVersionKeys[] = {
33     "CHROMEOS_RELEASE_VERSION", "GOOGLE_RELEASE", "DISTRIB_RELEASE",
34 };
35 
36 const char kChromeOsReleaseNameKey[] = "CHROMEOS_RELEASE_NAME";
37 
38 const char* const kChromeOsReleaseNames[] = {
39     "Chrome OS", "Chromium OS",
40 };
41 
42 const char kLinuxStandardBaseReleaseFile[] = "/etc/lsb-release";
43 
44 const char kLsbReleaseSourceKey[] = "lsb-release";
45 const char kLsbReleaseSourceEnv[] = "env";
46 const char kLsbReleaseSourceFile[] = "file";
47 
48 }  // namespace
49 
50 class ChromeOSVersionInfo {
51  public:
ChromeOSVersionInfo()52   ChromeOSVersionInfo() {
53     std::string lsb_release, lsb_release_time_str;
54     std::unique_ptr<Environment> env(Environment::Create());
55     bool parsed_from_env =
56         env->GetVar(kLsbReleaseKey, &lsb_release) &&
57         env->GetVar(kLsbReleaseTimeKey, &lsb_release_time_str);
58     if (parsed_from_env) {
59       double us = 0;
60       if (StringToDouble(lsb_release_time_str, &us))
61         lsb_release_time_ = Time::FromDoubleT(us);
62     } else {
63       // If the LSB_RELEASE and LSB_RELEASE_TIME environment variables are not
64       // set, fall back to a blocking read of the lsb_release file. This should
65       // only happen in non Chrome OS environments.
66       ScopedAllowBlocking allow_blocking;
67       FilePath path(kLinuxStandardBaseReleaseFile);
68       ReadFileToString(path, &lsb_release);
69       File::Info fileinfo;
70       if (GetFileInfo(path, &fileinfo))
71         lsb_release_time_ = fileinfo.creation_time;
72     }
73     ParseLsbRelease(lsb_release);
74     // For debugging:
75     lsb_release_map_[kLsbReleaseSourceKey] =
76         parsed_from_env ? kLsbReleaseSourceEnv : kLsbReleaseSourceFile;
77   }
78 
79   // The test-only instance should not parse the lsb-release file, because that
80   // file exists on the linux test bots, but contains irrelevant values.
81   enum ForTest { FOR_TEST };
ChromeOSVersionInfo(ForTest for_test)82   explicit ChromeOSVersionInfo(ForTest for_test) {}
83 
GetLsbReleaseValue(const std::string & key,std::string * value)84   bool GetLsbReleaseValue(const std::string& key, std::string* value) {
85     LsbReleaseMap::const_iterator iter = lsb_release_map_.find(key);
86     if (iter == lsb_release_map_.end())
87       return false;
88     *value = iter->second;
89     return true;
90   }
91 
GetVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)92   void GetVersionNumbers(int32_t* major_version,
93                          int32_t* minor_version,
94                          int32_t* bugfix_version) {
95     *major_version = major_version_;
96     *minor_version = minor_version_;
97     *bugfix_version = bugfix_version_;
98   }
99 
lsb_release_time() const100   const Time& lsb_release_time() const { return lsb_release_time_; }
set_lsb_release_time(const Time & time)101   void set_lsb_release_time(const Time& time) { lsb_release_time_ = time; }
102 
is_running_on_chromeos() const103   bool is_running_on_chromeos() const { return is_running_on_chromeos_; }
104 
ParseLsbRelease(const std::string & lsb_release)105   void ParseLsbRelease(const std::string& lsb_release) {
106     // Parse and cache lsb_release key pairs. There should only be a handful
107     // of entries so the overhead for this will be small, and it can be
108     // useful for debugging.
109     base::StringPairs pairs;
110     SplitStringIntoKeyValuePairs(lsb_release, '=', '\n', &pairs);
111     for (size_t i = 0; i < pairs.size(); ++i) {
112       std::string key, value;
113       TrimWhitespaceASCII(pairs[i].first, TRIM_ALL, &key);
114       TrimWhitespaceASCII(pairs[i].second, TRIM_ALL, &value);
115       if (key.empty())
116         continue;
117       lsb_release_map_[key] = value;
118     }
119     // Parse the version from the first matching recognized version key.
120     std::string version;
121     for (size_t i = 0; i < std::size(kLinuxStandardBaseVersionKeys); ++i) {
122       std::string key = kLinuxStandardBaseVersionKeys[i];
123       if (GetLsbReleaseValue(key, &version) && !version.empty())
124         break;
125     }
126     StringTokenizer tokenizer(version, ".");
127     if (tokenizer.GetNext()) {
128       StringToInt(tokenizer.token_piece(), &major_version_);
129     }
130     if (tokenizer.GetNext()) {
131       StringToInt(tokenizer.token_piece(), &minor_version_);
132     }
133     if (tokenizer.GetNext()) {
134       StringToInt(tokenizer.token_piece(), &bugfix_version_);
135     }
136 
137     // Check release name for Chrome OS.
138     std::string release_name;
139     if (GetLsbReleaseValue(kChromeOsReleaseNameKey, &release_name)) {
140       for (size_t i = 0; i < std::size(kChromeOsReleaseNames); ++i) {
141         if (release_name == kChromeOsReleaseNames[i]) {
142           is_running_on_chromeos_ = true;
143           break;
144         }
145       }
146     }
147   }
148 
149  private:
150   using LsbReleaseMap = std::map<std::string, std::string>;
151   Time lsb_release_time_;
152   LsbReleaseMap lsb_release_map_;
153   int32_t major_version_ = 0;
154   int32_t minor_version_ = 0;
155   int32_t bugfix_version_ = 0;
156   bool is_running_on_chromeos_ = false;
157 };
158 
159 ChromeOSVersionInfo* g_chromeos_version_info_for_test = nullptr;
160 
GetChromeOSVersionInfo()161 ChromeOSVersionInfo& GetChromeOSVersionInfo() {
162   // ChromeOSVersionInfo only stores the parsed lsb-release values, not the full
163   // contents of the lsb-release file. Therefore, use a second instance for
164   // overrides in tests so we can cleanly restore the original lsb-release.
165   if (g_chromeos_version_info_for_test)
166     return *g_chromeos_version_info_for_test;
167 
168   static base::NoDestructor<ChromeOSVersionInfo> version_info;
169   return *version_info;
170 }
171 
172 // static
HardwareModelName()173 std::string SysInfo::HardwareModelName() {
174   std::string board = GetLsbReleaseBoard();
175   // GetLsbReleaseBoard() may be suffixed with a "-signed-" and other extra
176   // info. Strip it.
177   const size_t index = board.find("-signed-");
178   if (index != std::string::npos)
179     board.resize(index);
180 
181   return base::ToUpperASCII(board);
182 }
183 
184 // static
OperatingSystemVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)185 void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
186                                             int32_t* minor_version,
187                                             int32_t* bugfix_version) {
188   return GetChromeOSVersionInfo().GetVersionNumbers(
189       major_version, minor_version, bugfix_version);
190 }
191 
192 // static
OperatingSystemVersion()193 std::string SysInfo::OperatingSystemVersion() {
194   int32_t major, minor, bugfix;
195   GetChromeOSVersionInfo().GetVersionNumbers(&major, &minor, &bugfix);
196   return base::StringPrintf("%d.%d.%d", major, minor, bugfix);
197 }
198 
199 // static
KernelVersion()200 std::string SysInfo::KernelVersion() {
201   struct utsname info;
202   if (uname(&info) < 0) {
203     NOTREACHED();
204     return std::string();
205   }
206   return std::string(info.release);
207 }
208 
209 // static
GetLsbReleaseValue(const std::string & key,std::string * value)210 bool SysInfo::GetLsbReleaseValue(const std::string& key, std::string* value) {
211   return GetChromeOSVersionInfo().GetLsbReleaseValue(key, value);
212 }
213 
214 // static
GetLsbReleaseBoard()215 std::string SysInfo::GetLsbReleaseBoard() {
216   const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD";
217   std::string board;
218   if (!GetLsbReleaseValue(kMachineInfoBoard, &board))
219     board = "unknown";
220   return board;
221 }
222 
223 // static
GetLsbReleaseTime()224 Time SysInfo::GetLsbReleaseTime() {
225   return GetChromeOSVersionInfo().lsb_release_time();
226 }
227 
228 // static
IsRunningOnChromeOS()229 bool SysInfo::IsRunningOnChromeOS() {
230   return GetChromeOSVersionInfo().is_running_on_chromeos();
231 }
232 
233 // static
SetChromeOSVersionInfoForTest(const std::string & lsb_release,const Time & lsb_release_time)234 void SysInfo::SetChromeOSVersionInfoForTest(const std::string& lsb_release,
235                                             const Time& lsb_release_time) {
236   DCHECK(!g_chromeos_version_info_for_test) << "Nesting is not allowed";
237   g_chromeos_version_info_for_test =
238       new ChromeOSVersionInfo(ChromeOSVersionInfo::FOR_TEST);
239   g_chromeos_version_info_for_test->ParseLsbRelease(lsb_release);
240   g_chromeos_version_info_for_test->set_lsb_release_time(lsb_release_time);
241 }
242 
243 // static
ResetChromeOSVersionInfoForTest()244 void SysInfo::ResetChromeOSVersionInfoForTest() {
245   DCHECK(g_chromeos_version_info_for_test);
246   delete g_chromeos_version_info_for_test;
247   g_chromeos_version_info_for_test = nullptr;
248 }
249 
250 // static
CrashIfChromeOSNonTestImage()251 void SysInfo::CrashIfChromeOSNonTestImage() {
252   if (!IsRunningOnChromeOS())
253     return;
254 
255   // On the test images etc/lsb-release has a line:
256   // CHROMEOS_RELEASE_TRACK=testimage-channel.
257   const char kChromeOSReleaseTrack[] = "CHROMEOS_RELEASE_TRACK";
258   const char kTestImageRelease[] = "testimage-channel";
259 
260   std::string track;
261   CHECK(SysInfo::GetLsbReleaseValue(kChromeOSReleaseTrack, &track));
262 
263   // Crash if can't find test-image marker in the release track.
264   CHECK_NE(track.find(kTestImageRelease), std::string::npos);
265 }
266 
GetHardwareInfoSync()267 SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() {
268   HardwareInfo info;
269   // Manufacturer of ChromeOS device is always Google so hardcode it.
270   info.manufacturer = "Google";
271   if (IsRunningOnChromeOS()) {
272     // Read the model name from cros-configfs.
273     constexpr char kModelNamePath[] = "/run/chromeos-config/v1/name";
274     constexpr size_t kMaxStringSize = 100u;
275     std::string data;
276     if (ReadFileToStringWithMaxSize(FilePath(kModelNamePath), &data,
277                                     kMaxStringSize)) {
278       TrimWhitespaceASCII(data, TrimPositions::TRIM_ALL, &info.model);
279     }
280     DCHECK(IsStringUTF8(info.model));
281   } else {
282     // Fake model name on chromeos linux-emulator (for both linux/ash).
283     info.model = "linux-emulator";
284   }
285   return info;
286 }
287 
288 }  // namespace base
289