blob: a1ba25c664b7c7dc0fe8ccf96e792647ab120584 [file]
#include "tlbmc/hal/fru_scanner_cable.h"
#include <net/if.h>
#include <cstdint>
#include <filesystem> // NOLINT
#include <fstream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "bus_topology.pb.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "absl/status/statusor.h"
#include "absl/strings/numbers.h"
#include "tlbmc/hal/fru_scanner.h"
namespace milotic_tlbmc {
namespace {
constexpr char kNetSysfsPath[] = "sys/class/net/";
// The NCSI cable is not always named "ncsi" If we want to expand the support
// for more platforms, we need to update the logic here.
constexpr char kNcsiPortName[] = "ncsi";
// Helper function to read a file and return its contents as a string
bool ReadOneLineFile(const std::filesystem::path& file_path,
std::string& content) {
std::ifstream ifs(file_path);
if (ifs.is_open() && std::getline(ifs, content)) {
ifs.close();
return true;
}
return false;
}
// Helper function for convert a hex string to uint64 type
// if string is invalid, return default val instead
uint64_t HexStringToUint64(const std::string& str, uint64_t def_val = 0) {
uint64_t val;
if (!absl::SimpleHexAtoi(str, &val)) {
return def_val;
}
return val;
}
// Helper function for convert a decimal string to uint64 type
// if string is invalid, return default val instead
uint64_t DecStringToUint64(const std::string& str, uint64_t def_val = 0) {
uint64_t val;
if (!absl::SimpleAtoi(str, &val)) {
return def_val;
}
return val;
}
// Checks if the NCSI cable is present by checking the carrier, flags, and
// rx_packets files in /sys/class/net/<port_name>/. For example, if port_name is
// "ncsi", it checks:
// /sys/class/net/ncsi/carrier - should contain "1" if carrier is present.
// /sys/class/net/ncsi/flags - should have IFF_UP bit set if interface is up.
// /sys/class/net/ncsi/statistics/rx_packets - should be > 0.
// Example sysfs structure for ncsi:
// /sys/class/net/ncsi/
// ├── carrier
// ├── flags
// └── statistics/
// └── rx_packets
bool IsNcsiCablePresent(const std::string& root_dir,
const std::string& port_name) {
std::filesystem::path carrier_path =
std::filesystem::path(root_dir) / kNetSysfsPath / port_name / "carrier";
std::filesystem::path ifflag_path =
std::filesystem::path(root_dir) / kNetSysfsPath / port_name / "flags";
std::filesystem::path rx_packets_path = std::filesystem::path(root_dir) /
kNetSysfsPath / port_name /
"statistics" / "rx_packets";
std::string flags_str;
std::string carrier_str;
std::string rx_packets_str;
if (!ReadOneLineFile(ifflag_path, flags_str) ||
!ReadOneLineFile(carrier_path, carrier_str) ||
!ReadOneLineFile(rx_packets_path, rx_packets_str)) {
return false;
}
return (HexStringToUint64(flags_str, 0) & IFF_UP) != 0 &&
(DecStringToUint64(carrier_str, 0) == 1) &&
(DecStringToUint64(rx_packets_str, 0) > 0);
}
} // namespace
std::unique_ptr<FruScannerCable> FruScannerCable::Create(
const FruScannerCable::Options& option) {
std::vector<uhmm::BmcMonitoredComponentsResponse::MonitoredComponent>
cable_components;
for (const auto& component : option.components) {
// Only NCSI components are supported by FruScannerCable as of now.
if (component.probe_path() != uhmm::BmcMonitoredComponentsResponse::
MonitoredComponent::PROBE_PATH_NCSI) {
LOG(WARNING) << "FruScannerCable::Create: Skipping non-NCSI component: "
<< component.barepath();
continue;
}
cable_components.push_back(component);
}
return absl::WrapUnique(
new FruScannerCable(std::move(cable_components), option.root_dir));
}
absl::StatusOr<std::vector<std::unique_ptr<CableFruInfo>>>
FruScannerCable::ScanAllCableFrus() const {
std::vector<std::unique_ptr<CableFruInfo>> cable_frus;
for (const auto& component : cable_components_) {
auto cable_fru = std::make_unique<CableFruInfo>();
cable_fru->expected_barepath = component.barepath();
cable_fru->present = IsNcsiCablePresent(root_dir_, kNcsiPortName);
cable_frus.push_back(std::move(cable_fru));
}
return cable_frus;
}
} // namespace milotic_tlbmc