utils: Add a class to cache service lookups

Currently we are doing interface + path -> service lookups for each ipmi
command sent to the daemon. For many types of commands this is not ideal
as the daemon is unlikely to change often at runtime. Implement a basic
cache with the ability to invalidate the service name at any time. This
is used by code in a future commit.

Change-Id: I85b04dd17ac19e31d49070f289704b429bd1e577
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/utils.cpp b/utils.cpp
index bd8fade..31c219c 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -250,6 +250,47 @@
 }
 
 
+ServiceCache::ServiceCache(const std::string& intf, const std::string& path)
+    : intf(intf), path(path), cachedService(std::experimental::nullopt),
+      cachedBusName(std::experimental::nullopt)
+{
+}
+
+ServiceCache::ServiceCache(std::string&& intf, std::string&& path)
+    : intf(std::move(intf)), path(std::move(path)),
+      cachedService(std::experimental::nullopt),
+      cachedBusName(std::experimental::nullopt)
+{
+}
+
+const std::string& ServiceCache::getService(sdbusplus::bus::bus& bus)
+{
+    if (!isValid(bus))
+    {
+        cachedBusName = bus.get_unique_name();
+        cachedService = ::ipmi::getService(bus, intf, path);
+    }
+    return cachedService.value();
+}
+
+void ServiceCache::invalidate()
+{
+    cachedBusName = std::experimental::nullopt;
+    cachedService = std::experimental::nullopt;
+}
+
+sdbusplus::message::message ServiceCache::newMethodCall(
+        sdbusplus::bus::bus& bus, const char *intf, const char *method)
+{
+    return bus.new_method_call(getService(bus).c_str(), path.c_str(),
+                               intf, method);
+}
+
+bool ServiceCache::isValid(sdbusplus::bus::bus& bus) const
+{
+    return cachedService && cachedBusName == bus.get_unique_name();
+}
+
 std::string getService(sdbusplus::bus::bus& bus,
                        const std::string& intf,
                        const std::string& path)