power-restore: one-time support

The one_time PowerRestorePolicy property was introduced so system owners
could utilize the feature for one time type scenarios without rewriting
all of the logic on their own.

IBM wishes to utilize the one_time power restore policy to cause an
automatic power on after an in-band firmware update.

The one_time setting is defined by default within meta-phosphor here:
https://gerrit.openbmc-project.xyz/c/openbmc/openbmc/+/40948

Testing:
- Set one time and verify it's the one used and then reset to default
busctl set-property xyz.openbmc_project.Settings /xyz/openbmc_project/control/host0/power_restore_policy/one_time xyz.openbmc_project.Control.Power.RestorePolicy PowerRestorePolicy s xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff

busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/control/host0/power_restore_policy/one_time xyz.openbmc_project.Control.Power.RestorePolicy PowerRestorePolicy
s "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff"

phosphor-discover-system-state

Feb 25 19:44:11 witherspoon phosphor-discover-system-state[495]: One time set, use it and reset to default

busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/control/host0/power_restore_policy/one_time xyz.openbmc_project.Control.Power.RestorePolicy PowerRestorePolicy
s "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None"

- Verify when one time setting is at default it is ignored
busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/control/host0/power_restore_policy/one_time xyz.openbmc_project.Control.Power.RestorePolicy PowerRestorePolicy
s "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None"

phosphor-discover-system-state

Feb 25 19:47:08 witherspoon phosphor-discover-system-state[498]: One time not set, check user setting of power policy
Feb 25 19:47:08 witherspoon phosphor-discover-system-state[498]: Host power is off, processing power policy

- Verify the one time power on path
busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/control/host0/power_restore_policy/one_time xyz.openbmc_project.Control.Power.RestorePolicy PowerRestorePolicy
s "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn"

phosphor-discover-system-state

Feb 25 20:14:15 witherspoon phosphor-discover-system-state[504]: One time set, use it and reset to default
Feb 25 20:14:15 witherspoon phosphor-discover-system-state[504]: Host power is off, processing power policy
Feb 25 20:14:15 witherspoon phosphor-discover-system-state[504]: power_policy=ALWAYS_POWER_ON, powering host on

busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/control/host0/power_restore_policy/one_time xyz.openbmc_project.Control.Power.RestorePolicy PowerRestorePolicy
s "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None"

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I13715ad2fda1f7aa0d170e506570e85f1cef8722
diff --git a/discover_system_state.cpp b/discover_system_state.cpp
index 06a32c4..88fa4ab 100644
--- a/discover_system_state.cpp
+++ b/discover_system_state.cpp
@@ -155,17 +155,71 @@
 
     // This application is only run if chassis power is off
 
-    auto method = bus.new_method_call(
+    /* The logic here is to first check the one-time PowerRestorePolicy setting.
+     * If this property is not the default then look at the persistent
+     * user setting in the non one-time object, otherwise honor the one-time
+     * setting.
+     */
+    auto methodOneTime = bus.new_method_call(
+        settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
+        settings.powerRestorePolicyOneTime.c_str(),
+        "org.freedesktop.DBus.Properties", "Get");
+    methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy");
+
+    auto methodUserSetting = bus.new_method_call(
         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
         "Get");
-    method.append(powerRestoreIntf, "PowerRestorePolicy");
+    methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy");
 
     std::variant<std::string> result;
     try
     {
-        auto reply = bus.call(method);
+        auto reply = bus.call(methodOneTime);
         reply.read(result);
+        auto powerPolicy = std::get<std::string>(result);
+
+        if (RestorePolicy::Policy::None ==
+            RestorePolicy::convertPolicyFromString(powerPolicy))
+        {
+            // one_time is set to None so use the customer setting
+            log<level::INFO>(
+                "One time not set, check user setting of power policy");
+            auto reply = bus.call(methodUserSetting);
+            reply.read(result);
+            powerPolicy = std::get<std::string>(result);
+        }
+        else
+        {
+            // one_time setting was set so we're going to use it. Reset it
+            // to default for next time.
+            log<level::INFO>("One time set, use it and reset to default");
+            setProperty(bus, settings.powerRestorePolicyOneTime.c_str(),
+                        powerRestoreIntf, "PowerRestorePolicy",
+                        convertForMessage(RestorePolicy::Policy::None));
+        }
+
+        log<level::INFO>("Host power is off, processing power policy",
+                         entry("POWER_POLICY=%s", powerPolicy.c_str()));
+
+        if (RestorePolicy::Policy::AlwaysOn ==
+            RestorePolicy::convertPolicyFromString(powerPolicy))
+        {
+            log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on");
+            setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
+                        convertForMessage(server::Host::Transition::On));
+        }
+        else if (RestorePolicy::Policy::Restore ==
+                 RestorePolicy::convertPolicyFromString(powerPolicy))
+        {
+            log<level::INFO>("power_policy=RESTORE, restoring last state");
+
+            // Read last requested state and re-request it to execute it
+            auto hostReqState = getProperty(bus, hostPath, HOST_BUSNAME,
+                                            "RequestedHostTransition");
+            setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
+                        hostReqState);
+        }
     }
     catch (const SdBusError& e)
     {
@@ -174,29 +228,5 @@
         elog<InternalFailure>();
     }
 
-    auto powerPolicy = std::get<std::string>(result);
-
-    log<level::INFO>("Host power is off, checking power policy",
-                     entry("POWER_POLICY=%s", powerPolicy.c_str()));
-
-    if (RestorePolicy::Policy::AlwaysOn ==
-        RestorePolicy::convertPolicyFromString(powerPolicy))
-    {
-        log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on");
-        setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
-                    convertForMessage(server::Host::Transition::On));
-    }
-    else if (RestorePolicy::Policy::Restore ==
-             RestorePolicy::convertPolicyFromString(powerPolicy))
-    {
-        log<level::INFO>("power_policy=RESTORE, restoring last state");
-
-        // Read last requested state and re-request it to execute it
-        auto hostReqState =
-            getProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
-        setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
-                    hostReqState);
-    }
-
     return 0;
 }
diff --git a/settings.cpp b/settings.cpp
index cc0b4cb..f9f100f 100644
--- a/settings.cpp
+++ b/settings.cpp
@@ -77,7 +77,20 @@
                 }
                 else if (powerRestoreIntf == interface)
                 {
-                    powerRestorePolicy = path;
+                    /* There are two implementations of the PowerRestorePolicy
+                     * Interface. A persistent user setting and a one-time
+                     * setting which is only valid for one boot of the system.
+                     * The one-time setting will have "one_time" in its
+                     * object path.
+                     */
+                    if (path.find("one_time") != std::string::npos)
+                    {
+                        powerRestorePolicyOneTime = path;
+                    }
+                    else
+                    {
+                        powerRestorePolicy = path;
+                    }
                 }
             }
         }
diff --git a/settings.hpp b/settings.hpp
index de8be65..dcdfe8a 100644
--- a/settings.hpp
+++ b/settings.hpp
@@ -53,6 +53,9 @@
     /** @brief host power_restore_policy settings object */
     Path powerRestorePolicy;
 
+    /** @brief host power_restore_policy one-time settings object */
+    Path powerRestorePolicyOneTime;
+
     /** @brief The Dbus bus object */
     sdbusplus::bus::bus& bus;
 };