1// Copyright (c) 2011 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/file_path.h"
9#include "base/file_util.h"
10#include "base/lazy_instance.h"
11#include "base/string_number_conversions.h"
12#include "base/string_tokenizer.h"
13#include "base/threading/thread_restrictions.h"
14
15#include <execinfo.h>
16
17namespace base {
18
19static const char* kLinuxStandardBaseVersionKeys[] = {
20  "CHROMEOS_RELEASE_VERSION",
21  "GOOGLE_RELEASE",
22  "DISTRIB_RELEASE",
23  NULL
24};
25
26const char kLinuxStandardBaseReleaseFile[] = "/etc/lsb-release";
27
28struct ChromeOSVersionNumbers {
29  ChromeOSVersionNumbers()
30      : major_version(0),
31        minor_version(0),
32        bugfix_version(0),
33        parsed(false) {
34  }
35
36  int32 major_version;
37  int32 minor_version;
38  int32 bugfix_version;
39  bool parsed;
40};
41
42static base::LazyInstance<ChromeOSVersionNumbers>
43    g_chrome_os_version_numbers(base::LINKER_INITIALIZED);
44
45// static
46void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
47                                            int32* minor_version,
48                                            int32* bugfix_version) {
49  if (!g_chrome_os_version_numbers.Get().parsed) {
50    // The other implementations of SysInfo don't block on the disk.
51    // See http://code.google.com/p/chromium/issues/detail?id=60394
52    // Perhaps the caller ought to cache this?
53    // Temporary allowing while we work the bug out.
54    base::ThreadRestrictions::ScopedAllowIO allow_io;
55
56    FilePath path(kLinuxStandardBaseReleaseFile);
57    std::string contents;
58    if (file_util::ReadFileToString(path, &contents)) {
59      g_chrome_os_version_numbers.Get().parsed = true;
60      ParseLsbRelease(contents,
61          &(g_chrome_os_version_numbers.Get().major_version),
62          &(g_chrome_os_version_numbers.Get().minor_version),
63          &(g_chrome_os_version_numbers.Get().bugfix_version));
64    }
65  }
66  *major_version = g_chrome_os_version_numbers.Get().major_version;
67  *minor_version = g_chrome_os_version_numbers.Get().minor_version;
68  *bugfix_version = g_chrome_os_version_numbers.Get().bugfix_version;
69}
70
71// static
72std::string SysInfo::GetLinuxStandardBaseVersionKey() {
73  return std::string(kLinuxStandardBaseVersionKeys[0]);
74}
75
76// static
77void SysInfo::ParseLsbRelease(const std::string& lsb_release,
78                              int32* major_version,
79                              int32* minor_version,
80                              int32* bugfix_version) {
81  size_t version_key_index = std::string::npos;
82  for (int i = 0; kLinuxStandardBaseVersionKeys[i] != NULL; ++i) {
83    version_key_index = lsb_release.find(kLinuxStandardBaseVersionKeys[i]);
84    if (std::string::npos != version_key_index) {
85      break;
86    }
87  }
88  if (std::string::npos == version_key_index) {
89    return;
90  }
91
92  size_t start_index = lsb_release.find_first_of('=', version_key_index);
93  start_index++;  // Move past '='.
94  size_t length = lsb_release.find_first_of('\n', start_index) - start_index;
95  std::string version = lsb_release.substr(start_index, length);
96  StringTokenizer tokenizer(version, ".");
97  // TODO(rkc): Ignore the 0. here; fix this once we move Chrome OS version
98  // numbers from the 0.xx.yyy.zz format to the xx.yyy.zz format.
99  // Refer to http://code.google.com/p/chromium-os/issues/detail?id=15789
100  for (int i = 0; i < 4 && tokenizer.GetNext(); i++) {
101    if (1 == i) {
102      StringToInt(tokenizer.token_begin(),
103                  tokenizer.token_end(),
104                  major_version);
105      *minor_version = *bugfix_version = 0;
106    } else if (2 == i) {
107      StringToInt(tokenizer.token_begin(),
108                  tokenizer.token_end(),
109                  minor_version);
110    } else {  // 3 == i
111      StringToInt(tokenizer.token_begin(),
112                  tokenizer.token_end(),
113                  bugfix_version);
114    }
115  }
116}
117
118}  // namespace base
119