• 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 #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