1 // Copyright 2022 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 "components/metrics/motherboard.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/strings/string_util.h"
13 #include "build/build_config.h"
14 #include "third_party/abseil-cpp/absl/types/optional.h"
15
16 #if BUILDFLAG(IS_WIN)
17 #include <windows.h>
18
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/scoped_blocking_call.h"
21 #include "base/win/scoped_bstr.h"
22 #include "base/win/scoped_variant.h"
23 #include "base/win/wmi.h"
24 #endif
25
26 namespace metrics {
27 namespace {
28
29 struct MotherboardDetails {
30 absl::optional<std::string> manufacturer;
31 absl::optional<std::string> model;
32 absl::optional<std::string> bios_manufacturer;
33 absl::optional<std::string> bios_version;
34 absl::optional<Motherboard::BiosType> bios_type;
35 };
36
37 #if BUILDFLAG(IS_LINUX)
38 using base::FilePath;
39 using base::PathExists;
40 using base::ReadFileToString;
41 using base::TrimWhitespaceASCII;
42 using base::TRIM_TRAILING;
43
ReadMotherboardDetails()44 MotherboardDetails ReadMotherboardDetails() {
45 constexpr FilePath::CharType kDmiPath[] = "/sys/devices/virtual/dmi/id";
46 constexpr FilePath::CharType kEfiPath[] = "/sys/firmware/efi";
47 const FilePath dmi_path(kDmiPath);
48 MotherboardDetails details;
49 std::string temp;
50 if (ReadFileToString(dmi_path.Append("board_vendor"), &temp)) {
51 details.manufacturer =
52 std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
53 }
54 if (ReadFileToString(dmi_path.Append("board_name"), &temp)) {
55 details.model = std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
56 }
57 if (ReadFileToString(dmi_path.Append("bios_vendor"), &temp)) {
58 details.bios_manufacturer =
59 std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
60 }
61 if (ReadFileToString(dmi_path.Append("bios_version"), &temp)) {
62 details.bios_version =
63 std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
64 }
65 if (PathExists(FilePath(kEfiPath))) {
66 details.bios_type = Motherboard::BiosType::kUefi;
67 } else {
68 details.bios_type = Motherboard::BiosType::kLegacy;
69 }
70 return details;
71 }
72 #endif
73
74 #if BUILDFLAG(IS_WIN)
75 using Microsoft::WRL::ComPtr;
76 using base::win::ScopedBstr;
77 using base::win::ScopedVariant;
78
ReadStringMember(ComPtr<IWbemClassObject> class_object,const wchar_t * key)79 absl::optional<std::string> ReadStringMember(
80 ComPtr<IWbemClassObject> class_object, const wchar_t* key) {
81 ScopedVariant variant;
82 HRESULT hr = class_object->Get(key, 0, variant.Receive(), 0, 0);
83 if (SUCCEEDED(hr) && variant.type() == VT_BSTR) {
84 const auto len = ::SysStringLen(V_BSTR(variant.ptr()));
85 std::wstring wstr(V_BSTR(variant.ptr()), len);
86 return base::WideToUTF8(wstr);
87 }
88 return {};
89 }
90
ReadWin32BaseBoard(const ComPtr<IWbemServices> & services,absl::optional<std::string> * manufacturer,absl::optional<std::string> * model)91 void ReadWin32BaseBoard(const ComPtr<IWbemServices>& services,
92 absl::optional<std::string>* manufacturer,
93 absl::optional<std::string>* model) {
94 static constexpr wchar_t kManufacturer[] = L"Manufacturer";
95 static constexpr wchar_t kProduct[] = L"Product";
96 static constexpr wchar_t kQueryProcessor[] =
97 L"SELECT Manufacturer,Product FROM Win32_BaseBoard";
98
99 ComPtr<IEnumWbemClassObject> enumerator_base_board;
100 HRESULT hr = services->ExecQuery(
101 ScopedBstr(L"WQL").Get(), ScopedBstr(kQueryProcessor).Get(),
102 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
103 &enumerator_base_board);
104 if (FAILED(hr) || !enumerator_base_board.Get())
105 return;
106
107 ComPtr<IWbemClassObject> class_object;
108 ULONG items_returned = 0;
109 hr = enumerator_base_board->Next(WBEM_INFINITE, 1, &class_object,
110 &items_returned);
111 if (FAILED(hr) || !items_returned)
112 return;
113 *manufacturer = ReadStringMember(class_object, kManufacturer);
114 *model = ReadStringMember(class_object, kProduct);
115 }
116
ReadWin32Bios(const ComPtr<IWbemServices> & services,absl::optional<std::string> * bios_manufacturer,absl::optional<std::string> * bios_version)117 void ReadWin32Bios(const ComPtr<IWbemServices>& services,
118 absl::optional<std::string>* bios_manufacturer,
119 absl::optional<std::string>* bios_version) {
120 static constexpr wchar_t kManufacturer[] = L"Manufacturer";
121 static constexpr wchar_t kVersion[] = L"Version";
122 static constexpr wchar_t kQueryProcessor[] =
123 L"SELECT Manufacturer,Version FROM Win32_BIOS";
124
125 ComPtr<IEnumWbemClassObject> enumerator_base_board;
126 HRESULT hr = services->ExecQuery(
127 ScopedBstr(L"WQL").Get(), ScopedBstr(kQueryProcessor).Get(),
128 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
129 &enumerator_base_board);
130 if (FAILED(hr) || !enumerator_base_board.Get())
131 return;
132
133 ComPtr<IWbemClassObject> class_object;
134 ULONG items_returned = 0;
135 hr = enumerator_base_board->Next(WBEM_INFINITE, 1, &class_object,
136 &items_returned);
137 if (FAILED(hr) || !items_returned)
138 return;
139 *bios_manufacturer = ReadStringMember(class_object, kManufacturer);
140 *bios_version = ReadStringMember(class_object, kVersion);
141 }
142
ReadFirmwareType(absl::optional<Motherboard::BiosType> * bios_type)143 void ReadFirmwareType(absl::optional<Motherboard::BiosType>* bios_type) {
144 FIRMWARE_TYPE firmware_type = FirmwareTypeUnknown;
145 if (::GetFirmwareType(&firmware_type)) {
146 if (firmware_type == FirmwareTypeBios) {
147 *bios_type = Motherboard::BiosType::kLegacy;
148 } else if (firmware_type == FirmwareTypeUefi) {
149 *bios_type = Motherboard::BiosType::kUefi;
150 } else {
151 *bios_type = absl::nullopt;
152 }
153 }
154 }
155
ReadMotherboardDetails()156 MotherboardDetails ReadMotherboardDetails() {
157 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
158 base::BlockingType::MAY_BLOCK);
159 ComPtr<IWbemServices> services;
160 MotherboardDetails details;
161 if (!base::win::CreateLocalWmiConnection(true, &services))
162 return details;
163 ReadWin32BaseBoard(services, &details.manufacturer, &details.model);
164 ReadWin32Bios(services, &details.bios_manufacturer, &details.bios_version);
165 ReadFirmwareType(&details.bios_type);
166 return details;
167 }
168 #endif
169 } // namespace
170
171 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
Motherboard()172 Motherboard::Motherboard() {
173 const auto details = ReadMotherboardDetails();
174 manufacturer_ = std::move(details.manufacturer),
175 model_ = std::move(details.model),
176 bios_manufacturer_ = std::move(details.bios_manufacturer),
177 bios_version_ = std::move(details.bios_version),
178 bios_type_ = std::move(details.bios_type);
179 }
180 #else
181 Motherboard::Motherboard() = default;
182 #endif
183
184 Motherboard::~Motherboard() = default;
185
186 } // namespace metrics
187