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 <fmt/format.h>
10 #include <fmt/printf.h>
11 #include <getopt.h>
12 #include <systemd/sd-bus.h>
13 
14 #include <phosphor-logging/elog-errors.hpp>
15 #include <phosphor-logging/lg2.hpp>
16 #include <sdbusplus/exception.hpp>
17 #include <sdbusplus/server.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::xyz::openbmc_project::Control::Power::server;
37 
38 } // namespace manager
39 } // namespace state
40 } // namespace phosphor
41 
42 int main(int argc, char** argv)
43 {
44     using namespace phosphor::logging;
45 
46     size_t hostId = 0;
47     std::string hostPath = "/xyz/openbmc_project/state/host0";
48     int arg;
49     int optIndex = 0;
50 
51     static struct option longOpts[] = {{"host", required_argument, 0, 'h'},
52                                        {0, 0, 0, 0}};
53 
54     while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1)
55     {
56         switch (arg)
57         {
58             case 'h':
59                 hostId = std::stoul(optarg);
60                 hostPath =
61                     std::string("/xyz/openbmc_project/state/host") + optarg;
62                 break;
63             default:
64                 break;
65         }
66     }
67 
68     auto bus = sdbusplus::bus::new_default();
69 
70     using namespace settings;
71     HostObjects settings(bus, hostId);
72 
73     using namespace phosphor::state::manager;
74     namespace server = sdbusplus::xyz::openbmc_project::State::server;
75 
76     // This application is only run if chassis power is off
77 
78     // If the BMC was rebooted due to a user initiated pinhole reset, do not
79     // implement any power restore policies
80     auto bmcRebootCause = phosphor::state::manager::utils::getProperty(
81         bus, "/xyz/openbmc_project/state/bmc0", BMC_BUSNAME, "LastRebootCause");
82     if (bmcRebootCause ==
83         "xyz.openbmc_project.State.BMC.RebootCause.PinholeReset")
84     {
85         info(
86             "BMC was reset due to pinhole reset, no power restore policy will be run");
87         return 0;
88     }
89     else if (bmcRebootCause ==
90              "xyz.openbmc_project.State.BMC.RebootCause.Watchdog")
91     {
92         info(
93             "BMC was reset due to cold reset, no power restore policy will be run");
94         return 0;
95     }
96 
97     /* The logic here is to first check the one-time PowerRestorePolicy setting.
98      * If this property is not the default then look at the persistent
99      * user setting in the non one-time object, otherwise honor the one-time
100      * setting.
101      */
102     auto methodOneTime = bus.new_method_call(
103         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
104         settings.powerRestorePolicyOneTime.c_str(),
105         "org.freedesktop.DBus.Properties", "Get");
106     methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy");
107 
108     auto methodUserSetting = bus.new_method_call(
109         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
110         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
111         "Get");
112     methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy");
113 
114     std::variant<std::string> result;
115     try
116     {
117         auto reply = bus.call(methodOneTime);
118         reply.read(result);
119         auto powerPolicy = std::get<std::string>(result);
120 
121         if (RestorePolicy::Policy::None ==
122             RestorePolicy::convertPolicyFromString(powerPolicy))
123         {
124             // one_time is set to None so use the customer setting
125             info("One time not set, check user setting of power policy");
126 
127             auto reply = bus.call(methodUserSetting);
128             reply.read(result);
129             powerPolicy = std::get<std::string>(result);
130         }
131         else
132         {
133             // one_time setting was set so we're going to use it. Reset it
134             // to default for next time.
135             info("One time set, use it and reset to default");
136             phosphor::state::manager::utils::setProperty(
137                 bus, settings.powerRestorePolicyOneTime.c_str(),
138                 powerRestoreIntf, "PowerRestorePolicy",
139                 convertForMessage(RestorePolicy::Policy::None));
140         }
141 
142         auto methodUserSettingDelay = bus.new_method_call(
143             settings.service(settings.powerRestorePolicy, powerRestoreIntf)
144                 .c_str(),
145             settings.powerRestorePolicy.c_str(),
146             "org.freedesktop.DBus.Properties", "Get");
147 
148         methodUserSettingDelay.append(powerRestoreIntf, "PowerRestoreDelay");
149 
150         std::variant<uint64_t> restoreDelay;
151 
152         auto delayResult = bus.call(methodUserSettingDelay);
153         delayResult.read(restoreDelay);
154         auto powerRestoreDelayUsec =
155             std::chrono::microseconds(std::get<uint64_t>(restoreDelay));
156         auto powerRestoreDelaySec =
157             std::chrono::duration_cast<std::chrono::seconds>(
158                 powerRestoreDelayUsec);
159 
160         info("Host power is off, processing power policy {POWER_POLICY}",
161              "POWER_POLICY", powerPolicy);
162 
163         if (RestorePolicy::Policy::AlwaysOn ==
164             RestorePolicy::convertPolicyFromString(powerPolicy))
165         {
166             info(
167                 "power_policy=ALWAYS_POWER_ON, powering host on ({DELAY}s delay)",
168                 "DELAY", powerRestoreDelaySec.count());
169             std::this_thread::sleep_for(powerRestoreDelayUsec);
170             phosphor::state::manager::utils::setProperty(
171                 bus, hostPath, HOST_BUSNAME, "RestartCause",
172                 convertForMessage(
173                     server::Host::RestartCause::PowerPolicyAlwaysOn));
174             phosphor::state::manager::utils::setProperty(
175                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
176                 convertForMessage(server::Host::Transition::On));
177         }
178         // Always execute power on if AlwaysOn is set, otherwise check config
179         // option (and AC loss status) on whether to execute other policy
180         // settings
181 #if ONLY_RUN_APR_ON_POWER_LOSS
182         else if (!phosphor::state::manager::utils::checkACLoss(hostId))
183         {
184             info(
185                 "Chassis power was not on prior to BMC reboot so do not run any further power policy");
186             return 0;
187         }
188 #endif
189         else if (RestorePolicy::Policy::AlwaysOff ==
190                  RestorePolicy::convertPolicyFromString(powerPolicy))
191         {
192             info(
193                 "power_policy=ALWAYS_POWER_OFF, set requested state to off ({DELAY}s delay)",
194                 "DELAY", powerRestoreDelaySec.count());
195             std::this_thread::sleep_for(powerRestoreDelayUsec);
196             // Read last requested state and re-request it to execute it
197             auto hostReqState = phosphor::state::manager::utils::getProperty(
198                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
199             if (hostReqState !=
200                 convertForMessage(server::Host::Transition::Off))
201             {
202                 phosphor::state::manager::utils::setProperty(
203                     bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
204                     convertForMessage(server::Host::Transition::Off));
205             }
206         }
207         else if (RestorePolicy::Policy::Restore ==
208                  RestorePolicy::convertPolicyFromString(powerPolicy))
209         {
210             info("power_policy=RESTORE, restoring last state ({DELAY}s delay)",
211                  "DELAY", powerRestoreDelaySec.count());
212             std::this_thread::sleep_for(powerRestoreDelayUsec);
213             // Read last requested state and re-request it to execute it
214             auto hostReqState = phosphor::state::manager::utils::getProperty(
215                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
216 
217             // As long as the host transition is not 'Off' power on host state.
218             if (hostReqState !=
219                 convertForMessage(server::Host::Transition::Off))
220             {
221                 phosphor::state::manager::utils::setProperty(
222                     bus, hostPath, HOST_BUSNAME, "RestartCause",
223                     convertForMessage(
224                         server::Host::RestartCause::PowerPolicyPreviousState));
225                 phosphor::state::manager::utils::setProperty(
226                     bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
227                     convertForMessage(server::Host::Transition::On));
228             }
229         }
230     }
231     catch (const sdbusplus::exception_t& e)
232     {
233         error("Error in PowerRestorePolicy Get: {ERROR}", "ERROR", e);
234         elog<InternalFailure>();
235     }
236 
237     return 0;
238 }
239