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