Add utils functions

The utils.hpp/cpp are copied from phosphor-psu-code-mgmt and only keep
the functions that are related to get service, get properties.

Signed-off-by: Lei YU <yulei.sh@bytedance.com>
Change-Id: I6aab996f92c87560280c626d7776fe0177b57610
diff --git a/src/meson.build b/src/meson.build
index 5f99abd..c668624 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -5,6 +5,7 @@
 shared_library(
   'inspur-ipmi-oem',
   'inspur_oem.cpp',
+  'utils.cpp',
   dependencies: [
     phosphor_dbus_interfaces,
     phosphor_logging,
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644
index 0000000..70a4e61
--- /dev/null
+++ b/src/utils.cpp
@@ -0,0 +1,94 @@
+#include "utils.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+#include <algorithm>
+
+using namespace phosphor::logging;
+
+namespace utils
+{
+
+namespace // anonymous
+{
+constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
+constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
+constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
+} // namespace
+
+const UtilsInterface& getUtils()
+{
+    static Utils utils;
+    return utils;
+}
+
+std::string Utils::getService(sdbusplus::bus::bus& bus, const char* path,
+                              const char* interface) const
+{
+    auto services = getServices(bus, path, interface);
+    if (services.empty())
+    {
+        return {};
+    }
+    return services[0];
+}
+
+std::vector<std::string> Utils::getServices(sdbusplus::bus::bus& bus,
+                                            const char* path,
+                                            const char* interface) const
+{
+    auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
+                                      MAPPER_INTERFACE, "GetObject");
+
+    mapper.append(path, std::vector<std::string>({interface}));
+    try
+    {
+        auto mapperResponseMsg = bus.call(mapper);
+
+        std::vector<std::pair<std::string, std::vector<std::string>>>
+            mapperResponse;
+        mapperResponseMsg.read(mapperResponse);
+        if (mapperResponse.empty())
+        {
+            log<level::ERR>("Error reading mapper response");
+            throw std::runtime_error("Error reading mapper response");
+        }
+        std::vector<std::string> ret;
+        for (const auto& i : mapperResponse)
+        {
+            ret.emplace_back(i.first);
+        }
+        return ret;
+    }
+    catch (const sdbusplus::exception::SdBusError& ex)
+    {
+        log<level::ERR>("GetObject call failed", entry("PATH=%s", path),
+                        entry("INTERFACE=%s", interface));
+        throw std::runtime_error("GetObject call failed");
+    }
+}
+
+any Utils::getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
+                           const char* path, const char* interface,
+                           const char* propertyName) const
+{
+    auto method = bus.new_method_call(service, path,
+                                      "org.freedesktop.DBus.Properties", "Get");
+    method.append(interface, propertyName);
+    try
+    {
+        PropertyType value{};
+        auto reply = bus.call(method);
+        reply.read(value);
+        return any(value);
+    }
+    catch (const sdbusplus::exception::SdBusError& ex)
+    {
+        log<level::ERR>("GetProperty call failed", entry("PATH=%s", path),
+                        entry("INTERFACE=%s", interface),
+                        entry("PROPERTY=%s", propertyName));
+        throw std::runtime_error("GetProperty call failed");
+    }
+}
+
+} // namespace utils
diff --git a/src/utils.hpp b/src/utils.hpp
new file mode 100644
index 0000000..d382bf9
--- /dev/null
+++ b/src/utils.hpp
@@ -0,0 +1,131 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+
+#include <experimental/any>
+#include <string>
+#include <vector>
+
+namespace utils
+{
+
+class UtilsInterface;
+
+// Due to a libstdc++ bug, we got compile error using std::any with gmock.
+// A temporary workaround is to use std::experimental::any.
+// See details in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90415
+using std::experimental::any;
+using std::experimental::any_cast;
+
+/**
+ * @brief Get the implementation of UtilsInterface
+ */
+const UtilsInterface& getUtils();
+
+/** @brief Get service name from object path and interface
+ *
+ * @param[in] bus          - The Dbus bus object
+ * @param[in] path         - The Dbus object path
+ * @param[in] interface    - The Dbus interface
+ *
+ * @return The name of the service
+ */
+std::string getService(sdbusplus::bus::bus& bus, const char* path,
+                       const char* interface);
+
+/** @brief Get all the service names from object path and interface
+ *
+ * @param[in] bus          - The Dbus bus object
+ * @param[in] path         - The Dbus object path
+ * @param[in] interface    - The Dbus interface
+ *
+ * @return The name of the services
+ */
+std::vector<std::string> getServices(sdbusplus::bus::bus& bus, const char* path,
+                                     const char* interface);
+
+/** @brief The template function to get property from the requested dbus path
+ *
+ * @param[in] bus          - The Dbus bus object
+ * @param[in] service      - The Dbus service name
+ * @param[in] path         - The Dbus object path
+ * @param[in] interface    - The Dbus interface
+ * @param[in] propertyName - The property name to get
+ *
+ * @return The value of the property
+ */
+template <typename T>
+T getProperty(sdbusplus::bus::bus& bus, const char* service, const char* path,
+              const char* interface, const char* propertyName);
+
+/**
+ * @brief The interface for utils
+ */
+class UtilsInterface
+{
+  public:
+    // For now the code needs to get property for Present and Version
+    using PropertyType = std::variant<std::string, bool>;
+
+    virtual ~UtilsInterface() = default;
+
+    virtual std::string getService(sdbusplus::bus::bus& bus, const char* path,
+                                   const char* interface) const = 0;
+
+    virtual std::vector<std::string>
+        getServices(sdbusplus::bus::bus& bus, const char* path,
+                    const char* interface) const = 0;
+
+    virtual any getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
+                                const char* path, const char* interface,
+                                const char* propertyName) const = 0;
+
+    template <typename T>
+    T getProperty(sdbusplus::bus::bus& bus, const char* service,
+                  const char* path, const char* interface,
+                  const char* propertyName) const
+    {
+        any result =
+            getPropertyImpl(bus, service, path, interface, propertyName);
+        auto value = any_cast<PropertyType>(result);
+        return std::get<T>(value);
+    }
+};
+
+class Utils : public UtilsInterface
+{
+  public:
+    std::string getService(sdbusplus::bus::bus& bus, const char* path,
+                           const char* interface) const override;
+
+    std::vector<std::string> getServices(sdbusplus::bus::bus& bus,
+                                         const char* path,
+                                         const char* interface) const override;
+
+    any getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
+                        const char* path, const char* interface,
+                        const char* propertyName) const override;
+};
+
+inline std::string getService(sdbusplus::bus::bus& bus, const char* path,
+                              const char* interface)
+{
+    return getUtils().getService(bus, path, interface);
+}
+
+inline std::vector<std::string> getServices(sdbusplus::bus::bus& bus,
+                                            const char* path,
+                                            const char* interface)
+{
+    return getUtils().getServices(bus, path, interface);
+}
+
+template <typename T>
+T getProperty(sdbusplus::bus::bus& bus, const char* service, const char* path,
+              const char* interface, const char* propertyName)
+{
+    return getUtils().getProperty<T>(bus, service, path, interface,
+                                     propertyName);
+}
+
+} // namespace utils