control: Add interfacesAdded signal support

Subscribe to and handle interfacesAdded signals to trigger event
actions.

Change-Id: I9d42a4341cb2880cf839b626786c569bdbf19a7c
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/triggers/handlers.hpp b/control/json/triggers/handlers.hpp
index 2a52757..102a214 100644
--- a/control/json/triggers/handlers.hpp
+++ b/control/json/triggers/handlers.hpp
@@ -50,6 +50,47 @@
                         std::get<Prop>(obj), itProp->second);
         return true;
     }
+
+    /**
+     * @brief Processes an interfaces added signal and adds the interface
+     * (including property & property value) to the manager's object cache
+     *
+     * @param[in] msg - The sdbusplus signal message
+     * @param[in] obj - Object data associated with the signal
+     * @param[in] mgr - Manager that stores the object cache
+     */
+    static bool interfacesAdded(message& msg, const SignalObject& obj,
+                                Manager& mgr)
+    {
+        sdbusplus::message::object_path op;
+        msg.read(op);
+        if (static_cast<const std::string&>(op) != std::get<Path>(obj))
+        {
+            // Path name does not match object's path
+            return false;
+        }
+
+        std::map<std::string, std::map<std::string, PropertyVariantType>>
+            intfProps;
+        msg.read(intfProps);
+        auto itIntf = intfProps.find(std::get<Intf>(obj));
+        if (itIntf == intfProps.cend())
+        {
+            // Object's interface not in dictionary of interfaces added
+            return false;
+        }
+
+        auto itProp = itIntf->second.find(std::get<Prop>(obj));
+        if (itProp == itIntf->second.cend())
+        {
+            // Object's property not in dictionary of properties of interface
+            return false;
+        }
+
+        mgr.setProperty(std::get<Path>(obj), std::get<Intf>(obj),
+                        std::get<Prop>(obj), itProp->second);
+        return true;
+    }
 };
 
 } // namespace phosphor::fan::control::json::trigger::signal
diff --git a/control/json/triggers/signal.cpp b/control/json/triggers/signal.cpp
index b5e1e6a..375ec0f 100644
--- a/control/json/triggers/signal.cpp
+++ b/control/json/triggers/signal.cpp
@@ -117,6 +117,32 @@
     }
 }
 
+void interfacesAdded(Manager* mgr, const std::string& eventName,
+                     std::unique_ptr<ActionBase>& action)
+{
+    // Groups are optional, but a signal triggered event with no groups
+    // will do nothing since signals require a group
+    for (const auto& group : action->getGroups())
+    {
+        for (const auto& member : group.getMembers())
+        {
+            // Setup interfaces added signal handler on the group member
+            const auto match = rules::interfacesAdded(member);
+            SignalPkg signalPkg = {Handlers::interfacesAdded,
+                                   SignalObject(std::cref(member),
+                                                std::cref(group.getInterface()),
+                                                std::cref(group.getProperty())),
+                                   SignalActions({action})};
+            auto isSameSig = [&intf = group.getInterface()](SignalPkg& pkg) {
+                auto& obj = std::get<SignalObject>(pkg);
+                return intf == std::get<Intf>(obj);
+            };
+
+            subscribe(match, std::move(signalPkg), isSameSig, mgr);
+        }
+    }
+}
+
 void triggerSignal(const json& jsonObj, const std::string& eventName,
                    Manager* mgr,
                    std::vector<std::unique_ptr<ActionBase>>& actions)
diff --git a/control/json/triggers/signal.hpp b/control/json/triggers/signal.hpp
index 1077144..32a9021 100644
--- a/control/json/triggers/signal.hpp
+++ b/control/json/triggers/signal.hpp
@@ -51,13 +51,24 @@
 void propertiesChanged(Manager* mgr, const std::string& eventName,
                        std::unique_ptr<ActionBase>& action);
 
+/**
+ * @brief Subscribes to an interfacesAdded signal
+ *
+ * @param[in] mgr - Pointer to manager of the trigger
+ * @param[in] eventName - Name of event associated to the signal
+ * @param[in] action - Action to be run when signal is received
+ */
+void interfacesAdded(Manager* mgr, const std::string& eventName,
+                     std::unique_ptr<ActionBase>& action);
+
 // Match setup function for signals
 using SignalMatch = std::function<void(Manager*, const std::string&,
                                        std::unique_ptr<ActionBase>& action)>;
 
 /* Supported signals to their corresponding match setup functions */
 static const std::unordered_map<std::string, SignalMatch> signals = {
-    {"properties_changed", propertiesChanged}};
+    {"properties_changed", propertiesChanged},
+    {"interfaces_added", interfacesAdded}};
 
 /**
  * @brief Trigger to process an event after a signal is received