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