Add power_state implementation

Added power_state implementation for multi host platform
specific shutdown interfaces like hard shutdown or soft
shutdown interfaces.

Getting the current host state of all the hosts and set
power_state values based on the hosts current host state.

TESTED : Verified the Shutdown alarms trigged and platform
specific actions done in Facebook YosemiteV2 platform.

Signed-off-by: Kumar Thangavel <thangavel.k@hcl.com>
Change-Id: I5e38bb9f28b7840c9de10082905b31deb8ffc4ff
diff --git a/configure.ac b/configure.ac
index 2cc5221..f2be82d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,21 @@
 AM_CONDITIONAL([WANT_MONITOR], [test "x$enable_monitor" != "xno"])
 AM_CONDITIONAL([WANT_SENSOR_MONITOR], [test "x$enable_sensor_monitor" == "xyes"])
 
+
+AC_ARG_ENABLE([host-state],
+    AS_HELP_STRING([--enable-host-state], [Enable host state]))
+
+AM_CONDITIONAL([WANT_HOST_STATE], [test "x$enable_host_state" == "xyes"])
+
+AM_COND_IF([WANT_HOST_STATE],
+    [
+        AM_CONDITIONAL(HOST_STATE_ENABLED, true)
+    ],
+    [
+        AM_CONDITIONAL(HOST_STATE_ENABLED, false)
+    ]
+)
+
 # Package specific checks.
 AS_IF([test "x$enable_presence" != "xno"], [
     # Use runtime(json) config, otherwise default to compile time(yaml) config
diff --git a/power_state.hpp b/power_state.hpp
index dc74707..ad8a0d5 100644
--- a/power_state.hpp
+++ b/power_state.hpp
@@ -5,9 +5,13 @@
 #include <fmt/format.h>
 
 #include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/State/Host/server.hpp>
 
 #include <functional>
 
+using HostState =
+    sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
+
 namespace phosphor::fan
 {
 
@@ -221,4 +225,149 @@
     sdbusplus::bus::match::match _match;
 };
 
+/**
+ * @class HostPowerState
+ *
+ * This class implements the PowerState API by looking at the 'powerState'
+ * property on the phosphor virtual sensor interface.
+ */
+class HostPowerState : public PowerState
+{
+  public:
+    virtual ~HostPowerState() = default;
+    HostPowerState(const HostPowerState&) = delete;
+    HostPowerState& operator=(const HostPowerState&) = delete;
+    HostPowerState(HostPowerState&&) = delete;
+    HostPowerState& operator=(HostPowerState&&) = delete;
+
+    HostPowerState() :
+        PowerState(),
+        _match(_bus,
+               sdbusplus::bus::match::rules::propertiesChangedNamespace(
+                   _hostStatePath, _hostStateInterface),
+               [this](auto& msg) { this->hostStateChanged(msg); })
+    {
+        readHostState();
+    }
+
+    /**
+     * @brief Constructor
+     *
+     * @param[in] bus - The D-Bus bus connection object
+     * @param[in] callback - The function that should be run when
+     *                       the power state changes
+     */
+    HostPowerState(sdbusplus::bus::bus& bus, StateChangeFunc func) :
+        PowerState(bus, func),
+        _match(_bus,
+               sdbusplus::bus::match::rules::propertiesChangedNamespace(
+                   _hostStatePath, _hostStateInterface),
+               [this](auto& msg) { this->hostStateChanged(msg); })
+    {
+        readHostState();
+    }
+
+    /**
+     * @brief PropertiesChanged callback for the CurrentHostState property.
+     *
+     * Will call the registered callback function if necessary.
+     *
+     * @param[in] msg - The payload of the propertiesChanged signal
+     */
+    void hostStateChanged(sdbusplus::message::message& msg)
+    {
+        std::string interface;
+        std::map<std::string, std::variant<std::string>> properties;
+        std::vector<HostState> hostPowerStates;
+
+        msg.read(interface, properties);
+
+        auto hostStateProp = properties.find(_hostStateProperty);
+        if (hostStateProp != properties.end())
+        {
+            auto currentHostState =
+                sdbusplus::message::convert_from_string<HostState>(
+                    std::get<std::string>(hostStateProp->second));
+
+            if (!currentHostState)
+            {
+                throw sdbusplus::exception::InvalidEnumString();
+            }
+            HostState hostState = *currentHostState;
+
+            hostPowerStates.emplace_back(hostState);
+            setHostPowerState(hostPowerStates);
+        }
+    }
+
+  private:
+    void setHostPowerState(std::vector<HostState>& hostPowerStates)
+    {
+        bool powerStateflag = false;
+        for (const auto& powerState : hostPowerStates)
+        {
+            if (powerState == HostState::Standby ||
+                powerState == HostState::Running ||
+                powerState == HostState::TransitioningToRunning ||
+                powerState == HostState::Quiesced ||
+                powerState == HostState::DiagnosticMode)
+            {
+                powerStateflag = true;
+                break;
+            }
+        }
+        setPowerState(powerStateflag);
+    }
+
+    /**
+     * @brief Reads the CurrentHostState property from D-Bus and saves it.
+     */
+    void readHostState()
+    {
+
+        std::string hostStatePath;
+        std::string hostStateService;
+        std::string hostService = "xyz.openbmc_project.State.Host";
+        std::vector<HostState> hostPowerStates;
+
+        int32_t depth = 0;
+        const std::string path = "/";
+
+        auto mapperResponse = util::SDBusPlus::getSubTreeRaw(
+            _bus, path, _hostStateInterface, depth);
+
+        for (const auto& path : mapperResponse)
+        {
+            for (const auto& service : path.second)
+            {
+                hostStateService = service.first;
+
+                if (hostStateService.find(hostService) != std::string::npos)
+                {
+                    hostStatePath = path.first;
+
+                    auto currentHostState =
+                        util::SDBusPlus::getProperty<HostState>(
+                            hostStateService, hostStatePath,
+                            _hostStateInterface, _hostStateProperty);
+
+                    hostPowerStates.emplace_back(currentHostState);
+                }
+            }
+        }
+        setHostPowerState(hostPowerStates);
+    }
+
+    const std::string _hostStatePath{"/xyz/openbmc_project/state"};
+
+    /** @brief D-Bus interface constant */
+    const std::string _hostStateInterface{"xyz.openbmc_project.State.Host"};
+
+    /** @brief D-Bus property constant */
+    const std::string _hostStateProperty{"CurrentHostState"};
+
+    /** @brief The propertiesChanged match */
+    sdbusplus::bus::match::match _match;
+};
+
 } // namespace phosphor::fan
diff --git a/sensor-monitor/Makefile.am b/sensor-monitor/Makefile.am
index 313c59f..16333ab 100644
--- a/sensor-monitor/Makefile.am
+++ b/sensor-monitor/Makefile.am
@@ -26,3 +26,8 @@
 	$(PHOSPHOR_LOGGING_CFLAGS) \
 	${PHOSPHOR_DBUS_INTERFACES_CFLAGS} \
 	-flto
+
+if HOST_STATE_ENABLED
+ENABLE_HOST_STATE = 1
+sensor_monitor_CXXFLAGS+=-DENABLE_HOST_STATE
+endif
diff --git a/sensor-monitor/main.cpp b/sensor-monitor/main.cpp
index 8f1cc56..fc9967f 100644
--- a/sensor-monitor/main.cpp
+++ b/sensor-monitor/main.cpp
@@ -28,8 +28,13 @@
     auto bus = sdbusplus::bus::new_default();
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
 
+#ifdef ENABLE_HOST_STATE
+    std::shared_ptr<phosphor::fan::PowerState> powerState =
+        std::make_shared<phosphor::fan::HostPowerState>();
+#else
     std::shared_ptr<phosphor::fan::PowerState> powerState =
         std::make_shared<phosphor::fan::PGoodState>();
+#endif
 
     ShutdownAlarmMonitor shutdownMonitor{bus, event, powerState};