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