| #include "config.h" |
| |
| #include "inventory_mac.hpp" |
| |
| #include "network_manager.hpp" |
| #include "types.hpp" |
| |
| #include <filesystem> |
| #include <fstream> |
| #include <memory> |
| #include <nlohmann/json.hpp> |
| #include <phosphor-logging/elog-errors.hpp> |
| #include <phosphor-logging/log.hpp> |
| #include <sdbusplus/bus.hpp> |
| #include <sdbusplus/bus/match.hpp> |
| #include <string> |
| #include <vector> |
| #include <xyz/openbmc_project/Common/error.hpp> |
| |
| namespace phosphor::network::inventory |
| { |
| |
| using phosphor::logging::elog; |
| using phosphor::logging::entry; |
| using phosphor::logging::level; |
| using phosphor::logging::log; |
| using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; |
| |
| using DbusObjectPath = std::string; |
| using DbusInterface = std::string; |
| using PropertyValue = std::string; |
| using DbusService = std::string; |
| using ObjectTree = string_umap<string_umap<std::vector<std::string>>>; |
| |
| constexpr auto firstBootPath = "/var/lib/network/firstBoot_"; |
| constexpr auto configFile = "/usr/share/network/config.json"; |
| |
| constexpr auto invNetworkIntf = |
| "xyz.openbmc_project.Inventory.Item.NetworkInterface"; |
| constexpr auto invRoot = "/xyz/openbmc_project/inventory"; |
| constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper"; |
| constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper"; |
| constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; |
| constexpr auto propIntf = "org.freedesktop.DBus.Properties"; |
| constexpr auto methodGet = "Get"; |
| |
| Manager* manager = nullptr; |
| std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr; |
| std::vector<std::string> first_boot_status; |
| |
| void setFirstBootMACOnInterface(const std::string& intf, const std::string& mac) |
| { |
| for (const auto& interface : manager->interfaces) |
| { |
| if (interface.first == intf) |
| { |
| auto returnMAC = interface.second->macAddress(mac); |
| if (returnMAC == mac) |
| { |
| log<level::INFO>(fmt::format("Setting MAC on {}", intf).c_str(), |
| entry("INTF=%s", intf.c_str()), |
| entry("MAC=%s", mac.c_str())); |
| std::error_code ec; |
| if (std::filesystem::is_directory("/var/lib/network", ec)) |
| { |
| std::ofstream persistentFile(firstBootPath + intf); |
| } |
| break; |
| } |
| else |
| { |
| log<level::INFO>("MAC is Not Set on ethernet Interface"); |
| } |
| } |
| } |
| } |
| |
| ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName) |
| { |
| std::string interfaceName = intfName; |
| |
| // load the config JSON from the Read Only Path |
| std::ifstream in(configFile); |
| nlohmann::json configJson; |
| in >> configJson; |
| interfaceName = configJson[intfName]; |
| |
| std::vector<DbusInterface> interfaces; |
| interfaces.emplace_back(invNetworkIntf); |
| |
| auto depth = 0; |
| |
| auto mapperCall = |
| bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree"); |
| |
| mapperCall.append(invRoot, depth, interfaces); |
| |
| auto mapperReply = bus.call(mapperCall); |
| if (mapperReply.is_method_error()) |
| { |
| log<level::ERR>("Error in mapper call"); |
| elog<InternalFailure>(); |
| } |
| |
| ObjectTree objectTree; |
| mapperReply.read(objectTree); |
| |
| if (objectTree.empty()) |
| { |
| log<level::ERR>("No Object has implemented the interface", |
| entry("INTERFACE=%s", invNetworkIntf)); |
| elog<InternalFailure>(); |
| } |
| |
| DbusObjectPath objPath; |
| DbusService service; |
| |
| if (1 == objectTree.size()) |
| { |
| objPath = objectTree.begin()->first; |
| service = objectTree.begin()->second.begin()->first; |
| } |
| else |
| { |
| // If there are more than 2 objects, object path must contain the |
| // interface name |
| for (auto const& object : objectTree) |
| { |
| log<level::INFO>("interface", |
| entry("INT=%s", interfaceName.c_str())); |
| log<level::INFO>("object", entry("OBJ=%s", object.first.c_str())); |
| |
| if (std::string::npos != object.first.find(interfaceName.c_str())) |
| { |
| objPath = object.first; |
| service = object.second.begin()->first; |
| break; |
| } |
| } |
| |
| if (objPath.empty()) |
| { |
| log<level::ERR>("Can't find the object for the interface", |
| entry("intfName=%s", interfaceName.c_str())); |
| elog<InternalFailure>(); |
| } |
| } |
| |
| auto method = bus.new_method_call(service.c_str(), objPath.c_str(), |
| propIntf, methodGet); |
| |
| method.append(invNetworkIntf, "MACAddress"); |
| |
| auto reply = bus.call(method); |
| if (reply.is_method_error()) |
| { |
| log<level::ERR>("Failed to get MACAddress", |
| entry("PATH=%s", objPath.c_str()), |
| entry("INTERFACE=%s", invNetworkIntf)); |
| elog<InternalFailure>(); |
| } |
| |
| std::variant<std::string> value; |
| reply.read(value); |
| return ToAddr<ether_addr>{}(std::get<std::string>(value)); |
| } |
| |
| bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, |
| const nlohmann::json& configJson, |
| const std::string& intfname) |
| { |
| try |
| { |
| auto inventoryMAC = getfromInventory(bus, intfname); |
| if (inventoryMAC != ether_addr{}) |
| { |
| auto macStr = std::to_string(inventoryMAC); |
| log<level::INFO>("Mac Address in Inventory on ", |
| entry("Interface : ", intfname.c_str()), |
| entry("MAC Address :", macStr.c_str())); |
| setFirstBootMACOnInterface(intfname, macStr); |
| first_boot_status.push_back(intfname); |
| bool status = true; |
| for (const auto& keys : configJson.items()) |
| { |
| if (!(std::find(first_boot_status.begin(), |
| first_boot_status.end(), |
| keys.key()) != first_boot_status.end())) |
| { |
| log<level::INFO>("Interface MAC is NOT set from VPD"), |
| entry("INTERFACE", keys.key().c_str()); |
| status = false; |
| } |
| } |
| if (status) |
| { |
| log<level::INFO>("Removing the match for ethernet interfaces"); |
| EthInterfaceMatch = nullptr; |
| } |
| } |
| else |
| { |
| log<level::INFO>("Nothing is present in Inventory"); |
| return false; |
| } |
| } |
| catch (const std::exception& e) |
| { |
| log<level::ERR>("Exception occurred during getting of MAC " |
| "address from Inventory"); |
| return false; |
| } |
| return true; |
| } |
| |
| // register the macthes to be monitored from inventory manager |
| void registerSignals(sdbusplus::bus_t& bus, const nlohmann::json& configJson) |
| { |
| log<level::INFO>("Registering the Inventory Signals Matcher"); |
| |
| static std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch; |
| |
| auto callback = [&](sdbusplus::message_t& m) { |
| std::map<DbusObjectPath, |
| std::map<DbusInterface, std::variant<PropertyValue>>> |
| interfacesProperties; |
| |
| sdbusplus::message::object_path objPath; |
| m.read(objPath, interfacesProperties); |
| |
| for (const auto& pattern : configJson.items()) |
| { |
| if (objPath.str.find(pattern.value()) != std::string::npos) |
| { |
| for (auto& interface : interfacesProperties) |
| { |
| if (interface.first == invNetworkIntf) |
| { |
| for (const auto& property : interface.second) |
| { |
| if (property.first == "MACAddress") |
| { |
| setFirstBootMACOnInterface( |
| pattern.key(), |
| std::get<std::string>(property.second)); |
| break; |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| }; |
| |
| MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>( |
| bus, |
| "interface='org.freedesktop.DBus.ObjectManager',type='signal'," |
| "member='InterfacesAdded',path='/xyz/openbmc_project/" |
| "inventory'", |
| callback); |
| } |
| |
| void watchEthernetInterface(sdbusplus::bus_t& bus, |
| const nlohmann::json& configJson) |
| { |
| auto mycallback = [&](sdbusplus::message_t& m) { |
| std::map<DbusObjectPath, |
| std::map<DbusInterface, std::variant<PropertyValue>>> |
| interfacesProperties; |
| |
| sdbusplus::message::object_path objPath; |
| std::pair<std::string, std::string> ethPair; |
| m.read(objPath, interfacesProperties); |
| for (const auto& interfaces : interfacesProperties) |
| { |
| if (interfaces.first == |
| "xyz.openbmc_project.Network.EthernetInterface") |
| { |
| for (const auto& property : interfaces.second) |
| { |
| if (property.first == "InterfaceName") |
| { |
| std::string infname = |
| std::get<std::string>(property.second); |
| |
| if (configJson.find(infname) == configJson.end()) |
| { |
| // ethernet interface not found in configJSON |
| // check if it is not sit0 interface, as it is |
| // expected. |
| if (infname != "sit0") |
| { |
| log<level::ERR>( |
| "Wrong Interface Name in Config Json"); |
| } |
| } |
| else |
| { |
| if (!setInventoryMACOnSystem(bus, configJson, |
| infname)) |
| { |
| registerSignals(bus, configJson); |
| EthInterfaceMatch = nullptr; |
| } |
| } |
| break; |
| } |
| } |
| break; |
| } |
| } |
| }; |
| // Incase if phosphor-inventory-manager started early and the VPD is already |
| // collected by the time network service has come up, better to check the |
| // VPD directly and set the MAC Address on the respective Interface. |
| |
| bool registeredSignals = false; |
| for (const auto& interfaceString : configJson.items()) |
| { |
| if ((FORCE_SYNC_MAC_FROM_INVENTORY || |
| !std::filesystem::exists(firstBootPath + interfaceString.key())) && |
| !registeredSignals) |
| { |
| auto msg = fmt::format("{}, check VPD for MAC", |
| (FORCE_SYNC_MAC_FROM_INVENTORY) |
| ? "Force sync enabled" |
| : "First boot file is not present"); |
| log<level::INFO>(msg.c_str()); |
| EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>( |
| bus, |
| "interface='org.freedesktop.DBus.ObjectManager',type='signal'," |
| "member='InterfacesAdded',path='/xyz/openbmc_project/network'", |
| mycallback); |
| registeredSignals = true; |
| } |
| } |
| } |
| |
| std::unique_ptr<Runtime> watch(stdplus::PinnedRef<sdbusplus::bus_t> bus, |
| stdplus::PinnedRef<Manager> m) |
| { |
| manager = &m.get(); |
| std::ifstream in(configFile); |
| nlohmann::json configJson; |
| in >> configJson; |
| watchEthernetInterface(bus, configJson); |
| return nullptr; |
| } |
| |
| } // namespace phosphor::network::inventory |