blob: 73e573af6e18d8505f13ecdfd15e8eb8e204a179 [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"}, {"subsystem", "subsystem_device"}};
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 || device_type == type) {
devices_props.push_back(std::move(device_props));
}
}
return devices_props;
}
NetworkFunction::DataType NetworkFunction::Eval() const {
auto json_output = InvokeHelperToJSON();
if (!json_output) {
LOG(ERROR)
<< "Failed to invoke helper to retrieve cached network information.";
return {};
}
if (!json_output->is_list()) {
LOG(ERROR) << "Failed to parse output from " << GetFunctionName()
<< "::EvalInHelper.";
return {};
}
return DataType(json_output->TakeList());
}
int NetworkFunction::EvalInHelper(std::string* output) const {
const auto devices_props = GetDevicesProps(GetNetworkType());
base::Value result(base::Value::Type::LIST);
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)
continue;
// Report the absolute path we probe the reported info from.
DLOG_IF(INFO, node_res->FindStringKey("path"))
<< "Attribute \"path\" already existed. Overrided.";
node_res->SetStringKey("path", node_path.value());
DLOG_IF(INFO, node_res->FindStringKey("type"))
<< "Attribute \"type\" already existed. Overrided.";
// Align with the category name.
if (device_type == shill::kTypeWifi) {
node_res->SetStringKey("type", kTypeWireless);
} else {
node_res->SetStringKey("type", device_type);
}
result.Append(std::move(*node_res));
}
if (!base::JSONWriter::Write(result, output)) {
LOG(ERROR) << "Failed to serialize network probed result to json string.";
return -1;
}
return 0;
}
base::Optional<base::Value> NetworkFunction::EvalInHelperByPath(
const base::FilePath& node_path) const {
const auto dev_path = node_path.Append("device");
const auto 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 base::nullopt;
}
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);
const std::vector<FieldType>*fields, *optional_fields;
base::FilePath field_path;
if (bus_type == kBusTypePci) {
field_path = dev_path;
fields = &kPciFields;
optional_fields = &kPciOptionalFields;
} else if (bus_type == kBusTypeSdio) {
field_path = dev_path;
fields = &kSdioFields;
optional_fields = &kSdioOptionalFields;
} else if (bus_type == kBusTypeUsb) {
field_path = base::MakeAbsoluteFilePath(dev_path.Append(".."));
fields = &kUsbFields;
optional_fields = &kUsbOptionalFields;
} else {
LOG(ERROR) << "Unknown bus_type " << bus_type;
return base::nullopt;
}
auto res = MapFilesToDict(field_path, *fields, *optional_fields);
if (!res) {
LOG(ERROR) << "Cannot find " << bus_type << "-specific fields on network \""
<< dev_path.value() << "\"";
return base::nullopt;
}
PrependToDVKey(&*res, std::string(bus_type) + "_");
res->SetStringKey("bus_type", bus_type);
return res;
}
} // namespace runtime_probe