xref: /openbmc/phosphor-state-manager/discover_system_state.cpp (revision 25f1b891d57d2adedc97e75faab7d96741b4b7fe)
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