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