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