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