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