Create OCC pass-through objects

Create OCC pass-through d-bus objects when corresponding CPU objects are
created in the inventory.

Resolves openbmc/openbmc#1450.

Change-Id: I8da879f51ebef8dcc3d25358def81c5e0dce0617
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 50d23da..b68f9a0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,11 +1,9 @@
 # Build these headers, don't install them
 noinst_HEADERS = \
-	occ_finder.hpp \
 	occ_pass_through.hpp
 
 sbin_PROGRAMS = openpower-occ-control
 openpower_occ_control_SOURCES = \
-	occ_finder.cpp \
 	occ_pass_through.cpp \
 	app.cpp \
 	org/open_power/OCC/PassThrough/error.cpp
@@ -18,7 +16,8 @@
 openpower_occ_control_LDFLAGS = \
 	$(SDBUSPLUS_LIBS) \
 	$(PHOSPHOR_LOGGING_LIBS) \
-	$(OPENPOWER_DBUS_INTERFACES_LIBS)
+	$(OPENPOWER_DBUS_INTERFACES_LIBS) \
+        -lstdc++fs
 
 openpower_occ_control_CXXFLAGS =
 	$(SDBUSPLUS_CFLAGS) \
diff --git a/app.cpp b/app.cpp
index 0ffe711..4b73e51 100644
--- a/app.cpp
+++ b/app.cpp
@@ -6,7 +6,19 @@
 {
     try
     {
-        open_power::occ::pass_through::run();
+        auto bus = sdbusplus::bus::new_default();
+        bus.request_name(OCC_PASS_THROUGH_BUSNAME);
+
+        sdbusplus::server::manager::manager objManager(bus,
+                                                       OCC_PASS_THROUGH_ROOT);
+
+        open_power::occ::pass_through::manager::Manager mgr(bus);
+
+        while (true)
+        {
+            bus.process_discard();
+            bus.wait();
+        }
     }
     catch (const std::exception& e)
     {
diff --git a/configure.ac b/configure.ac
index 211e723..676dc0d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,17 +43,21 @@
 AS_IF([test "x$INVENTORY_ITEM_INTERFACE" == "x"], [INVENTORY_ITEM_INTERFACE="xyz.openbmc_project.Inventory.Item"])
 AC_DEFINE_UNQUOTED([INVENTORY_ITEM_INTERFACE], ["$INVENTORY_ITEM_INTERFACE"], [The Inventory.Item root])
 
-AC_ARG_VAR(INVENTORY_ROOT, [The Inventory root])
-AS_IF([test "x$INVENTORY_ROOT" == "x"], [INVENTORY_ROOT="/xyz/openbmc_project/inventory/system"])
-AC_DEFINE_UNQUOTED([INVENTORY_ROOT], ["$INVENTORY_ROOT"], [The Inventory root])
+AC_ARG_VAR(CPU_PATH, [CPU inventory path])
+AS_IF([test "x$CPU_PATH" == "x"], [CPU_PATH="/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu"])
+AC_DEFINE_UNQUOTED([CPU_PATH], ["$CPU_PATH"], [CPU inventory path])
 
-AC_ARG_VAR(CPU_NAME, [The name of the cpu object])
+AC_ARG_VAR(MAX_CPUS, [The max number of CPUs])
+AS_IF([test "x$MAX_CPUS" == "x"], [MAX_CPUS=2])
+AC_DEFINE_UNQUOTED([MAX_CPUS], [$MAX_CPUS], [The max number of CPUs])
+
+AC_ARG_VAR(CPU_NAME, [The CPU object name])
 AS_IF([test "x$CPU_NAME" == "x"], [CPU_NAME="cpu"])
-AC_DEFINE_UNQUOTED([CPU_NAME], ["$CPU_NAME"], [The name of the cpu object])
+AC_DEFINE_UNQUOTED([CPU_NAME], ["$CPU_NAME"], [The CPU object name])
 
-AC_ARG_VAR(OCC_NAME, [The name of the occ object])
+AC_ARG_VAR(OCC_NAME, [The OCC object name])
 AS_IF([test "x$OCC_NAME" == "x"], [OCC_NAME="occ"])
-AC_DEFINE_UNQUOTED([OCC_NAME], ["$OCC_NAME"], [The name of the occ object])
+AC_DEFINE_UNQUOTED([OCC_NAME], ["$OCC_NAME"], [The OCC object name])
 
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_FILES([Makefile])
diff --git a/occ_finder.cpp b/occ_finder.cpp
deleted file mode 100644
index 8d6391d..0000000
--- a/occ_finder.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <algorithm>
-#include <sdbusplus/server.hpp>
-#include "occ_finder.hpp"
-#include "config.h"
-
-namespace open_power
-{
-namespace occ
-{
-namespace finder
-{
-
-constexpr auto toChar(size_t c)
-{
-    constexpr auto map = "0123";
-    return map[c];
-}
-
-std::vector<std::string> get()
-{
-    auto bus = sdbusplus::bus::new_default();
-    auto mapper =
-        bus.new_method_call(
-            "xyz.openbmc_project.ObjectMapper",
-            "/xyz/openbmc_project/object_mapper",
-            "xyz.openbmc_project.ObjectMapper",
-            "GetSubTreePaths");
-
-    auto depth = 0;
-    mapper.append(std::string(INVENTORY_ROOT));
-    mapper.append(depth);
-    mapper.append(std::vector<std::string>(
-        {std::string(INVENTORY_ITEM_INTERFACE)}));
-
-    auto result = bus.call(mapper);
-    if (result.is_method_error())
-    {
-        throw std::runtime_error("ObjectMapper GetSubTreePaths failed");
-    }
-
-    std::vector<std::string> response;
-    result.read(response);
-    if (response.empty())
-    {
-        throw std::runtime_error("ObjectMapper GetSubTreePaths : bad response");
-    }
-
-    std::vector<std::string> occs;
-    size_t count = 0;
-    std::string cpu = std::string(CPU_NAME) + toChar(count);
-    auto constexpr MAX_PROCS = 4; // Revisit for multi-node systems
-    for (const auto& path: response)
-    {
-        if (std::equal(cpu.crbegin(), cpu.crend(), path.crbegin()))
-        {
-            if(count == MAX_PROCS)
-            {
-                break;
-            }
-            occs.emplace_back(std::string(OCC_NAME) +
-                              toChar(count++));
-        }
-        cpu.back() = toChar(count);
-    }
-
-    return occs;
-}
-
-} // namespace finder
-} // namespace occ
-} // namespace open_power
diff --git a/occ_finder.hpp b/occ_finder.hpp
deleted file mode 100644
index 2a31b71..0000000
--- a/occ_finder.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include <vector>
-#include <string>
-
-namespace open_power
-{
-namespace occ
-{
-namespace finder
-{
-
-/** @brief Get OCC objects on the system by mapping them to processor inventory
- *  @returns list of occ objects, such as occ0, occ1, and so on.
- */
-std::vector<std::string> get();
-
-} // namespace finder
-} // namespace occ
-} // namespace open_power
diff --git a/occ_pass_through.cpp b/occ_pass_through.cpp
index 7027d65..911dd85 100644
--- a/occ_pass_through.cpp
+++ b/occ_pass_through.cpp
@@ -6,7 +6,6 @@
 #include <phosphor-logging/elog.hpp>
 #include <org/open_power/OCC/PassThrough/error.hpp>
 #include "occ_pass_through.hpp"
-#include "occ_finder.hpp"
 #include "elog-errors.hpp"
 namespace open_power
 {
@@ -15,30 +14,6 @@
 namespace pass_through
 {
 
-void run()
-{
-    auto bus = sdbusplus::bus::new_default();
-    sdbusplus::server::manager::manager objManager(bus,
-                                                   OCC_PASS_THROUGH_ROOT);
-
-    std::vector<std::unique_ptr<PassThrough>> objects;
-    auto occs = open_power::occ::finder::get();
-
-    for (const auto& occ : occs)
-    {
-        auto occPassThrough = object(occ);
-        objects.emplace_back(
-            std::make_unique<PassThrough>(bus, occPassThrough.c_str()));
-    }
-    bus.request_name(OCC_PASS_THROUGH_BUSNAME);
-
-    while (true)
-    {
-        bus.process_discard();
-        bus.wait();
-    }
-}
-
 PassThrough::PassThrough(
     sdbusplus::bus::bus& bus,
     const char* path) :
diff --git a/occ_pass_through.hpp b/occ_pass_through.hpp
index 070e371..c9f6566 100644
--- a/occ_pass_through.hpp
+++ b/occ_pass_through.hpp
@@ -1,14 +1,20 @@
 #pragma once
 
 #include <string>
+#include <cstring>
+#include <map>
 #include <vector>
+#include <experimental/filesystem>
 #include <unistd.h>
 #include <sdbusplus/bus.hpp>
+#include <functional>
 #include <sdbusplus/server/object.hpp>
 #include <org/open_power/OCC/PassThrough/server.hpp>
 #include "config.h"
 #include "file.hpp"
 
+namespace sdbusRule = sdbusplus::bus::match::rules;
+
 namespace open_power
 {
 namespace occ
@@ -16,20 +22,86 @@
 namespace pass_through
 {
 
-/** @brief Make occ pass-through d-bus object pathname
- *  @param[in] occ - occ name
- *  @returns occ pass-through path
- */
-inline auto object(const std::string& occ)
-{
-    return std::string(OCC_PASS_THROUGH_ROOT) +
-           '/' +
-           occ;
-}
+class PassThrough;
 
-/** @brief Put occ pass through objects on the bus
+namespace manager
+{
+
+/** @class Manager
+ *  @brief Builds and manages OCC pass-through objects
  */
-void run();
+struct Manager
+{
+    public:
+        Manager() = delete;
+        Manager(const Manager&) = delete;
+        Manager& operator=(const Manager&) = delete;
+        Manager(Manager&&) = default;
+        Manager& operator=(Manager&&) = default;
+        ~Manager() = default;
+
+        /** @brief Ctor - Add OCC pass-through objects on the bus. Create
+         *         OCC objects when corresponding CPU inventory is created.
+         *  @param[in] bus - handle to the bus
+         */
+        Manager(sdbusplus::bus::bus& bus):
+            bus(bus)
+        {
+            for (auto id = 0; id < MAX_CPUS; ++id)
+            {
+                auto path = std::string(CPU_PATH) + std::to_string(id);
+                cpuMatches.emplace_back(
+                    bus,
+                    sdbusRule::interfacesAdded() +
+                    sdbusRule::argNpath(0, path),
+                    std::bind(std::mem_fn(&Manager::cpuCreated),
+                              this, std::placeholders::_1));
+            }
+        }
+
+        /** @brief Callback that responds to cpu creation in the inventory -
+         *         by creating the occ passthrough object.
+         *
+         *  @param[in] msg - bus message
+         *
+         *  @returns 0 to indicate success
+         */
+        int cpuCreated(sdbusplus::message::message& msg)
+        {
+            namespace fs = std::experimental::filesystem;
+
+            sdbusplus::message::object_path o;
+            msg.read(o);
+            fs::path cpuPath(std::string(std::move(o)));
+            auto cpu = cpuPath.filename();
+
+            auto occPath = fs::path(OCC_PASS_THROUGH_ROOT);
+            std::string name{cpu.c_str()};
+            auto index = name.find(CPU_NAME);
+            name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
+            occPath /= name;
+
+            objects.emplace_back(
+                std::make_unique<PassThrough>(
+                    bus,
+                    occPath.c_str()));
+
+            return 0;
+        }
+
+    private:
+        /** @brief reference to the bus */
+        sdbusplus::bus::bus& bus;
+
+        /** @brief OCC pass-through objects */
+        std::vector<std::unique_ptr<PassThrough>> objects;
+
+        /** @brief sbdbusplus match objects */
+        std::vector<sdbusplus::bus::match_t> cpuMatches;
+};
+
+} // namespace manager
+
 
 using Iface = sdbusplus::server::object::object<
     sdbusplus::org::open_power::OCC::server::PassThrough>;