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