1 #include "config.h"
2 
3 #include "host_state_manager.hpp"
4 #include "settings.hpp"
5 #include "xyz/openbmc_project/Common/error.hpp"
6 #include "xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp"
7 
8 #include <getopt.h>
9 #include <systemd/sd-bus.h>
10 
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <phosphor-logging/log.hpp>
13 #include <sdbusplus/exception.hpp>
14 #include <sdbusplus/server.hpp>
15 
16 #include <iostream>
17 #include <map>
18 #include <string>
19 
20 namespace phosphor
21 {
22 namespace state
23 {
24 namespace manager
25 {
26 
27 using namespace phosphor::logging;
28 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
29 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
30 using sdbusplus::exception::SdBusError;
31 
32 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
33 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
34 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
35 
36 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
37 
38 std::string getService(sdbusplus::bus::bus& bus, std::string path,
39                        std::string interface)
40 {
41     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
42                                       MAPPER_INTERFACE, "GetObject");
43 
44     mapper.append(path, std::vector<std::string>({interface}));
45 
46     std::map<std::string, std::vector<std::string>> mapperResponse;
47     try
48     {
49         auto mapperResponseMsg = bus.call(mapper);
50 
51         mapperResponseMsg.read(mapperResponse);
52         if (mapperResponse.empty())
53         {
54             log<level::ERR>("Error reading mapper response",
55                             entry("PATH=%s", path.c_str()),
56                             entry("INTERFACE=%s", interface.c_str()));
57             throw std::runtime_error("Error reading mapper response");
58         }
59     }
60     catch (const SdBusError& e)
61     {
62         log<level::ERR>("Error in mapper call", entry("ERROR=%s", e.what()),
63                         entry("PATH=%s", path.c_str()),
64                         entry("INTERFACE=%s", interface.c_str()));
65         throw;
66     }
67 
68     return mapperResponse.begin()->first;
69 }
70 
71 std::string getProperty(sdbusplus::bus::bus& bus, std::string path,
72                         std::string interface, std::string propertyName)
73 {
74     std::variant<std::string> property;
75     std::string service = getService(bus, path, interface);
76 
77     auto method = bus.new_method_call(service.c_str(), path.c_str(),
78                                       PROPERTY_INTERFACE, "Get");
79 
80     method.append(interface, propertyName);
81 
82     try
83     {
84         auto reply = bus.call(method);
85         reply.read(property);
86     }
87     catch (const SdBusError& e)
88     {
89         log<level::ERR>("Error in property Get", entry("ERROR=%s", e.what()),
90                         entry("PROPERTY=%s", propertyName.c_str()));
91         throw;
92     }
93 
94     if (std::get<std::string>(property).empty())
95     {
96         log<level::ERR>("Error reading property response",
97                         entry("PROPERTY=%s", propertyName.c_str()));
98         throw std::runtime_error("Error reading property response");
99     }
100 
101     return std::get<std::string>(property);
102 }
103 
104 void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
105                  const std::string& interface, const std::string& property,
106                  const std::string& value)
107 {
108     std::variant<std::string> variantValue = value;
109     std::string service = getService(bus, path, interface);
110 
111     auto method = bus.new_method_call(service.c_str(), path.c_str(),
112                                       PROPERTY_INTERFACE, "Set");
113 
114     method.append(interface, property, variantValue);
115     bus.call_noreply(method);
116 
117     return;
118 }
119 
120 } // namespace manager
121 } // namespace state
122 } // namespace phosphor
123 
124 int main(int argc, char** argv)
125 {
126     using namespace phosphor::logging;
127 
128     std::string hostPath = "/xyz/openbmc_project/state/host0";
129     int arg;
130     int optIndex = 0;
131 
132     static struct option longOpts[] = {{"host", required_argument, 0, 'h'},
133                                        {0, 0, 0, 0}};
134 
135     while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1)
136     {
137         switch (arg)
138         {
139             case 'h':
140                 hostPath =
141                     std::string("/xyz/openbmc_project/state/host") + optarg;
142                 break;
143             default:
144                 break;
145         }
146     }
147 
148     auto bus = sdbusplus::bus::new_default();
149 
150     using namespace settings;
151     Objects settings(bus);
152 
153     using namespace phosphor::state::manager;
154     namespace server = sdbusplus::xyz::openbmc_project::State::server;
155 
156     // This application is only run if chassis power is off
157 
158     /* The logic here is to first check the one-time PowerRestorePolicy setting.
159      * If this property is not the default then look at the persistent
160      * user setting in the non one-time object, otherwise honor the one-time
161      * setting.
162      */
163     auto methodOneTime = bus.new_method_call(
164         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
165         settings.powerRestorePolicyOneTime.c_str(),
166         "org.freedesktop.DBus.Properties", "Get");
167     methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy");
168 
169     auto methodUserSetting = bus.new_method_call(
170         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
171         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
172         "Get");
173     methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy");
174 
175     std::variant<std::string> result;
176     try
177     {
178         auto reply = bus.call(methodOneTime);
179         reply.read(result);
180         auto powerPolicy = std::get<std::string>(result);
181 
182         if (RestorePolicy::Policy::None ==
183             RestorePolicy::convertPolicyFromString(powerPolicy))
184         {
185             // one_time is set to None so use the customer setting
186             log<level::INFO>(
187                 "One time not set, check user setting of power policy");
188             auto reply = bus.call(methodUserSetting);
189             reply.read(result);
190             powerPolicy = std::get<std::string>(result);
191         }
192         else
193         {
194             // one_time setting was set so we're going to use it. Reset it
195             // to default for next time.
196             log<level::INFO>("One time set, use it and reset to default");
197             setProperty(bus, settings.powerRestorePolicyOneTime.c_str(),
198                         powerRestoreIntf, "PowerRestorePolicy",
199                         convertForMessage(RestorePolicy::Policy::None));
200         }
201 
202         log<level::INFO>("Host power is off, processing power policy",
203                          entry("POWER_POLICY=%s", powerPolicy.c_str()));
204 
205         if (RestorePolicy::Policy::AlwaysOn ==
206             RestorePolicy::convertPolicyFromString(powerPolicy))
207         {
208             log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on");
209             setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
210                         convertForMessage(server::Host::Transition::On));
211         }
212         else if (RestorePolicy::Policy::Restore ==
213                  RestorePolicy::convertPolicyFromString(powerPolicy))
214         {
215             log<level::INFO>("power_policy=RESTORE, restoring last state");
216 
217             // Read last requested state and re-request it to execute it
218             auto hostReqState = getProperty(bus, hostPath, HOST_BUSNAME,
219                                             "RequestedHostTransition");
220             setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
221                         hostReqState);
222         }
223     }
224     catch (const SdBusError& e)
225     {
226         log<level::ERR>("Error in PowerRestorePolicy Get",
227                         entry("ERROR=%s", e.what()));
228         elog<InternalFailure>();
229     }
230 
231     return 0;
232 }
233