Reconstruct OCC objects on app restart

The OCC objects would get created when corresponding CPU inventory items
get added. This doesn't cover a scenario where the OCC app restarts.

If the CPU inventory is already present when the app starts, construct
OCC objects as well.

Resolves openbmc/openbmc#1824.

Change-Id: I4994d93ba6f528ca67977604ccb1da717563092a
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 5042ec5..361af89 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,8 @@
 	occ_status.hpp \
 	powercap.hpp \
 	occ_errors.hpp \
-	occ_events.hpp
+	occ_events.hpp \
+	occ_finder.hpp
 
 sbin_PROGRAMS = openpower-occ-control
 openpower_occ_control_SOURCES = \
@@ -14,7 +15,8 @@
 	occ_errors.cpp \
 	app.cpp \
 	powercap.cpp \
-	org/open_power/OCC/Device/error.cpp
+	org/open_power/OCC/Device/error.cpp \
+	occ_finder.cpp
 
 BUILT_SOURCES =  org/open_power/OCC/Device/error.hpp \
                  org/open_power/OCC/Device/error.cpp
diff --git a/configure.ac b/configure.ac
index dadc118..06497f1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,10 @@
 AS_IF([test "x$PS_DERATING_FACTOR" == "x"], [PS_DERATING_FACTOR=90])
 AC_DEFINE_UNQUOTED([PS_DERATING_FACTOR], [$PS_DERATING_FACTOR], [The power supply derating factor])
 
+AC_ARG_VAR(CPU_SUBPATH, [The subpath containing CPU objects])
+AS_IF([test "x$CPU_SUBPATH" == "x"], [CPU_SUBPATH="/xyz/openbmc_project/inventory/system/chassis/motherboard"])
+AC_DEFINE_UNQUOTED([CPU_SUBPATH], ["$CPU_SUBPATH"], [The subpath containing CPU objects])
+
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_FILES([Makefile test/Makefile])
 AC_OUTPUT
diff --git a/occ_finder.cpp b/occ_finder.cpp
new file mode 100644
index 0000000..a533067
--- /dev/null
+++ b/occ_finder.cpp
@@ -0,0 +1,82 @@
+#include <algorithm>
+#include <iterator>
+#include <experimental/filesystem>
+#include <sdbusplus/server.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include "occ_finder.hpp"
+#include "config.h"
+
+namespace open_power
+{
+namespace occ
+{
+namespace finder
+{
+
+using namespace phosphor::logging;
+
+constexpr auto toChar(size_t c)
+{
+    constexpr auto map = "0123456789abcdef";
+    return map[c];
+}
+
+std::vector<std::string> get()
+{
+    namespace fs = std::experimental::filesystem;
+    using Path = std::string;
+    using Service = std::string;
+    using Interface = std::string;
+    using Interfaces = std::vector<Interface>;
+
+    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",
+            "GetSubTree");
+
+    auto depth = 0;
+    Path path = CPU_SUBPATH;
+    Interfaces interfaces{INVENTORY_ITEM_INTERFACE};
+    mapper.append(path);
+    mapper.append(depth);
+    mapper.append(interfaces);
+
+    auto result = bus.call(mapper);
+    if (result.is_method_error())
+    {
+        // It's okay to not have inventory, for example at BMC standby
+        return {};
+    }
+
+    using MapperResponse = std::map<Path, std::map<Service, Interfaces>>;
+    MapperResponse response;
+    result.read(response);
+    if (response.empty())
+    {
+        // It's okay to not have inventory, for example at BMC standby
+        return {};
+    }
+
+    std::vector<std::string> occs;
+    for (auto count = 0; count < MAX_CPUS; ++count)
+    {
+        fs::path p(path);
+        p /= std::string(CPU_NAME) + toChar(count);
+        if (response.end() != response.find(p.string()))
+        {
+            occs.emplace_back(std::string(OCC_NAME) +
+                              toChar(count));
+        }
+    }
+
+    return occs;
+}
+
+} // namespace finder
+} // namespace occ
+} // namespace open_power
diff --git a/occ_finder.hpp b/occ_finder.hpp
new file mode 100644
index 0000000..cad8277
--- /dev/null
+++ b/occ_finder.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <vector>
+#include <string>
+
+namespace open_power
+{
+namespace occ
+{
+namespace finder
+{
+
+/** @brief Get OCC objects on the system by mapping them to CPU inventory
+  * @returns vector 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_manager.hpp b/occ_manager.hpp
index 4430d74..7b76703 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -5,10 +5,11 @@
 #include <experimental/filesystem>
 #include <functional>
 #include <sdbusplus/bus.hpp>
+#include <powercap.hpp>
 #include "occ_pass_through.hpp"
 #include "occ_status.hpp"
+#include "occ_finder.hpp"
 #include "config.h"
-#include <powercap.hpp>
 
 namespace sdbusRule = sdbusplus::bus::match::rules;
 
@@ -41,15 +42,29 @@
             bus(bus),
             event(event)
         {
-            for (auto id = 0; id < MAX_CPUS; ++id)
+            // Check if CPU inventory exists already.
+            auto occs = open_power::occ::finder::get();
+            if (occs.empty())
             {
-                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));
+                // Need to watch for CPU inventory creation.
+                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));
+                }
+            }
+            else
+            {
+                for (const auto& occ : occs)
+                {
+                    // CPU inventory exists already, OCC objects can be created.
+                    createObjects(occ);
+                }
             }
         }
 
@@ -67,13 +82,25 @@
             sdbusplus::message::object_path o;
             msg.read(o);
             fs::path cpuPath(std::string(std::move(o)));
-            auto cpu = cpuPath.filename();
 
-            std::string name{cpu.c_str()};
+            auto name = cpuPath.filename().string();
             auto index = name.find(CPU_NAME);
             name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
 
-            auto path = fs::path(OCC_CONTROL_ROOT) / name;
+            createObjects(name);
+
+            return 0;
+        }
+
+    private:
+        /** @brief Create child OCC objects.
+         *
+         *  @param[in] occ - the occ name, such as occ0.
+         */
+        void createObjects(const std::string& occ)
+        {
+            auto path = fs::path(OCC_CONTROL_ROOT) / occ;
+
             passThroughObjects.emplace_back(
                 std::make_unique<PassThrough>(
                     bus,
@@ -86,16 +113,14 @@
                     path.c_str()));
 
             // Create the power cap monitor object for master occ (0)
-            if(!pcap && (index == 0))
+            if (!pcap)
             {
                 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
                                                         bus,
-                                                        *statusObjects[index]);
+                                                        *statusObjects.front());
             }
-            return 0;
         }
 
-    private:
         /** @brief reference to the bus */
         sdbusplus::bus::bus& bus;