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