blob: 7fcc02f0ab017dbf53139983eb7bad06503507cb [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "runtime_probe/function_templates/network.h"
#include <utility>
#include <base/files/file_util.h>
#include <base/json/json_writer.h>
#include <base/values.h>
#include <brillo/dbus/dbus_connection.h>
#include <chromeos/dbus/service_constants.h>
#include <shill/dbus-proxies.h>
#include "runtime_probe/utils/file_utils.h"
#include "runtime_probe/utils/value_utils.h"
namespace runtime_probe {
namespace {
constexpr auto kNetworkDirPath("/sys/class/net/");
constexpr auto kBusTypePci("pci");
constexpr auto kBusTypeSdio("sdio");
constexpr auto kBusTypeUsb("usb");
using FieldType = std::pair<std::string, std::string>;
const std::vector<FieldType> kPciFields = {{"vendor_id", "vendor"},
{"device_id", "device"}};
const std::vector<FieldType> kPciOptionalFields = {{"revision", "revision"}};
const std::vector<FieldType> kSdioFields = {{"vendor_id", "vendor"}};
const std::vector<FieldType> kSdioOptionalFields = {
{"manufacturer", "manufacturer"},
{"product", "product"},
{"bcd_device", "bcdDevice"}};
const std::vector<FieldType> kUsbFields = {{"vendor_id", "idVendor"},
{"product_id", "idProduct"}};
const std::vector<FieldType> kUsbOptionalFields = {{"bcd_device", "bcdDevice"}};
} // namespace
std::vector<brillo::VariantDictionary> NetworkFunction::GetDevicesProps(
base::Optional<std::string> type) const {
std::vector<brillo::VariantDictionary> devices_props{};
brillo::DBusConnection dbus_connection;
const auto bus = dbus_connection.Connect();
if (bus == nullptr) {
LOG(ERROR) << "Failed to connect to system D-Bus service.";
return {};
}
auto shill_proxy =
std::make_unique<org::chromium::flimflam::ManagerProxy>(bus);
brillo::VariantDictionary props;
if (!shill_proxy->GetProperties(&props, nullptr)) {
LOG(ERROR) << "Unable to get manager properties.";
return {};
}
const auto it = props.find(shill::kDevicesProperty);
if (it == props.end()) {
LOG(ERROR) << "Manager properties is missing devices.";
return {};
}
for (const auto& path : it->second.TryGet<std::vector<dbus::ObjectPath>>()) {
auto device =
std::make_unique<org::chromium::flimflam::DeviceProxy>(bus, path);
brillo::VariantDictionary device_props;
if (!device->GetProperties(&device_props, nullptr)) {
DLOG(INFO) << "Unable to get device properties of " << path.value()
<< ". Skipped.";
continue;
}
auto device_type = device_props[shill::kTypeProperty].TryGet<std::string>();
if (type == base::nullopt || device_type == type) {
devices_props.push_back(std::move(device_props));
}
}
return devices_props;
}
NetworkFunction::DataType NetworkFunction::Eval() const {
DataType result{};
auto json_output = InvokeHelperToJSON();
if (!json_output) {
LOG(ERROR)
<< "Failed to invoke helper to retrieve cached network information.";
return result;
}
const auto network_results = base::ListValue::From(std::move(json_output));
if (!network_results) {
LOG(ERROR) << "Failed to parse output from " << GetFunctionName()
<< "::EvalInHelper.";
return result;
}
for (int i = 0; i < network_results->GetSize(); i++) {
base::DictionaryValue* network_res;
if (!network_results->GetDictionary(i, &network_res)) {
DLOG(INFO) << "Unable to get result " << i << ". Skipped.";
continue;
}
result.push_back(std::move(*network_res));
}
return result;
}
int NetworkFunction::EvalInHelper(std::string* output) const {
const auto devices_props = GetDevicesProps(GetNetworkType());
base::ListValue result{};
for (const auto& device_props : devices_props) {
base::FilePath node_path(
kNetworkDirPath +
device_props.at(shill::kInterfaceProperty).TryGet<std::string>());
std::string device_type =
device_props.at(shill::kTypeProperty).TryGet<std::string>();
DLOG(INFO) << "Processing the node \"" << node_path.value() << "\".";
// Get type specific fields and their values.
auto node_res = EvalInHelperByPath(node_path);
if (node_res.empty())
continue;
// Report the absolute path we probe the reported info from.
DLOG_IF(INFO, node_res.HasKey("path"))
<< "Attribute \"path\" already existed. Overrided.";
node_res.SetString("path", node_path.value());
DLOG_IF(INFO, node_res.HasKey("type"))
<< "Attribute \"type\" already existed. Overrided.";
// Align with the category name.
if (device_type == shill::kTypeWifi) {
node_res.SetString("type", kTypeWireless);
} else {
node_res.SetString("type", device_type);
}
result.Append(node_res.CreateDeepCopy());
}
if (!base::JSONWriter::Write(result, output)) {
LOG(ERROR) << "Failed to serialize network probed result to json string.";
return -1;
}
return 0;
}
base::DictionaryValue NetworkFunction::EvalInHelperByPath(
const base::FilePath& node_path) const {
base::DictionaryValue res;
const base::FilePath dev_path = node_path.Append("device");
const base::FilePath dev_real_path = base::MakeAbsoluteFilePath(dev_path);
const base::FilePath dev_subsystem_path = dev_path.Append("subsystem");
base::FilePath dev_subsystem_link_path;
if (!base::ReadSymbolicLink(dev_subsystem_path, &dev_subsystem_link_path)) {
LOG(ERROR) << "Cannot get real path of " << dev_subsystem_path.value()
<< ".";
return {};
}
auto bus_type_idx = dev_subsystem_link_path.value().find_last_of('/') + 1;
const std::string bus_type =
dev_subsystem_link_path.value().substr(bus_type_idx);
res.SetString("bus_type", bus_type);
if (bus_type == kBusTypePci) {
auto pci_res =
MapFilesToDict(dev_real_path, kPciFields, kPciOptionalFields);
PrependToDVKey(&pci_res, std::string(kBusTypePci) + "_");
res.MergeDictionary(&pci_res);
} else if (bus_type == kBusTypeSdio) {
auto sdio_res =
MapFilesToDict(dev_real_path, kSdioFields, kSdioOptionalFields);
PrependToDVKey(&sdio_res, std::string(kBusTypeSdio) + "_");
res.MergeDictionary(&sdio_res);
} else if (bus_type == kBusTypeUsb) {
const base::FilePath usb_real_path =
base::MakeAbsoluteFilePath(dev_real_path.Append(".."));
auto usb_res =
MapFilesToDict(usb_real_path, kUsbFields, kUsbOptionalFields);
PrependToDVKey(&usb_res, std::string(kBusTypeUsb) + "_");
res.MergeDictionary(&usb_res);
}
return res;
}
} // namespace runtime_probe