control: Handle updating service states

Add the necessary functions to handle updating the cached services tree
with the service names and owner states for any give path and interface.
A future commit is going to shift the manager's service handling
functions away from being static once all the required objects get an
ability to retrieve their associated manager object.

Change-Id: I6d1c80c3eeaffa6c38d527680454fd98432e8e03
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/manager.cpp b/control/json/manager.cpp
index b0dc6e3..ec15d18 100644
--- a/control/json/manager.cpp
+++ b/control/json/manager.cpp
@@ -47,7 +47,7 @@
 
 std::vector<std::string> Manager::_activeProfiles;
 std::map<std::string,
-         std::map<std::pair<std::string, bool>, std::vector<std::string>>>
+         std::map<std::string, std::pair<bool, std::vector<std::string>>>>
     Manager::_servTree;
 std::map<std::string,
          std::map<std::string, std::map<std::string, PropertyVariantType>>>
@@ -148,21 +148,136 @@
         // Path not found in cache, therefore owner missing
         return false;
     }
-    for (const auto& serv : itServ->second)
+    for (const auto& service : itServ->second)
     {
         auto itIntf = std::find_if(
-            serv.second.begin(), serv.second.end(),
+            service.second.second.begin(), service.second.second.end(),
             [&intf](const auto& interface) { return intf == interface; });
-        if (itIntf != std::end(serv.second))
+        if (itIntf != std::end(service.second.second))
         {
             // Service found, return owner state
-            return serv.first.second;
+            return service.second.first;
         }
     }
     // Interface not found in cache, therefore owner missing
     return false;
 }
 
+void Manager::setOwner(const std::string& path, const std::string& serv,
+                       const std::string& intf, bool isOwned)
+{
+    auto itServ = _servTree.find(path);
+    if (itServ == _servTree.end())
+    {
+        auto intfs = {intf};
+        _servTree[path] = {{serv, std::make_pair(isOwned, intfs)}};
+        return;
+    }
+    for (auto& service : itServ->second)
+    {
+        auto itIntf = std::find_if(
+            service.second.second.begin(), service.second.second.end(),
+            [&intf](const auto& interface) { return intf == interface; });
+        if (itIntf != std::end(service.second.second))
+        {
+            if (service.first == serv)
+            {
+                service.second.first = isOwned;
+                return;
+            }
+        }
+    }
+    auto intfs = {intf};
+    itServ->second[serv] = std::make_pair(isOwned, intfs);
+}
+
+const std::string& Manager::findService(const std::string& path,
+                                        const std::string& intf)
+{
+    static const std::string empty = "";
+
+    auto itServ = _servTree.find(path);
+    if (itServ != _servTree.end())
+    {
+        for (const auto& service : itServ->second)
+        {
+            auto itIntf = std::find_if(
+                service.second.second.begin(), service.second.second.end(),
+                [&intf](const auto& interface) { return intf == interface; });
+            if (itIntf != std::end(service.second.second))
+            {
+                // Service found, return service name
+                return service.first;
+            }
+        }
+    }
+
+    return empty;
+}
+
+void Manager::addServices(const std::string& path, const std::string& intf,
+                          int32_t depth)
+{
+    // Get all subtree objects for the given interface
+    auto objects = util::SDBusPlus::getSubTree(util::SDBusPlus::getBus(), "/",
+                                               intf, depth);
+    // Add what's returned to the cache of path->services
+    for (auto& itPath : objects)
+    {
+        auto pathIter = _servTree.find(itPath.first);
+        if (pathIter != _servTree.end())
+        {
+            // Path found in cache
+            for (auto& itServ : itPath.second)
+            {
+                auto servIter = pathIter->second.find(itServ.first);
+                if (servIter != pathIter->second.end())
+                {
+                    // Service found in cache
+                    for (auto& itIntf : itServ.second)
+                    {
+                        if (std::find(servIter->second.second.begin(),
+                                      servIter->second.second.end(),
+                                      itIntf) == servIter->second.second.end())
+                        {
+                            // Add interface to cache
+                            servIter->second.second.emplace_back(itIntf);
+                        }
+                    }
+                }
+                else
+                {
+                    // Service not found in cache
+                    auto intfs = {intf};
+                    pathIter->second[itServ.first] =
+                        std::make_pair(true, intfs);
+                }
+            }
+        }
+        else
+        {
+            // Path not found in cache
+            auto intfs = {intf};
+            _servTree[itPath.first] = {
+                {itPath.second.begin()->first, std::make_pair(true, intfs)}};
+        }
+    }
+}
+
+const std::string& Manager::getService(const std::string& path,
+                                       const std::string& intf)
+{
+    // Retrieve service from cache
+    const auto& serviceName = findService(path, intf);
+    if (serviceName.empty())
+    {
+        addServices(path, intf, 0);
+        return findService(path, intf);
+    }
+
+    return serviceName;
+}
+
 void Manager::addTimer(const TimerType type,
                        const std::chrono::microseconds interval,
                        std::unique_ptr<TimerPkg> pkg)
diff --git a/control/json/manager.hpp b/control/json/manager.hpp
index eab11a6..420551e 100644
--- a/control/json/manager.hpp
+++ b/control/json/manager.hpp
@@ -188,6 +188,47 @@
     static bool hasOwner(const std::string& path, const std::string& intf);
 
     /**
+     * @brief Sets the dbus service owner state of a given object
+     *
+     * @param[in] path - Dbus object path
+     * @param[in] serv - Dbus service name
+     * @param[in] intf - Dbus object interface
+     * @param[in] isOwned - Dbus service owner state
+     */
+    void setOwner(const std::string& path, const std::string& serv,
+                  const std::string& intf, bool isOwned);
+
+    /**
+     * @brief Add a set of services for a path and interface by retrieving all
+     * the path subtrees to the given depth from root for the interface
+     *
+     * @param[in] path - Path to add services for
+     * @param[in] intf - Interface to add services for
+     * @param[in] depth - Depth of tree traversal from root path
+     *
+     * @throws - DBusMethodError
+     * Throws a DBusMethodError when the `getSubTree` method call fails
+     */
+    static void addServices(const std::string& path, const std::string& intf,
+                            int32_t depth);
+
+    /**
+     * @brief Get the service for a given path and interface from cached
+     * dataset and attempt to add all the services for the given path/interface
+     * when it's not found
+     *
+     * @param[in] path - Path to get service for
+     * @param[in] intf - Interface to get service for
+     *
+     * @return - The now cached service name
+     *
+     * @throws - DBusMethodError
+     * Ripples up a DBusMethodError exception from calling addServices
+     */
+    static const std::string& getService(const std::string& path,
+                                         const std::string& intf);
+
+    /**
      * @brief Get the object's property value as a variant
      *
      * @param[in] path - Path of the object containing the property
@@ -252,9 +293,10 @@
     /* List of active profiles */
     static std::vector<std::string> _activeProfiles;
 
-    /* Subtree map of paths to services (with ownership state) of interfaces */
-    static std::map<std::string, std::map<std::pair<std::string, bool>,
-                                          std::vector<std::string>>>
+    /* Subtree map of paths to services of interfaces(with ownership state) */
+    static std::map<
+        std::string,
+        std::map<std::string, std::pair<bool, std::vector<std::string>>>>
         _servTree;
 
     /* Object map of paths to interfaces of properties and their values */
@@ -273,6 +315,18 @@
     std::map<configKey, std::unique_ptr<Event>> _events;
 
     /**
+     * @brief Find the service name for a given path and interface from the
+     * cached dataset
+     *
+     * @param[in] path - Path to get service for
+     * @param[in] intf - Interface to get service for
+     *
+     * @return - The cached service name
+     */
+    static const std::string& findService(const std::string& path,
+                                          const std::string& intf);
+
+    /**
      * @brief Parse and set the configured profiles from the profiles JSON file
      *
      * Retrieves the optional profiles JSON configuration file, parses it, and