psu-ng: Get max PSU number from entity manager

Read the max number of PSUs supported from entity manager instead of
JSON since entity manager would have the value based on system. If the
entity manager interface does not exist by the time the psu manager
starts, subscribe to interfaces added. The psu manager can take action
once the max count property is available on D-Bus.

Tested: On Rainier 2U with debug traces:
1. Restarted phosphor-psu-monitor and verified maxPowerSupplies was 2.
2. Stopped xyz.openbmc_project.Inventory.Manager. Restarted
   phosphor-psu-monitor and verified maxPowerSupplies was 0.
3. Started xyz.openbmc_project.Inventory.Manager and verified the
   psu callback was triggered setting maxPowerSupplies to 2.
4. Stopped and started xyz.openbmc_project.Inventory.Manager and
   verified the psu callback was not called again.

Change-Id: Ieff796ca5c79bfddc1fc6e15e339a0b15eb422d7
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/phosphor-power-supply/README.md b/phosphor-power-supply/README.md
index bd86b11..ffb57f5 100644
--- a/phosphor-power-supply/README.md
+++ b/phosphor-power-supply/README.md
@@ -11,10 +11,6 @@
 
 The JSON configuration file should contain:
 
-## MaxPowerSupplies
-Optional property, integer, that indicates the maximum number of power supplies
-that should be present.
-
 ## PowerSupplies
 An array of power supply properties.
 
@@ -27,3 +23,11 @@
 ### Address
 A string representing the 16-bit I2C address of the PMBus power supply.
 
+# D-Bus System Configuration
+
+Entity Manager provides information about the supported system configuration.
+The information is as follows:
+
+## Max Power Supplies
+Integer that indicates the maximum number of power supplies that should be
+present. This is exposed via the `MaxCount` property.
diff --git a/phosphor-power-supply/configurations/witherspoon/psu_config.json b/phosphor-power-supply/configurations/witherspoon/psu_config.json
index 4542cea..2087284 100644
--- a/phosphor-power-supply/configurations/witherspoon/psu_config.json
+++ b/phosphor-power-supply/configurations/witherspoon/psu_config.json
@@ -1,7 +1,4 @@
 {
-    "SystemProperties" : {
-        "MaxPowerSupplies" : 2
-    },
     "PowerSupplies": [
         {
             "Inventory" : "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0",
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 1f578aa..3117e2c 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -11,6 +11,10 @@
 namespace phosphor::power::manager
 {
 
+constexpr auto supportedConfIntf =
+    "xyz.openbmc_project.Configuration.SupportedConfiguration";
+constexpr auto maxCountProp = "MaxCount";
+
 PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
                        const std::string& configfile) :
     bus(bus)
@@ -18,6 +22,7 @@
     // Parse out the JSON properties
     sysProperties = {0};
     getJSONProperties(configfile);
+    getSystemProperties();
 
     using namespace sdeventplus;
     auto interval = std::chrono::milliseconds(1000);
@@ -44,23 +49,11 @@
         throw std::runtime_error("Failed to load JSON configuration file");
     }
 
-    if (!configFileJSON.contains("SystemProperties"))
-    {
-        throw std::runtime_error("Missing required SystemProperties");
-    }
-
     if (!configFileJSON.contains("PowerSupplies"))
     {
         throw std::runtime_error("Missing required PowerSupplies");
     }
 
-    auto sysProps = configFileJSON["SystemProperties"];
-
-    if (sysProps.contains("MaxPowerSupplies"))
-    {
-        sysProperties.maxPowerSupplies = sysProps["MaxPowerSupplies"];
-    }
-
     for (auto psuJSON : configFileJSON["PowerSupplies"])
     {
         if (psuJSON.contains("Inventory") && psuJSON.contains("Bus") &&
@@ -85,6 +78,82 @@
     }
 }
 
+void PSUManager::getSystemProperties()
+{
+    // Subscribe to InterfacesAdded before doing a property read, otherwise
+    // the interface could be created after the read attempt but before the
+    // match is created.
+    entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
+        bus,
+        sdbusplus::bus::match::rules::interfacesAdded() +
+            sdbusplus::bus::match::rules::sender(
+                "xyz.openbmc_project.EntityManager"),
+        std::bind(&PSUManager::supportedConfIfaceAdded, this,
+                  std::placeholders::_1));
+
+    uint64_t maxCount;
+    try
+    {
+        util::DbusSubtree subtree =
+            util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
+        auto objectIt = subtree.cbegin();
+        if (objectIt == subtree.cend())
+        {
+            throw std::runtime_error("Supported Configuration Not Found");
+        }
+        std::string objPath = objectIt->first;
+        auto serviceIt = objectIt->second.cbegin();
+        if (serviceIt != objectIt->second.cend())
+        {
+            std::string service = serviceIt->first;
+            if (!service.empty())
+            {
+                util::getProperty<uint64_t>(supportedConfIntf, maxCountProp,
+                                            objPath, service, bus, maxCount);
+                sysProperties.maxPowerSupplies = maxCount;
+
+                // Don't need the match anymore
+                entityManagerIfacesAddedMatch.reset();
+            }
+        }
+    }
+    catch (std::exception& e)
+    {
+        // Interface or property not found. Let the Interfaces Added callback
+        // process the information once the interfaces are added to D-Bus.
+    }
+}
+
+void PSUManager::supportedConfIfaceAdded(sdbusplus::message::message& msg)
+{
+    try
+    {
+        sdbusplus::message::object_path objPath;
+        std::map<std::string, std::map<std::string, std::variant<uint64_t>>>
+            interfaces;
+        msg.read(objPath, interfaces);
+
+        auto itIntf = interfaces.find(supportedConfIntf);
+        if (itIntf == interfaces.cend())
+        {
+            return;
+        }
+
+        auto itProp = itIntf->second.find(maxCountProp);
+        if (itProp != itIntf->second.cend())
+        {
+            sysProperties.maxPowerSupplies = std::get<0>(itProp->second);
+
+            // Don't need the match anymore
+            entityManagerIfacesAddedMatch.reset();
+        }
+    }
+    catch (std::exception& e)
+    {
+        // Ignore, the property may be of a different type than expected.
+    }
+}
+
 void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
 {
     int32_t state = 0;
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index f3bba27..d3d101a 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -47,13 +47,18 @@
                const std::string& configfile);
 
     /**
-     * @brief Initialize the system properties and PowerSupply objects from
-     *        the JSON config file.
+     * @brief Initialize the PowerSupply objects from the JSON config file.
      * @param[in] path - Path to the JSON config file
      */
     void getJSONProperties(const std::string& path);
 
     /**
+     * @brief Initialize the system properties from the Supported Configuration
+     *        D-Bus object provided by Entity Manager.
+     */
+    void getSystemProperties();
+
+    /**
      * Initializes the manager.
      *
      * Get current BMC state, ...
@@ -163,6 +168,9 @@
     /** @brief Used to subscribe to D-Bus power on state changes */
     std::unique_ptr<sdbusplus::bus::match_t> powerOnMatch;
 
+    /** @brief Used to subscribe to Entity Manager interfaces added */
+    std::unique_ptr<sdbusplus::bus::match_t> entityManagerIfacesAddedMatch;
+
     /**
      * @brief Callback for power state property changes
      *
@@ -173,6 +181,15 @@
     void powerStateChanged(sdbusplus::message::message& msg);
 
     /**
+     * @brief Callback for supported configuration interface added
+     *
+     * Process the information from the supported configuration interface
+     *
+     * @param[in] msg - Data associated with the interfaces added signal
+     */
+    void supportedConfIfaceAdded(sdbusplus::message::message& msg);
+
+    /**
      * @brief Adds properties to the inventory.
      *
      * Reads the values from the devices and writes them to the associated