1 #include "config.h"
2 
3 #include "host_state_manager.hpp"
4 #include "settings.hpp"
5 #include "utils.hpp"
6 #include "xyz/openbmc_project/Common/error.hpp"
7 #include "xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp"
8 
9 #include <getopt.h>
10 #include <systemd/sd-bus.h>
11 
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 #include <sdbusplus/exception.hpp>
15 #include <sdbusplus/server.hpp>
16 
17 #include <iostream>
18 #include <map>
19 #include <string>
20 
21 namespace phosphor
22 {
23 namespace state
24 {
25 namespace manager
26 {
27 
28 PHOSPHOR_LOG2_USING;
29 
30 using namespace phosphor::logging;
31 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
32 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
33 
34 } // namespace manager
35 } // namespace state
36 } // namespace phosphor
37 
38 int main(int argc, char** argv)
39 {
40     using namespace phosphor::logging;
41 
42     std::string hostPath = "/xyz/openbmc_project/state/host0";
43     int arg;
44     int optIndex = 0;
45 
46     static struct option longOpts[] = {{"host", required_argument, 0, 'h'},
47                                        {0, 0, 0, 0}};
48 
49     while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1)
50     {
51         switch (arg)
52         {
53             case 'h':
54                 hostPath =
55                     std::string("/xyz/openbmc_project/state/host") + optarg;
56                 break;
57             default:
58                 break;
59         }
60     }
61 
62     auto bus = sdbusplus::bus::new_default();
63 
64     using namespace settings;
65     Objects settings(bus);
66 
67     using namespace phosphor::state::manager;
68     namespace server = sdbusplus::xyz::openbmc_project::State::server;
69 
70     // This application is only run if chassis power is off
71 
72     // If the BMC was rebooted due to a user initiated pinhole reset, do not
73     // implement any power restore policies
74     auto bmcRebootCause = phosphor::state::manager::utils::getProperty(
75         bus, "/xyz/openbmc_project/state/bmc0", BMC_BUSNAME, "LastRebootCause");
76     if (bmcRebootCause ==
77         "xyz.openbmc_project.State.BMC.RebootCause.PinholeReset")
78     {
79         info(
80             "BMC was reset due to pinhole reset, no power restore policy will be run");
81         return 0;
82     }
83     else if (bmcRebootCause ==
84              "xyz.openbmc_project.State.BMC.RebootCause.Watchdog")
85     {
86         info(
87             "BMC was reset due to cold reset, no power restore policy will be run");
88         return 0;
89     }
90 
91     /* The logic here is to first check the one-time PowerRestorePolicy setting.
92      * If this property is not the default then look at the persistent
93      * user setting in the non one-time object, otherwise honor the one-time
94      * setting.
95      */
96     auto methodOneTime = bus.new_method_call(
97         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
98         settings.powerRestorePolicyOneTime.c_str(),
99         "org.freedesktop.DBus.Properties", "Get");
100     methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy");
101 
102     auto methodUserSetting = bus.new_method_call(
103         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
104         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
105         "Get");
106     methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy");
107 
108     std::variant<std::string> result;
109     try
110     {
111         auto reply = bus.call(methodOneTime);
112         reply.read(result);
113         auto powerPolicy = std::get<std::string>(result);
114 
115         if (RestorePolicy::Policy::None ==
116             RestorePolicy::convertPolicyFromString(powerPolicy))
117         {
118             // one_time is set to None so use the customer setting
119             info("One time not set, check user setting of power policy");
120             auto reply = bus.call(methodUserSetting);
121             reply.read(result);
122             powerPolicy = std::get<std::string>(result);
123         }
124         else
125         {
126             // one_time setting was set so we're going to use it. Reset it
127             // to default for next time.
128             info("One time set, use it and reset to default");
129             phosphor::state::manager::utils::setProperty(
130                 bus, settings.powerRestorePolicyOneTime.c_str(),
131                 powerRestoreIntf, "PowerRestorePolicy",
132                 convertForMessage(RestorePolicy::Policy::None));
133         }
134 
135         info("Host power is off, processing power policy {POWER_POLICY}",
136              "POWER_POLICY", powerPolicy);
137 
138         if (RestorePolicy::Policy::AlwaysOn ==
139             RestorePolicy::convertPolicyFromString(powerPolicy))
140         {
141             info("power_policy=ALWAYS_POWER_ON, powering host on");
142             phosphor::state::manager::utils::setProperty(
143                 bus, hostPath, HOST_BUSNAME, "RestartCause",
144                 convertForMessage(
145                     server::Host::RestartCause::PowerPolicyAlwaysOn));
146             phosphor::state::manager::utils::setProperty(
147                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
148                 convertForMessage(server::Host::Transition::On));
149         }
150         else if (RestorePolicy::Policy::AlwaysOff ==
151                  RestorePolicy::convertPolicyFromString(powerPolicy))
152         {
153             info("power_policy=ALWAYS_POWER_OFF, set requested state to off");
154             // Read last requested state and re-request it to execute it
155             auto hostReqState = phosphor::state::manager::utils::getProperty(
156                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
157             if (hostReqState == convertForMessage(server::Host::Transition::On))
158             {
159                 phosphor::state::manager::utils::setProperty(
160                     bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
161                     convertForMessage(server::Host::Transition::Off));
162             }
163         }
164         else if (RestorePolicy::Policy::Restore ==
165                  RestorePolicy::convertPolicyFromString(powerPolicy))
166         {
167             info("power_policy=RESTORE, restoring last state");
168             // Read last requested state and re-request it to execute it
169             auto hostReqState = phosphor::state::manager::utils::getProperty(
170                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
171             if (hostReqState == convertForMessage(server::Host::Transition::On))
172             {
173                 phosphor::state::manager::utils::setProperty(
174                     bus, hostPath, HOST_BUSNAME, "RestartCause",
175                     convertForMessage(
176                         server::Host::RestartCause::PowerPolicyPreviousState));
177                 phosphor::state::manager::utils::setProperty(
178                     bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
179                     hostReqState);
180             }
181         }
182     }
183     catch (const sdbusplus::exception::exception& e)
184     {
185         error("Error in PowerRestorePolicy Get: {ERROR}", "ERROR", e);
186         elog<InternalFailure>();
187     }
188 
189     return 0;
190 }
191