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