Cache service names for paths and interfaces

To lookup a service name for a given path and interface, use GetSubTree
to retrieve all the service names from the root path for the interface
and add each entry to a cached dataset.

Tested:
    Check that a missing interface's service is added
    Check that a missing path that exists has its service added
    Inspect all missing paths for an interface are added
    Inspect missing interfaces are added to existing service
    Returned service name matches the service for a path/interface

Change-Id: I6db05479e825350198896b2e237882f7ebc58d51
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/zone.cpp b/control/zone.cpp
index 209d773..41ee0cc 100644
--- a/control/zone.cpp
+++ b/control/zone.cpp
@@ -479,6 +479,93 @@
         });
 }
 
+const std::string& Zone::getService(const std::string& path,
+                                    const std::string& intf)
+{
+    // Retrieve service from cache
+    auto srvIter = _servTree.find(path);
+    if (srvIter != _servTree.end())
+    {
+        for (auto& serv : srvIter->second)
+        {
+            auto it = std::find_if(
+                serv.second.begin(),
+                serv.second.end(),
+                [&intf](auto const& interface)
+                {
+                    return intf == interface;
+                });
+            if (it != std::end(serv.second))
+            {
+                // Service found
+                return serv.first;
+            }
+        }
+        // Interface not found in cache, add and return
+        return addServices(path, intf, 0);
+    }
+    else
+    {
+        // Path not found in cache, add and return
+        return addServices(path, intf, 0);
+    }
+}
+
+const std::string& Zone::addServices(const std::string& path,
+                                     const std::string& intf,
+                                     int32_t depth)
+{
+    static const std::string empty = "";
+    auto it = _servTree.end();
+
+    // Get all subtree objects for the given interface
+    auto objects = util::SDBusPlus::getSubTree(_bus, "/", intf, depth);
+    // Add what's returned to the cache of path->services
+    for (auto& pIter : objects)
+    {
+        auto pathIter = _servTree.find(pIter.first);
+        if (pathIter != _servTree.end())
+        {
+            // Path found in cache
+            for (auto& sIter : pIter.second)
+            {
+                auto servIter = pathIter->second.find(sIter.first);
+                if (servIter != pathIter->second.end())
+                {
+                    // Service found in cache
+                    for (auto& iIter : sIter.second)
+                    {
+                        // Add interface to cache
+                        servIter->second.emplace_back(iIter);
+                    }
+                }
+                else
+                {
+                    // Service not found in cache
+                    pathIter->second.insert(sIter);
+                }
+            }
+        }
+        else
+        {
+            _servTree.insert(pIter);
+        }
+        // When the paths match, since a single interface constraint is given,
+        // that is the service to return
+        if (path == pIter.first)
+        {
+            it = _servTree.find(pIter.first);
+        }
+    }
+
+    if (it != _servTree.end())
+    {
+        return it->second.begin()->first;
+    }
+
+    return empty;
+}
+
 }
 }
 }