platform-mc: Handle `Connectivity` propertiesChanged signal

From Mctp codeConstruct version 2.0 [1], mctpd supports `.Connectivity`
property under `au.com.CodeConstruct.MCTP.Endpoint` interface of the
endpoint. This commit handles the propertiesChanged signal from this
interface, and updates the Availability of the MCTP Endpoint accordingly
in the source to enable or disable message sending/receiving via that
endpoint of the terminus.

[1] https://github.com/CodeConstruct/mctp/blob/v2.0/docs/endpoint-recovery.md#proposed-design

When the discovery process first starts, it will only handle the
endpoints that have `Available` `.Connectivity`. It lets the
propertiesChanged signal trigger the adding of the endpoints when they
are back to` Available`.

On interfaceAdded signal, it assumes that mctpd only publishes available
endpoints to D-Bus, so it adds the endpoints to the terminus anyway.

Tested:
1. Enable `unsafe-writable-connectivity` option in PACKAGECONFIG of mctp
recipe.
2. After PLDM discovers all the endpoints, write `Degraded` to
`.Connectivity` of one of the endpoint.
3. Write it back to `Available` to see how message is stopped from being
sent/received via the endpoint.

Signed-off-by: Chau Ly <chaul@amperecomputing.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I5b7a38ae72e655b60d71396a1118f2809aaa3838
diff --git a/requester/mctp_endpoint_discovery.cpp b/requester/mctp_endpoint_discovery.cpp
index 1b6cd30..6833684 100644
--- a/requester/mctp_endpoint_discovery.cpp
+++ b/requester/mctp_endpoint_discovery.cpp
@@ -32,13 +32,27 @@
     mctpEndpointRemovedSignal(
         bus, interfacesRemoved(MCTPPath),
         std::bind_front(&MctpDiscovery::removeEndpoints, this)),
+    mctpEndpointPropChangedSignal(
+        bus, propertiesChangedNamespace(MCTPPath, MCTPInterfaceCC),
+        std::bind_front(&MctpDiscovery::propertiesChangedCb, this)),
     handlers(list)
 {
-    getMctpInfos(existingMctpInfos);
+    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(MctpInfos& mctpInfos)
+void MctpDiscovery::getMctpInfos(std::map<MctpInfo, Availability>& mctpInfoMap)
 {
     // Find all implementations of the MCTP Endpoint interface
     pldm::utils::GetSubTreeResponse mapperResponse;
@@ -63,13 +77,15 @@
             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())
             {
-                mctpInfos.emplace_back(
-                    MctpInfo(std::get<eid>(epProps), uuid, "",
-                             std::get<NetworkId>(epProps)));
+                mctpInfoMap[MctpInfo(std::get<eid>(epProps), uuid, "",
+                                     std::get<NetworkId>(epProps))] =
+                    availability;
             }
         }
     }
@@ -128,6 +144,29 @@
     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)
 {
@@ -149,6 +188,7 @@
             "ERROR", e);
         return;
     }
+    const Availability& availability = getEndpointConnectivityProp(objPath.str);
 
     /* Get UUID */
     try
@@ -176,6 +216,15 @@
                 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())
                 {
@@ -222,6 +271,75 @@
     }
 }
 
+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));
+            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;
@@ -234,7 +352,12 @@
 {
     MctpInfos mctpInfos;
     MctpInfos removedInfos;
-    getMctpInfos(mctpInfos);
+    std::map<MctpInfo, Availability> currentMctpInfoMap;
+    getMctpInfos(currentMctpInfoMap);
+    for (const auto& mapIt : currentMctpInfoMap)
+    {
+        mctpInfos.push_back(mapIt.first);
+    }
     removeFromExistingMctpInfos(mctpInfos, removedInfos);
     handleRemovedMctpEndpoints(removedInfos);
 }
@@ -261,4 +384,16 @@
     }
 }
 
+void MctpDiscovery::updateMctpEndpointAvailability(const MctpInfo& mctpInfo,
+                                                   Availability availability)
+{
+    for (const auto& handler : handlers)
+    {
+        if (handler)
+        {
+            handler->updateMctpEndpointAvailability(mctpInfo, availability);
+        }
+    }
+}
+
 } // namespace pldm