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     auto method = bus.new_method_call(
159         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
160         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
161         "Get");
162     method.append(powerRestoreIntf, "PowerRestorePolicy");
163 
164     std::variant<std::string> result;
165     try
166     {
167         auto reply = bus.call(method);
168         reply.read(result);
169     }
170     catch (const SdBusError& e)
171     {
172         log<level::ERR>("Error in PowerRestorePolicy Get",
173                         entry("ERROR=%s", e.what()));
174         elog<InternalFailure>();
175     }
176 
177     auto powerPolicy = std::get<std::string>(result);
178 
179     log<level::INFO>("Host power is off, checking power policy",
180                      entry("POWER_POLICY=%s", powerPolicy.c_str()));
181 
182     if (RestorePolicy::Policy::AlwaysOn ==
183         RestorePolicy::convertPolicyFromString(powerPolicy))
184     {
185         log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on");
186         setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
187                     convertForMessage(server::Host::Transition::On));
188     }
189     else if (RestorePolicy::Policy::Restore ==
190              RestorePolicy::convertPolicyFromString(powerPolicy))
191     {
192         log<level::INFO>("power_policy=RESTORE, restoring last state");
193 
194         // Read last requested state and re-request it to execute it
195         auto hostReqState =
196             getProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
197         setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
198                     hostReqState);
199     }
200 
201     return 0;
202 }
203