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, std::string path,
93                  std::string interface, std::string property, std::string value)
94 {
95     sdbusplus::message::variant<std::string> variantValue = value;
96     std::string service = getService(bus, path, interface);
97 
98     auto method = bus.new_method_call(service.c_str(), path.c_str(),
99                                       PROPERTY_INTERFACE, "Set");
100 
101     method.append(interface, property, variantValue);
102     bus.call_noreply(method);
103 
104     return;
105 }
106 
107 } // namespace manager
108 } // namespace state
109 } // namespace phosphor
110 
111 int main(int argc, char** argv)
112 {
113     using namespace phosphor::logging;
114 
115     std::string hostPath = "/xyz/openbmc_project/state/host0";
116     int arg;
117     int optIndex = 0;
118 
119     static struct option longOpts[] = {{"host", required_argument, 0, 'h'},
120                                        {0, 0, 0, 0}};
121 
122     while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1)
123     {
124         switch (arg)
125         {
126             case 'h':
127                 hostPath =
128                     std::string("/xyz/openbmc_project/state/host") + optarg;
129                 break;
130             default:
131                 break;
132         }
133     }
134 
135     auto bus = sdbusplus::bus::new_default();
136 
137     using namespace settings;
138     Objects settings(bus);
139 
140     using namespace phosphor::state::manager;
141     namespace server = sdbusplus::xyz::openbmc_project::State::server;
142 
143     // This application is only run if chassis power is off
144 
145     auto method = bus.new_method_call(
146         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
147         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
148         "Get");
149     method.append(powerRestoreIntf, "PowerRestorePolicy");
150     auto reply = bus.call(method);
151     if (reply.is_method_error())
152     {
153         log<level::ERR>("Error in PowerRestorePolicy Get");
154         elog<InternalFailure>();
155     }
156 
157     sdbusplus::message::variant<std::string> result;
158     reply.read(result);
159     auto powerPolicy = result.get<std::string>();
160 
161     log<level::INFO>("Host power is off, checking power policy",
162                      entry("POWER_POLICY=%s", powerPolicy.c_str()));
163 
164     if (RestorePolicy::Policy::AlwaysOn ==
165         RestorePolicy::convertPolicyFromString(powerPolicy))
166     {
167         log<level::INFO>("power_policy=ALWAYS_POWER_ON, powering host on");
168         setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
169                     convertForMessage(server::Host::Transition::On));
170     }
171     else if (RestorePolicy::Policy::Restore ==
172              RestorePolicy::convertPolicyFromString(powerPolicy))
173     {
174         log<level::INFO>("power_policy=RESTORE, restoring last state");
175 
176         // Read last requested state and re-request it to execute it
177         auto hostReqState =
178             getProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
179         setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
180                     hostReqState);
181     }
182 
183     return 0;
184 }
185