Add Chassis System Power cycle

Added chassis system instance under State, it will serve to power cycle
whole chassis by setting state transision to power cycle. And it will
internally invoke platform specific target chassis-system-reset.target.

User can provide platform specific target and service file which
will execute provided script or commands

Tested: Verified this with following command
busctl set-property xyz.openbmc_project.State.Chassis
/xyz/openbmc_project/state/chassis_system0 \
xyz.openbmc_project.State.Chassis \
RequestedPowerTransition s \
xyz.openbmc_project.State.Chassis.Transition.PowerCycle

Signed-off-by: Vijay Khemka <vijaykhemka@fb.com>
Change-Id: I76b826c3a33a51b31d423543dae8e968f0f68737
diff --git a/power-control-x86/src/power_control.cpp b/power-control-x86/src/power_control.cpp
index d3a2054..1a66bf6 100644
--- a/power-control-x86/src/power_control.cpp
+++ b/power-control-x86/src/power_control.cpp
@@ -35,6 +35,7 @@
 std::shared_ptr<sdbusplus::asio::connection> conn;
 static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
 static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
+static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSysIface;
 static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
 static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
 static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
@@ -1812,6 +1813,26 @@
         });
 }
 
+static constexpr auto systemdBusname = "org.freedesktop.systemd1";
+static constexpr auto systemdPath = "/org/freedesktop/systemd1";
+static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
+static constexpr auto systemTargetName = "chassis-system-reset.target";
+
+void systemReset()
+{
+    conn->async_method_call(
+        [](boost::system::error_code ec) {
+            if (ec)
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "Failed to call chassis system reset",
+                    phosphor::logging::entry("ERR=%s", ec.message().c_str()));
+            }
+        },
+        systemdBusname, systemdPath, systemdInterface, "StartUnit",
+        systemTargetName, "replace");
+}
+
 static void nmiSetEnablePorperty(bool value)
 {
     conn->async_method_call(
@@ -2237,6 +2258,43 @@
 
     power_control::chassisIface->initialize();
 
+    // Chassis System Service
+    sdbusplus::asio::object_server chassisSysServer =
+        sdbusplus::asio::object_server(power_control::conn);
+
+    // Chassis System Interface
+    power_control::chassisSysIface = chassisSysServer.add_interface(
+        "/xyz/openbmc_project/state/chassis_system0",
+        "xyz.openbmc_project.State.Chassis");
+
+    power_control::chassisSysIface->register_property(
+        "RequestedPowerTransition",
+        std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
+        [](const std::string& requested, std::string& resp) {
+            if (requested ==
+                "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
+            {
+                power_control::systemReset();
+                addRestartCause(power_control::RestartCause::command);
+            }
+            else
+            {
+                std::cerr << "Unrecognized chassis system state transition "
+                             "request.\n";
+                throw std::invalid_argument("Unrecognized Transition Request");
+                return 0;
+            }
+            resp = requested;
+            return 1;
+        });
+    power_control::chassisSysIface->register_property(
+        "CurrentPowerState",
+        std::string(power_control::getChassisState(power_control::powerState)));
+    power_control::chassisSysIface->register_property(
+        "LastStateChangeTime", power_control::getCurrentTimeMs());
+
+    power_control::chassisSysIface->initialize();
+
     // Buttons Service
     sdbusplus::asio::object_server buttonsServer =
         sdbusplus::asio::object_server(power_control::conn);