control: Add nameOwnerChanged signal support

Subscribe to and handle nameOwnerChanged signals to trigger event
actions.

Change-Id: I7d5a472d31a2af5297581c18d84ef4ac897ff3ea
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/triggers/handlers.hpp b/control/json/triggers/handlers.hpp
index d33d00d..f3b1a23 100644
--- a/control/json/triggers/handlers.hpp
+++ b/control/json/triggers/handlers.hpp
@@ -124,6 +124,36 @@
         mgr.removeInterface(std::get<Path>(obj), std::get<Intf>(obj));
         return true;
     }
+
+    /**
+     * @brief Processes a name owner changed signal and updates the service's
+     * owner state
+     *
+     * @param[in] msg - The sdbusplus signal message
+     * @param[in] obj - Object data associated with the signal
+     * @param[in] mgr - Manager that stores the service's owner state
+     */
+    static bool nameOwnerChanged(message& msg, const SignalObject& obj,
+                                 Manager& mgr)
+    {
+        bool hasOwner = false;
+
+        std::string serv;
+        msg.read(serv);
+
+        std::string oldOwner;
+        msg.read(oldOwner);
+
+        std::string newOwner;
+        msg.read(newOwner);
+        if (!newOwner.empty())
+        {
+            hasOwner = true;
+        }
+
+        mgr.setOwner(std::get<Path>(obj), std::get<Intf>(obj), serv, hasOwner);
+        return true;
+    }
 };
 
 } // namespace phosphor::fan::control::json::trigger::signal
diff --git a/control/json/triggers/signal.cpp b/control/json/triggers/signal.cpp
index 683acc3..cd05756 100644
--- a/control/json/triggers/signal.cpp
+++ b/control/json/triggers/signal.cpp
@@ -169,6 +169,50 @@
     }
 }
 
+void nameOwnerChanged(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())
+        {
+            auto serv = Manager::getService(member, group.getInterface());
+            if (!serv.empty())
+            {
+                // Setup name owner changed signal handler on the group
+                // member's service
+                const auto match = rules::nameOwnerChanged(serv);
+                SignalPkg signalPkg = {
+                    Handlers::nameOwnerChanged,
+                    SignalObject(std::cref(member),
+                                 std::cref(group.getInterface()),
+                                 std::cref(group.getProperty())),
+                    SignalActions({action})};
+                // If signal match already exists, then the service will be the
+                // same so add action to be run
+                auto isSameSig = [](SignalPkg& pkg) { return true; };
+
+                subscribe(match, std::move(signalPkg), isSameSig, mgr);
+            }
+            else
+            {
+                // Unable to construct nameOwnerChanged match string
+                // Path and/or interface configured does not exist on dbus yet?
+                // TODO How to handle this? Create timer to keep checking for
+                // service to appear? When to stop checking?
+                log<level::ERR>(
+                    fmt::format(
+                        "Event '{}' will not be triggered by name owner "
+                        "changed signals from service of path {}, interface {}",
+                        eventName, member, group.getInterface())
+                        .c_str());
+            }
+        }
+    }
+}
+
 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 f382828..61b03e3 100644
--- a/control/json/triggers/signal.hpp
+++ b/control/json/triggers/signal.hpp
@@ -71,6 +71,16 @@
 void interfacesRemoved(Manager* mgr, const std::string& eventName,
                        std::unique_ptr<ActionBase>& action);
 
+/**
+ * @brief Subscribes to a nameOwnerChanged 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 nameOwnerChanged(Manager* mgr, const std::string& eventName,
+                      std::unique_ptr<ActionBase>& actions);
+
 // Match setup function for signals
 using SignalMatch = std::function<void(Manager*, const std::string&,
                                        std::unique_ptr<ActionBase>& action)>;
@@ -79,7 +89,8 @@
 static const std::unordered_map<std::string, SignalMatch> signals = {
     {"properties_changed", propertiesChanged},
     {"interfaces_added", interfacesAdded},
-    {"interfaces_removed", interfacesRemoved}};
+    {"interfaces_removed", interfacesRemoved},
+    {"name_owner_changed", nameOwnerChanged}};
 
 /**
  * @brief Trigger to process an event after a signal is received