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 <fmt/format.h>
10 #include <fmt/printf.h>
11 #include <getopt.h>
12 #include <systemd/sd-bus.h>
13 
14 #include <phosphor-logging/elog-errors.hpp>
15 #include <phosphor-logging/lg2.hpp>
16 #include <sdbusplus/exception.hpp>
17 #include <sdbusplus/server.hpp>
18 #include <xyz/openbmc_project/State/BMC/client.hpp>
19 #include <xyz/openbmc_project/State/Host/client.hpp>
20 
21 #include <filesystem>
22 #include <iostream>
23 #include <map>
24 #include <string>
25 #include <thread>
26 
27 namespace phosphor
28 {
29 namespace state
30 {
31 namespace manager
32 {
33 
34 PHOSPHOR_LOG2_USING;
35 
36 using namespace phosphor::logging;
37 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
38 using namespace sdbusplus::server::xyz::openbmc_project::control::power;
39 
40 } // namespace manager
41 } // namespace state
42 } // namespace phosphor
43 
44 int main(int argc, char** argv)
45 {
46     using namespace phosphor::logging;
47 
48     size_t hostId = 0;
49     int arg;
50     int optIndex = 0;
51 
52     static struct option longOpts[] = {{"host", required_argument, 0, 'h'},
53                                        {0, 0, 0, 0}};
54 
55     while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1)
56     {
57         switch (arg)
58         {
59             case 'h':
60                 hostId = std::stoul(optarg);
61                 break;
62             default:
63                 break;
64         }
65     }
66 
67     using Host = sdbusplus::client::xyz::openbmc_project::state::Host<>;
68     std::string hostPath = std::string(Host::namespace_path::value) + "/" +
69                            std::string(Host::namespace_path::host) +
70                            std::to_string(hostId);
71 
72     auto bus = sdbusplus::bus::new_default();
73 
74     using namespace settings;
75     HostObjects settings(bus, hostId);
76 
77     using namespace phosphor::state::manager;
78     namespace server = sdbusplus::server::xyz::openbmc_project::state;
79 
80     // This application is only run if chassis power is off
81 
82     // If the BMC was rebooted due to a user initiated pinhole reset, do not
83     // implement any power restore policies
84     using BMC = sdbusplus::client::xyz::openbmc_project::state::BMC<>;
85     auto bmcPath = sdbusplus::message::object_path(BMC::namespace_path::value) /
86                    BMC::namespace_path::bmc;
87 
88     auto bmcRebootCause =
89         sdbusplus::message::convert_from_string<BMC::RebootCause>(
90             phosphor::state::manager::utils::getProperty(
91                 bus, bmcPath.str.c_str(), BMC_BUSNAME, "LastRebootCause"));
92 
93     if (bmcRebootCause == BMC::RebootCause::PinholeReset)
94     {
95         info(
96             "BMC was reset due to pinhole reset, no power restore policy will be run");
97         return 0;
98     }
99     else if (bmcRebootCause == BMC::RebootCause::Watchdog)
100     {
101         info(
102             "BMC was reset due to cold reset, no power restore policy will be run");
103         return 0;
104     }
105 
106     /* The logic here is to first check the one-time PowerRestorePolicy setting.
107      * If this property is not the default then look at the persistent
108      * user setting in the non one-time object, otherwise honor the one-time
109      * setting.
110      */
111     auto methodOneTime = bus.new_method_call(
112         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
113         settings.powerRestorePolicyOneTime.c_str(),
114         "org.freedesktop.DBus.Properties", "Get");
115     methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy");
116 
117     auto methodUserSetting = bus.new_method_call(
118         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
119         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
120         "Get");
121     methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy");
122 
123     std::variant<std::string> result;
124     try
125     {
126         auto reply = bus.call(methodOneTime);
127         reply.read(result);
128         auto powerPolicy = std::get<std::string>(result);
129 
130         if (RestorePolicy::Policy::None ==
131             RestorePolicy::convertPolicyFromString(powerPolicy))
132         {
133             // one_time is set to None so use the customer setting
134             info("One time not set, check user setting of power policy");
135 
136             auto reply = bus.call(methodUserSetting);
137             reply.read(result);
138             powerPolicy = std::get<std::string>(result);
139         }
140         else
141         {
142             // one_time setting was set so we're going to use it. Reset it
143             // to default for next time.
144             info("One time set, use it and reset to default");
145             phosphor::state::manager::utils::setProperty(
146                 bus, settings.powerRestorePolicyOneTime.c_str(),
147                 powerRestoreIntf, "PowerRestorePolicy",
148                 convertForMessage(RestorePolicy::Policy::None));
149         }
150 
151         auto methodUserSettingDelay = bus.new_method_call(
152             settings.service(settings.powerRestorePolicy, powerRestoreIntf)
153                 .c_str(),
154             settings.powerRestorePolicy.c_str(),
155             "org.freedesktop.DBus.Properties", "Get");
156 
157         methodUserSettingDelay.append(powerRestoreIntf, "PowerRestoreDelay");
158 
159         std::variant<uint64_t> restoreDelay;
160 
161         auto delayResult = bus.call(methodUserSettingDelay);
162         delayResult.read(restoreDelay);
163         auto powerRestoreDelayUsec =
164             std::chrono::microseconds(std::get<uint64_t>(restoreDelay));
165         auto powerRestoreDelaySec =
166             std::chrono::duration_cast<std::chrono::seconds>(
167                 powerRestoreDelayUsec);
168 
169         info("Host power is off, processing power policy {POWER_POLICY}",
170              "POWER_POLICY", powerPolicy);
171 
172         if (RestorePolicy::Policy::AlwaysOn ==
173             RestorePolicy::convertPolicyFromString(powerPolicy))
174         {
175             info(
176                 "power_policy=ALWAYS_POWER_ON, powering host on ({DELAY}s delay)",
177                 "DELAY", powerRestoreDelaySec.count());
178             utils::waitBmcReady(bus, powerRestoreDelaySec);
179             phosphor::state::manager::utils::setProperty(
180                 bus, hostPath, HOST_BUSNAME, "RestartCause",
181                 convertForMessage(
182                     server::Host::RestartCause::PowerPolicyAlwaysOn));
183             phosphor::state::manager::utils::setProperty(
184                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
185                 convertForMessage(server::Host::Transition::On));
186         }
187         // Always execute power on if AlwaysOn is set, otherwise check config
188         // option (and AC loss status) on whether to execute other policy
189         // settings
190 #if ONLY_RUN_APR_ON_POWER_LOSS
191         else if (!phosphor::state::manager::utils::checkACLoss(hostId))
192         {
193             info(
194                 "Chassis power was not on prior to BMC reboot so do not run any further power policy");
195             return 0;
196         }
197 #endif
198         else if (RestorePolicy::Policy::AlwaysOff ==
199                  RestorePolicy::convertPolicyFromString(powerPolicy))
200         {
201             info(
202                 "power_policy=ALWAYS_POWER_OFF, set requested state to off ({DELAY}s delay)",
203                 "DELAY", powerRestoreDelaySec.count());
204             utils::waitBmcReady(bus, powerRestoreDelaySec);
205             // Read last requested state and re-request it to execute it
206             auto hostReqState = phosphor::state::manager::utils::getProperty(
207                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
208             if (hostReqState !=
209                 convertForMessage(server::Host::Transition::Off))
210             {
211                 phosphor::state::manager::utils::setProperty(
212                     bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
213                     convertForMessage(server::Host::Transition::Off));
214             }
215         }
216         else if (RestorePolicy::Policy::Restore ==
217                  RestorePolicy::convertPolicyFromString(powerPolicy))
218         {
219             info("power_policy=RESTORE, restoring last state ({DELAY}s delay)",
220                  "DELAY", powerRestoreDelaySec.count());
221             utils::waitBmcReady(bus, powerRestoreDelaySec);
222             // Read last requested state and re-request it to execute it
223             auto hostReqState = phosphor::state::manager::utils::getProperty(
224                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
225 
226             // As long as the host transition is not 'Off' power on host state.
227             if (hostReqState !=
228                 convertForMessage(server::Host::Transition::Off))
229             {
230                 phosphor::state::manager::utils::setProperty(
231                     bus, hostPath, HOST_BUSNAME, "RestartCause",
232                     convertForMessage(
233                         server::Host::RestartCause::PowerPolicyPreviousState));
234                 phosphor::state::manager::utils::setProperty(
235                     bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
236                     convertForMessage(server::Host::Transition::On));
237             }
238         }
239     }
240     catch (const sdbusplus::exception_t& e)
241     {
242         error("Error in PowerRestorePolicy Get: {ERROR}", "ERROR", e);
243         elog<InternalFailure>();
244     }
245 
246     return 0;
247 }
248