Util: Add common dbus code to util namespace

Add dbus code that can be used by analyzer, isolator, attention to util.

Signed-off-by: Ben Tyner <ben.tyner@ibm.com>
Change-Id: I6205ea227b72c7bafa230446c3c2120b87abc207
diff --git a/util/dbus.cpp b/util/dbus.cpp
new file mode 100644
index 0000000..5b4de10
--- /dev/null
+++ b/util/dbus.cpp
@@ -0,0 +1,170 @@
+#include <util/dbus.hpp>
+#include <util/trace.hpp>
+
+namespace util
+{
+
+namespace dbus
+{
+
+//------------------------------------------------------------------------------
+
+constexpr auto objectMapperService   = "xyz.openbmc_project.ObjectMapper";
+constexpr auto objectMapperPath      = "/xyz/openbmc_project/object_mapper";
+constexpr auto objectMapperInterface = "xyz.openbmc_project.ObjectMapper";
+
+/** @brief Find the path and service that implements the given interface */
+int find(const std::string& i_interface, std::string& o_path,
+         std::string& o_service)
+{
+    int rc = 1; // assume not success
+
+    auto bus = sdbusplus::bus::new_default();
+
+    constexpr auto function = "GetSubTree";
+
+    try
+    {
+        auto method = bus.new_method_call(objectMapperService, objectMapperPath,
+                                          objectMapperInterface, function);
+
+        // Search the entire dbus tree for the specified interface
+        method.append(std::string{"/"}, 0,
+                      std::vector<std::string>{i_interface});
+
+        auto reply = bus.call(method);
+
+        DBusSubTree response;
+        reply.read(response);
+
+        if (!response.empty())
+        {
+            // Response is a map of object paths to a map of service, interfaces
+            auto object = *(response.begin());
+            o_path      = object.first;                 // return path
+            o_service   = object.second.begin()->first; // return service
+
+            rc = 0; // success
+        }
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        trace::err("util::dbus::find exception");
+        std::string traceMsg = std::string(e.what());
+        trace::err(traceMsg.c_str());
+    }
+
+    return rc;
+}
+
+/** @brief Find the service that implements the given object and interface */
+int findService(const std::string& i_interface, const std::string& i_path,
+                std::string& o_service)
+{
+    int rc = 1; // assume not success
+
+    auto bus = sdbusplus::bus::new_default();
+
+    constexpr auto function = "GetObject";
+
+    try
+    {
+        auto method = bus.new_method_call(objectMapperService, objectMapperPath,
+                                          objectMapperInterface, function);
+
+        // Find services that implement the object path, constrain the search
+        // to the given interface.
+        method.append(i_path, std::vector<std::string>{i_interface});
+
+        auto reply = bus.call(method);
+
+        // response is a map of service names to their interfaces
+        std::map<DBusService, DBusInterfaceList> response;
+        reply.read(response);
+
+        if (!response.empty())
+        {
+            // return the service
+            o_service = response.begin()->first;
+
+            rc = 0; // success
+        }
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        trace::err("util::dbus::map exception");
+        std::string traceMsg = std::string(e.what());
+        trace::err(traceMsg.c_str());
+    }
+
+    return rc;
+}
+
+/** @brief Read a property from a dbus object interface */
+int getProperty(const std::string& i_interface, const std::string& i_path,
+                const std::string& i_service, const std::string& i_property,
+                DBusValue& o_response)
+{
+    int rc = 1; // assume not success
+
+    auto bus = sdbusplus::bus::new_default();
+
+    constexpr auto interface = "org.freedesktop.DBus.Properties";
+    constexpr auto function  = "Get";
+
+    try
+    {
+        // calling the get property method
+        auto method = bus.new_method_call(i_service.c_str(), i_path.c_str(),
+                                          interface, function);
+
+        method.append(i_interface, i_property);
+        auto reply = bus.call(method);
+
+        // returning the property value
+        reply.read(o_response);
+
+        rc = 0; // success
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        trace::err("util::dbus::getProperty exception");
+        std::string traceMsg = std::string(e.what());
+        trace::err(traceMsg.c_str());
+    }
+
+    return rc;
+}
+
+/** @brief Get the IBM compatible names defined for this system */
+std::vector<std::string> systemNames()
+{
+    std::vector<std::string> names;
+
+    constexpr auto interface =
+        "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
+
+    DBusService service;
+    DBusPath path;
+
+    // find a dbus object and path that implements the interface
+    if (0 == find(interface, path, service))
+    {
+        DBusValue value;
+
+        // compatible system names are implemented as a property
+        constexpr auto property = "Names";
+
+        if (0 == getProperty(interface, path, service, property, value))
+        {
+            // return value is a variant, names are in the vector
+            names = std::get<std::vector<std::string>>(value);
+        }
+    }
+
+    return names;
+}
+
+} // namespace dbus
+
+} // namespace util
diff --git a/util/dbus.hpp b/util/dbus.hpp
new file mode 100644
index 0000000..03fb5be
--- /dev/null
+++ b/util/dbus.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace util
+{
+
+namespace dbus
+{
+
+using DBusValue         = std::variant<std::string, bool, std::vector<uint8_t>,
+                               std::vector<std::string>>;
+using DBusProperty      = std::string;
+using DBusInterface     = std::string;
+using DBusService       = std::string;
+using DBusPath          = std::string;
+using DBusInterfaceList = std::vector<DBusInterface>;
+using DBusSubTree =
+    std::map<DBusPath, std::map<DBusService, DBusInterfaceList>>;
+
+/**
+ * Find the dbus object path and service that implements the given interface
+ *
+ * @param[in]   i_interface Interface to search for
+ * @param[out]  o_path      Path of dbus object implementing the interface
+ * @param[out]  o_service   Service implementing the dbus object path
+ * @return      non-zero on error
+ */
+int find(const std::string& i_interface, std::string& o_path,
+         std::string& o_service);
+
+/**
+ * Find the dbus service that implements the given dbus object and interface
+ *
+ * @param[in]   i_interface Interface that maps to the service
+ * @param[in]   i_path      Path that maps to the service
+ * @param[out]  o_service   Service implementing the dbus object and interface
+ * @return      non-zer on error
+ */
+int findService(const std::string& i_interface, const std::string& i_path,
+                std::string& o_service);
+
+/**
+ * Read a property from a dbus object interface
+ *
+ * @param[in]   i_interface Interface implementing the property
+ * @param[in]   i_path      Path of the dbus object
+ * @param[in]   i_service   Service implementing the dbus object and interface
+ * @param[in]   i_property  Property to read
+ * @return      non-zero on error
+ */
+int getProperty(const std::string& i_interface, const std::string& i_path,
+                const std::string& i_service, const std::string& i_property,
+                DBusValue& o_response);
+
+/**
+ * Get the IBM compatible names defined for this system
+ *
+ * @return     A vector of strings containing the system names
+ */
+std::vector<std::string> systemNames();
+
+} // namespace dbus
+
+} // namespace util
diff --git a/util/meson.build b/util/meson.build
index e4f94c0..4e559d4 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -1,5 +1,6 @@
 # Source files.
 util_src = files(
+    'dbus.cpp',
     'ffdc_file.cpp',
     'pdbg.cpp',
     'temporary_file.cpp',