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