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