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