| #include "config.h" | 
 |  | 
 | #include "mctp_endpoint_discovery.hpp" | 
 |  | 
 | #include "common/types.hpp" | 
 | #include "common/utils.hpp" | 
 |  | 
 | #include <linux/mctp.h> | 
 |  | 
 | #include <phosphor-logging/lg2.hpp> | 
 |  | 
 | #include <algorithm> | 
 | #include <fstream> | 
 | #include <iostream> | 
 | #include <map> | 
 | #include <string> | 
 | #include <string_view> | 
 | #include <vector> | 
 |  | 
 | using namespace sdbusplus::bus::match::rules; | 
 |  | 
 | PHOSPHOR_LOG2_USING; | 
 |  | 
 | namespace pldm | 
 | { | 
 | MctpDiscovery::MctpDiscovery( | 
 |     sdbusplus::bus_t& bus, | 
 |     std::initializer_list<MctpDiscoveryHandlerIntf*> list) : | 
 |     bus(bus), | 
 |     mctpEndpointAddedSignal( | 
 |         bus, interfacesAdded(MCTPPath), | 
 |         [this](sdbusplus::message_t& msg) { this->discoverEndpoints(msg); }), | 
 |     mctpEndpointRemovedSignal( | 
 |         bus, interfacesRemoved(MCTPPath), | 
 |         [this](sdbusplus::message_t& msg) { this->removeEndpoints(msg); }), | 
 |     mctpEndpointPropChangedSignal( | 
 |         bus, propertiesChangedNamespace(MCTPPath, MCTPInterfaceCC), | 
 |         [this](sdbusplus::message_t& msg) { this->propertiesChangedCb(msg); }), | 
 |     handlers(list) | 
 | { | 
 |     std::map<MctpInfo, Availability> currentMctpInfoMap; | 
 |     getMctpInfos(currentMctpInfoMap); | 
 |     for (const auto& mapIt : currentMctpInfoMap) | 
 |     { | 
 |         if (mapIt.second) | 
 |         { | 
 |             // Only add the available endpoints to the terminus | 
 |             // Let the propertiesChanged signal tells us when it comes back | 
 |             // to Available again | 
 |             addToExistingMctpInfos(MctpInfos(1, mapIt.first)); | 
 |         } | 
 |     } | 
 |     handleMctpEndpoints(existingMctpInfos); | 
 | } | 
 |  | 
 | void MctpDiscovery::getMctpInfos(std::map<MctpInfo, Availability>& mctpInfoMap) | 
 | { | 
 |     // Find all implementations of the MCTP Endpoint interface | 
 |     pldm::utils::GetSubTreeResponse mapperResponse; | 
 |     try | 
 |     { | 
 |         mapperResponse = pldm::utils::DBusHandler().getSubtree( | 
 |             MCTPPath, 0, std::vector<std::string>({MCTPInterface})); | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         error( | 
 |             "Failed to getSubtree call at path '{PATH}' and interface '{INTERFACE}', error - {ERROR} ", | 
 |             "ERROR", e, "PATH", MCTPPath, "INTERFACE", MCTPInterface); | 
 |         return; | 
 |     } | 
 |  | 
 |     for (const auto& [path, services] : mapperResponse) | 
 |     { | 
 |         for (const auto& serviceIter : services) | 
 |         { | 
 |             const std::string& service = serviceIter.first; | 
 |             const MctpEndpointProps& epProps = | 
 |                 getMctpEndpointProps(service, path); | 
 |             const UUID& uuid = getEndpointUUIDProp(service, path); | 
 |             const Availability& availability = | 
 |                 getEndpointConnectivityProp(path); | 
 |             auto types = std::get<MCTPMsgTypes>(epProps); | 
 |             if (std::find(types.begin(), types.end(), mctpTypePLDM) != | 
 |                 types.end()) | 
 |             { | 
 |                 auto mctpInfo = | 
 |                     MctpInfo(std::get<eid>(epProps), uuid, "", | 
 |                              std::get<NetworkId>(epProps), std::nullopt); | 
 |                 searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo); | 
 |                 mctpInfoMap[std::move(mctpInfo)] = availability; | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | MctpEndpointProps MctpDiscovery::getMctpEndpointProps( | 
 |     const std::string& service, const std::string& path) | 
 | { | 
 |     try | 
 |     { | 
 |         auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant( | 
 |             service.c_str(), path.c_str(), MCTPInterface); | 
 |  | 
 |         if (properties.contains("NetworkId") && properties.contains("EID") && | 
 |             properties.contains("SupportedMessageTypes")) | 
 |         { | 
 |             auto networkId = std::get<NetworkId>(properties.at("NetworkId")); | 
 |             auto eid = std::get<mctp_eid_t>(properties.at("EID")); | 
 |             auto types = std::get<std::vector<uint8_t>>( | 
 |                 properties.at("SupportedMessageTypes")); | 
 |             return MctpEndpointProps(networkId, eid, types); | 
 |         } | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         error( | 
 |             "Error reading MCTP Endpoint property at path '{PATH}' and service '{SERVICE}', error - {ERROR}", | 
 |             "SERVICE", service, "PATH", path, "ERROR", e); | 
 |         return MctpEndpointProps(0, MCTP_ADDR_ANY, {}); | 
 |     } | 
 |  | 
 |     return MctpEndpointProps(0, MCTP_ADDR_ANY, {}); | 
 | } | 
 |  | 
 | UUID MctpDiscovery::getEndpointUUIDProp(const std::string& service, | 
 |                                         const std::string& path) | 
 | { | 
 |     try | 
 |     { | 
 |         auto properties = pldm::utils::DBusHandler().getDbusPropertiesVariant( | 
 |             service.c_str(), path.c_str(), EndpointUUID); | 
 |  | 
 |         if (properties.contains("UUID")) | 
 |         { | 
 |             return std::get<UUID>(properties.at("UUID")); | 
 |         } | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         error( | 
 |             "Error reading Endpoint UUID property at path '{PATH}' and service '{SERVICE}', error - {ERROR}", | 
 |             "SERVICE", service, "PATH", path, "ERROR", e); | 
 |         return static_cast<UUID>(emptyUUID); | 
 |     } | 
 |  | 
 |     return static_cast<UUID>(emptyUUID); | 
 | } | 
 |  | 
 | Availability MctpDiscovery::getEndpointConnectivityProp(const std::string& path) | 
 | { | 
 |     Availability available = false; | 
 |     try | 
 |     { | 
 |         pldm::utils::PropertyValue propertyValue = | 
 |             pldm::utils::DBusHandler().getDbusPropertyVariant( | 
 |                 path.c_str(), MCTPConnectivityProp, MCTPInterfaceCC); | 
 |         if (std::get<std::string>(propertyValue) == "Available") | 
 |         { | 
 |             available = true; | 
 |         } | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         error( | 
 |             "Error reading Endpoint Connectivity property at path '{PATH}', error - {ERROR}", | 
 |             "PATH", path, "ERROR", e); | 
 |     } | 
 |  | 
 |     return available; | 
 | } | 
 |  | 
 | void MctpDiscovery::getAddedMctpInfos(sdbusplus::message_t& msg, | 
 |                                       MctpInfos& mctpInfos) | 
 | { | 
 |     using ObjectPath = sdbusplus::message::object_path; | 
 |     ObjectPath objPath; | 
 |     using Property = std::string; | 
 |     using PropertyMap = std::map<Property, dbus::Value>; | 
 |     std::map<std::string, PropertyMap> interfaces; | 
 |     std::string uuid = emptyUUID; | 
 |  | 
 |     try | 
 |     { | 
 |         msg.read(objPath, interfaces); | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         error( | 
 |             "Error reading MCTP Endpoint added interface message, error - {ERROR}", | 
 |             "ERROR", e); | 
 |         return; | 
 |     } | 
 |     const Availability& availability = getEndpointConnectivityProp(objPath.str); | 
 |  | 
 |     /* Get UUID */ | 
 |     try | 
 |     { | 
 |         auto service = pldm::utils::DBusHandler().getService( | 
 |             objPath.str.c_str(), EndpointUUID); | 
 |         uuid = getEndpointUUIDProp(service, objPath.str); | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         error("Error getting Endpoint UUID D-Bus interface, error - {ERROR}", | 
 |               "ERROR", e); | 
 |     } | 
 |  | 
 |     for (const auto& [intfName, properties] : interfaces) | 
 |     { | 
 |         if (intfName == MCTPInterface) | 
 |         { | 
 |             if (properties.contains("NetworkId") && | 
 |                 properties.contains("EID") && | 
 |                 properties.contains("SupportedMessageTypes")) | 
 |             { | 
 |                 auto networkId = | 
 |                     std::get<NetworkId>(properties.at("NetworkId")); | 
 |                 auto eid = std::get<mctp_eid_t>(properties.at("EID")); | 
 |                 auto types = std::get<std::vector<uint8_t>>( | 
 |                     properties.at("SupportedMessageTypes")); | 
 |  | 
 |                 if (!availability) | 
 |                 { | 
 |                     // Log an error message here, but still add it to the | 
 |                     // terminus | 
 |                     error( | 
 |                         "mctpd added a DEGRADED endpoint {EID} networkId {NET} to D-Bus", | 
 |                         "NET", networkId, "EID", static_cast<unsigned>(eid)); | 
 |                 } | 
 |                 if (std::find(types.begin(), types.end(), mctpTypePLDM) != | 
 |                     types.end()) | 
 |                 { | 
 |                     info( | 
 |                         "Adding Endpoint networkId '{NETWORK}' and EID '{EID}' UUID '{UUID}'", | 
 |                         "NETWORK", networkId, "EID", eid, "UUID", uuid); | 
 |                     auto mctpInfo = | 
 |                         MctpInfo(eid, uuid, "", networkId, std::nullopt); | 
 |                     searchConfigurationFor(pldm::utils::DBusHandler(), | 
 |                                            mctpInfo); | 
 |                     mctpInfos.emplace_back(std::move(mctpInfo)); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void MctpDiscovery::addToExistingMctpInfos(const MctpInfos& addedInfos) | 
 | { | 
 |     for (const auto& mctpInfo : addedInfos) | 
 |     { | 
 |         if (std::find(existingMctpInfos.begin(), existingMctpInfos.end(), | 
 |                       mctpInfo) == existingMctpInfos.end()) | 
 |         { | 
 |             existingMctpInfos.emplace_back(mctpInfo); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void MctpDiscovery::removeFromExistingMctpInfos(MctpInfos& mctpInfos, | 
 |                                                 MctpInfos& removedInfos) | 
 | { | 
 |     for (const auto& mctpInfo : existingMctpInfos) | 
 |     { | 
 |         if (std::find(mctpInfos.begin(), mctpInfos.end(), mctpInfo) == | 
 |             mctpInfos.end()) | 
 |         { | 
 |             removedInfos.emplace_back(mctpInfo); | 
 |         } | 
 |     } | 
 |     for (const auto& mctpInfo : removedInfos) | 
 |     { | 
 |         info("Removing Endpoint networkId '{NETWORK}' and  EID '{EID}'", | 
 |              "NETWORK", std::get<3>(mctpInfo), "EID", std::get<0>(mctpInfo)); | 
 |         existingMctpInfos.erase(std::remove(existingMctpInfos.begin(), | 
 |                                             existingMctpInfos.end(), mctpInfo), | 
 |                                 existingMctpInfos.end()); | 
 |     } | 
 | } | 
 |  | 
 | void MctpDiscovery::propertiesChangedCb(sdbusplus::message_t& msg) | 
 | { | 
 |     using Interface = std::string; | 
 |     using Property = std::string; | 
 |     using Value = std::string; | 
 |     using Properties = std::map<Property, std::variant<Value>>; | 
 |  | 
 |     Interface interface; | 
 |     Properties properties; | 
 |     std::string objPath{}; | 
 |     std::string service{}; | 
 |  | 
 |     try | 
 |     { | 
 |         msg.read(interface, properties); | 
 |         objPath = msg.get_path(); | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         error( | 
 |             "Error handling Connectivity property changed message, error - {ERROR}", | 
 |             "ERROR", e); | 
 |         return; | 
 |     } | 
 |  | 
 |     for (const auto& [key, valueVariant] : properties) | 
 |     { | 
 |         Value propVal = std::get<std::string>(valueVariant); | 
 |         auto availability = (propVal == "Available") ? true : false; | 
 |  | 
 |         if (key == MCTPConnectivityProp) | 
 |         { | 
 |             service = pldm::utils::DBusHandler().getService(objPath.c_str(), | 
 |                                                             MCTPInterface); | 
 |             const MctpEndpointProps& epProps = | 
 |                 getMctpEndpointProps(service, objPath); | 
 |  | 
 |             auto types = std::get<MCTPMsgTypes>(epProps); | 
 |             if (!std::ranges::contains(types, mctpTypePLDM)) | 
 |             { | 
 |                 return; | 
 |             } | 
 |             const UUID& uuid = getEndpointUUIDProp(service, objPath); | 
 |  | 
 |             MctpInfo mctpInfo(std::get<eid>(epProps), uuid, "", | 
 |                               std::get<NetworkId>(epProps), std::nullopt); | 
 |             searchConfigurationFor(pldm::utils::DBusHandler(), mctpInfo); | 
 |             if (!std::ranges::contains(existingMctpInfos, mctpInfo)) | 
 |             { | 
 |                 if (availability) | 
 |                 { | 
 |                     // The endpoint not in existingMctpInfos and is | 
 |                     // available Add it to existingMctpInfos | 
 |                     info( | 
 |                         "Adding Endpoint networkId {NETWORK} ID {EID} by propertiesChanged signal", | 
 |                         "NETWORK", std::get<3>(mctpInfo), "EID", | 
 |                         unsigned(std::get<0>(mctpInfo))); | 
 |                     addToExistingMctpInfos(MctpInfos(1, mctpInfo)); | 
 |                     handleMctpEndpoints(MctpInfos(1, mctpInfo)); | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 // The endpoint already in existingMctpInfos | 
 |                 updateMctpEndpointAvailability(mctpInfo, availability); | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void MctpDiscovery::discoverEndpoints(sdbusplus::message_t& msg) | 
 | { | 
 |     MctpInfos addedInfos; | 
 |     getAddedMctpInfos(msg, addedInfos); | 
 |     addToExistingMctpInfos(addedInfos); | 
 |     handleMctpEndpoints(addedInfos); | 
 | } | 
 |  | 
 | void MctpDiscovery::removeEndpoints(sdbusplus::message_t&) | 
 | { | 
 |     MctpInfos mctpInfos; | 
 |     MctpInfos removedInfos; | 
 |     std::map<MctpInfo, Availability> currentMctpInfoMap; | 
 |     getMctpInfos(currentMctpInfoMap); | 
 |     for (const auto& mapIt : currentMctpInfoMap) | 
 |     { | 
 |         mctpInfos.push_back(mapIt.first); | 
 |     } | 
 |     removeFromExistingMctpInfos(mctpInfos, removedInfos); | 
 |     handleRemovedMctpEndpoints(removedInfos); | 
 |     removeConfigs(removedInfos); | 
 | } | 
 |  | 
 | void MctpDiscovery::handleMctpEndpoints(const MctpInfos& mctpInfos) | 
 | { | 
 |     for (const auto& handler : handlers) | 
 |     { | 
 |         if (handler) | 
 |         { | 
 |             handler->handleConfigurations(configurations); | 
 |             handler->handleMctpEndpoints(mctpInfos); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void MctpDiscovery::handleRemovedMctpEndpoints(const MctpInfos& mctpInfos) | 
 | { | 
 |     for (const auto& handler : handlers) | 
 |     { | 
 |         if (handler) | 
 |         { | 
 |             handler->handleRemovedMctpEndpoints(mctpInfos); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void MctpDiscovery::updateMctpEndpointAvailability(const MctpInfo& mctpInfo, | 
 |                                                    Availability availability) | 
 | { | 
 |     for (const auto& handler : handlers) | 
 |     { | 
 |         if (handler) | 
 |         { | 
 |             handler->updateMctpEndpointAvailability(mctpInfo, availability); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | std::string MctpDiscovery::getNameFromProperties( | 
 |     const utils::PropertyMap& properties) | 
 | { | 
 |     if (!properties.contains("Name")) | 
 |     { | 
 |         error("Missing name property"); | 
 |         return ""; | 
 |     } | 
 |     return std::get<std::string>(properties.at("Name")); | 
 | } | 
 |  | 
 | std::string MctpDiscovery::constructMctpReactorObjectPath( | 
 |     const MctpInfo& mctpInfo) | 
 | { | 
 |     const auto networkId = std::get<NetworkId>(mctpInfo); | 
 |     const auto eid = std::get<pldm::eid>(mctpInfo); | 
 |     return std::string{MCTPPath} + "/networks/" + std::to_string(networkId) + | 
 |            "/endpoints/" + std::to_string(eid) + "/configured_by"; | 
 | } | 
 |  | 
 | void MctpDiscovery::searchConfigurationFor( | 
 |     const pldm::utils::DBusHandler& handler, MctpInfo& mctpInfo) | 
 | { | 
 |     const auto mctpReactorObjectPath = constructMctpReactorObjectPath(mctpInfo); | 
 |     try | 
 |     { | 
 |         std::string associatedObjPath; | 
 |         std::string associatedService; | 
 |         std::string associatedInterface; | 
 |         sdbusplus::message::object_path inventorySubtreePath( | 
 |             inventorySubtreePathStr); | 
 |  | 
 |         //"/{board or chassis type}/{board or chassis}/{device}" | 
 |         auto constexpr subTreeDepth = 3; | 
 |         auto response = handler.getAssociatedSubTree( | 
 |             mctpReactorObjectPath, inventorySubtreePath, subTreeDepth, | 
 |             interfaceFilter); | 
 |         if (response.empty()) | 
 |         { | 
 |             warning("No associated subtree found for path {PATH}", "PATH", | 
 |                     mctpReactorObjectPath); | 
 |             return; | 
 |         } | 
 |         // Assume the first entry is the one we want | 
 |         auto subTree = response.begin(); | 
 |         associatedObjPath = subTree->first; | 
 |         auto associatedServiceProp = subTree->second; | 
 |         if (associatedServiceProp.empty()) | 
 |         { | 
 |             warning("No associated service found for path {PATH}", "PATH", | 
 |                     mctpReactorObjectPath); | 
 |             return; | 
 |         } | 
 |         // Assume the first entry is the one we want | 
 |         auto entry = associatedServiceProp.begin(); | 
 |         associatedService = entry->first; | 
 |         auto dBusIntfList = entry->second; | 
 |         auto associatedInterfaceItr = std::find_if( | 
 |             dBusIntfList.begin(), dBusIntfList.end(), [](const auto& intf) { | 
 |                 return std::find(interfaceFilter.begin(), interfaceFilter.end(), | 
 |                                  intf) != interfaceFilter.end(); | 
 |             }); | 
 |         if (associatedInterfaceItr == dBusIntfList.end()) | 
 |         { | 
 |             error("No associated interface found for path {PATH}", "PATH", | 
 |                   mctpReactorObjectPath); | 
 |             return; | 
 |         } | 
 |         associatedInterface = *associatedInterfaceItr; | 
 |         auto mctpTargetProperties = handler.getDbusPropertiesVariant( | 
 |             associatedService.c_str(), associatedObjPath.c_str(), | 
 |             associatedInterface.c_str()); | 
 |         auto name = getNameFromProperties(mctpTargetProperties); | 
 |         if (!name.empty()) | 
 |         { | 
 |             std::get<std::optional<std::string>>(mctpInfo) = name; | 
 |         } | 
 |         configurations.emplace(associatedObjPath, mctpInfo); | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         error( | 
 |             "Error getting associated subtree for path {PATH}, error - {ERROR}", | 
 |             "PATH", mctpReactorObjectPath, "ERROR", e); | 
 |         return; | 
 |     } | 
 | } | 
 |  | 
 | void MctpDiscovery::removeConfigs(const MctpInfos& removedInfos) | 
 | { | 
 |     for (const auto& mctpInfo : removedInfos) | 
 |     { | 
 |         auto eidToRemove = std::get<eid>(mctpInfo); | 
 |         std::erase_if(configurations, [eidToRemove](const auto& config) { | 
 |             auto& [__, mctpInfo] = config; | 
 |             auto eidValue = std::get<eid>(mctpInfo); | 
 |             return eidValue == eidToRemove; | 
 |         }); | 
 |     } | 
 | } | 
 |  | 
 | } // namespace pldm |