side-switch: power off chassis in prep for update

When switching to a new level of firmware, ensure the host is off and
the chassis power is off before switching to the new firmware.

Tested:
- Verify power off works
<6> Checking for side switch reboot
<6> Running firmware version path is /xyz/openbmc_project/software/74575136
<6> Running firmware version priority is 1
<6> Running image is at priority 1, execute side switch
<see power off occur in journal>
<6> chassis power is off

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: Ifdb3e0c51fe66e55ebb1c3fca7097af49607bf35
diff --git a/side-switch/side_switch.cpp b/side-switch/side_switch.cpp
index fb558c4..4c13ed0 100644
--- a/side-switch/side_switch.cpp
+++ b/side-switch/side_switch.cpp
@@ -3,18 +3,18 @@
 #include "utils.hpp"
 
 #include <phosphor-logging/lg2.hpp>
-#include <sdbusplus/bus.hpp>
 
 #include <exception>
 #include <string>
+#include <thread>
 #include <variant>
 #include <vector>
 
 PHOSPHOR_LOG2_USING;
 
-bool sideSwitchNeeded()
+bool sideSwitchNeeded(sdbusplus::bus::bus& bus)
 {
-    auto bus = sdbusplus::bus::new_default();
+
     std::string fwRunningVersionPath;
     uint8_t fwRunningPriority = 0;
 
@@ -124,15 +124,71 @@
     return (false);
 }
 
+bool powerOffSystem(sdbusplus::bus::bus& bus)
+{
+
+    try
+    {
+        utils::PropertyValue chassOff =
+            "xyz.openbmc_project.State.Chassis.Transition.Off";
+        utils::setProperty(bus, "/xyz/openbmc_project/state/chassis0",
+                           "xyz.openbmc_project.State.Chassis",
+                           "RequestedPowerTransition", chassOff);
+    }
+    catch (const std::exception& e)
+    {
+        error("chassis off request failed: {ERROR}", "ERROR", e);
+        return (false);
+    }
+
+    // Now just wait for power to turn off
+    // Worst case is a systemd service hangs in power off for 2 minutes so
+    // take that and double it to avoid any timing issues. The user has
+    // requested we switch to the other side, so a lengthy delay is warranted
+    // if needed. On most systems the power off takes 5-15 seconds.
+    for (int i = 0; i < 240; i++)
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+        try
+        {
+            auto currentPwrState = utils::getProperty<std::string>(
+                bus, "/xyz/openbmc_project/state/chassis0",
+                "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
+
+            if (currentPwrState ==
+                "xyz.openbmc_project.State.Chassis.PowerState.Off")
+            {
+                info("chassis power is off");
+                return (true);
+            }
+        }
+        catch (const std::exception& e)
+        {
+            error("reading chassis power state failed: {ERROR}", "ERROR", e);
+            return (false);
+        }
+    }
+    error("timeout waiting for chassis power to turn off");
+    return (false);
+}
+
 int main()
 {
     info("Checking for side switch reboot");
 
-    if (!sideSwitchNeeded())
+    auto bus = sdbusplus::bus::new_default();
+
+    if (!sideSwitchNeeded(bus))
     {
         info("Side switch not needed");
         return 0;
     }
 
+    if (!powerOffSystem(bus))
+    {
+        error("unable to power off chassis");
+        return 0;
+    }
+
     // TODO - Future commits in series to fill in rest of logic
 }