netipmid: Provide support to fetch paths of settings dbus objects

Resolves openbmc/openbmc#2065

Change-Id: I71038cb13807cb4dd87fc6661d27e95f08cdc29e
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 11c07b2..33735fd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -52,11 +52,27 @@
 	command/payload_cmds.hpp \
 	command/payload_cmds.cpp \
 	sol_module.hpp \
-	sol_module.cpp
+	sol_module.cpp \
+	settings.hpp \
+	settings.cpp
 
 netipmid_CPPFLAGS = -DNET_IPMID_LIB_PATH=\"/usr/lib/net-ipmid/\"
-netipmid_LDFLAGS = $(SYSTEMD_LIBS) $(CRYPTO_LIBS) $(libmapper_LIBS) $(PHOSPHOR_LOGGING_LIBS) $(LIBADD_DLOPEN) -export-dynamic
-netipmid_CXXFLAGS = $(SYSTEMD_CFLAGS) $(libmapper_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
+
+
+netipmid_LDFLAGS = \
+	$(SYSTEMD_LIBS) \
+	$(CRYPTO_LIBS) \
+	$(libmapper_LIBS) \
+	$(PHOSPHOR_LOGGING_LIBS) \
+	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+	$(LIBADD_DLOPEN) \
+	-export-dynamic
+
+netipmid_CXXFLAGS = \
+	$(SYSTEMD_CFLAGS) \
+	$(libmapper_CFLAGS) \
+	$(PHOSPHOR_LOGGING_CFLAGS) \
+	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
 
 SUBDIRS = test
 
diff --git a/configure.ac b/configure.ac
index 3b7a75b..8b7ec12 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,6 +43,8 @@
 PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
 PKG_CHECK_MODULES([CRYPTO], [libcrypto >= 1.0.2g], ,[AC_MSG_ERROR([can't find openssl libcrypto])])
 PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, [AC_MSG_ERROR([Could not find phosphor-logging...openbmc/phosphor-logging package required])])
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],, AC_MSG_ERROR(["Requires sdbusplus package."]))
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],, [AC_MSG_ERROR([Could not find phosphor-dbus-interfaces...openbmc/phosphor-dbus-interfaces package required])])
 AC_CHECK_LIB([mapper], [mapper_get_service], ,[AC_MSG_ERROR([Could not find libmapper...openbmc/phosphor-objmgr package required])])
 
 # Checks for header files.
diff --git a/settings.cpp b/settings.cpp
new file mode 100644
index 0000000..cb86d99
--- /dev/null
+++ b/settings.cpp
@@ -0,0 +1,82 @@
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include "xyz/openbmc_project/Common/error.hpp"
+#include "settings.hpp"
+
+namespace settings
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
+constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper";
+constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
+
+Objects::Objects(sdbusplus::bus::bus& bus,
+                 const std::vector<Interface>& filter):
+    bus(bus)
+{
+    auto depth = 0;
+
+    auto mapperCall = bus.new_method_call(mapperService,
+                                          mapperPath,
+                                          mapperIntf,
+                                          "GetSubTree");
+    mapperCall.append(root);
+    mapperCall.append(depth);
+    mapperCall.append(filter);
+    auto response = bus.call(mapperCall);
+    if (response.is_method_error())
+    {
+        log<level::ERR>("Error in mapper GetSubTree");
+        elog<InternalFailure>();
+    }
+
+    using Interfaces = std::vector<Interface>;
+    using MapperResponse = std::map<Path, std::map<Service, Interfaces>>;
+    MapperResponse result;
+    response.read(result);
+    if (result.empty())
+    {
+        log<level::ERR>("Invalid response from mapper");
+        elog<InternalFailure>();
+    }
+
+    for (auto& iter : result)
+    {
+        const auto& path = iter.first;
+        auto& interface = iter.second.begin()->second[0];
+        map.emplace(std::move(interface), path);
+    }
+}
+
+Service Objects::service(const Path& path, const Interface& interface) const
+{
+    using Interfaces = std::vector<Interface>;
+    auto mapperCall = bus.new_method_call(mapperService,
+                                          mapperPath,
+                                          mapperIntf,
+                                          "GetObject");
+    mapperCall.append(path);
+    mapperCall.append(Interfaces({interface}));
+
+    auto response = bus.call(mapperCall);
+    if (response.is_method_error())
+    {
+        log<level::ERR>("Error in mapper GetObject");
+        elog<InternalFailure>();
+    }
+
+    std::map<Service, Interfaces> result;
+    response.read(result);
+    if (result.empty())
+    {
+        log<level::ERR>("Invalid response from mapper");
+        elog<InternalFailure>();
+    }
+
+    return result.begin()->first;
+}
+
+} // namespace settings
diff --git a/settings.hpp b/settings.hpp
new file mode 100644
index 0000000..60ee4d4
--- /dev/null
+++ b/settings.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <string>
+#include <sdbusplus/bus.hpp>
+
+namespace settings
+{
+
+using Path = std::string;
+using Service = std::string;
+using Interface = std::string;
+
+constexpr auto root = "/";
+
+/** @class Objects
+ *  @brief Fetch paths of settings d-bus objects of interest, upon construction
+ */
+struct Objects
+{
+    public:
+        /** @brief Constructor - fetch settings objects
+         *
+         * @param[in] bus - The Dbus bus object
+         * @param[in] filter - A vector of settings interfaces the caller is
+         *            interested in.
+         */
+        Objects(sdbusplus::bus::bus& bus, const std::vector<Interface>& filter);
+        Objects(const Objects&) = default;
+        Objects& operator=(const Objects&) = default;
+        Objects(Objects&&) = delete;
+        Objects& operator=(Objects&&) = delete;
+        ~Objects() = default;
+
+        /** @brief Fetch d-bus service, given a path and an interface. The
+         *         service can't be cached because mapper returns unique
+         *         service names.
+         *
+         * @param[in] path - The Dbus object
+         * @param[in] interface - The Dbus interface
+         *
+         * @return std::string - the dbus service
+         */
+        Service service(const Path& path, const Interface& interface) const;
+
+        // TODO openbmc/openbmc#2058 - This will break when multiple settings,
+        // or in general multiple objects implement a single setting interface.
+        // For instance this will break for a 2-blade server, because we'd have
+        // 2 sets of settings objects. Need to revisit and fix this.
+        /** @brief map of settings objects */
+        std::map<Interface, Path> map;
+
+        /** @brief The Dbus bus object */
+        sdbusplus::bus::bus& bus;
+};
+
+} // namespace settings