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
main(int argc,char ** argv)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[] = {{"host", required_argument, 0, 'h'},
51 {0, 0, 0, 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