1 #include "config.h"
2 
3 #include "host_state_manager.hpp"
4 #include "settings.hpp"
5 #include "xyz/openbmc_project/Common/error.hpp"
6 #include "xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp"
7 
8 #include <getopt.h>
9 #include <systemd/sd-bus.h>
10 
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <phosphor-logging/lg2.hpp>
13 #include <sdbusplus/exception.hpp>
14 #include <sdbusplus/server.hpp>
15 
16 #include <iostream>
17 #include <map>
18 #include <string>
19 
20 namespace phosphor
21 {
22 namespace state
23 {
24 namespace manager
25 {
26 
27 PHOSPHOR_LOG2_USING;
28 
29 using namespace phosphor::logging;
30 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
31 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
32 
33 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
34 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
35 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
36 
37 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
38 
39 std::string getService(sdbusplus::bus::bus& bus, std::string path,
40                        std::string interface)
41 {
42     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
43                                       MAPPER_INTERFACE, "GetObject");
44 
45     mapper.append(path, std::vector<std::string>({interface}));
46 
47     std::map<std::string, std::vector<std::string>> mapperResponse;
48     try
49     {
50         auto mapperResponseMsg = bus.call(mapper);
51 
52         mapperResponseMsg.read(mapperResponse);
53         if (mapperResponse.empty())
54         {
55             error("Mapper response empty, does not have path {PATH} and "
56                   "interface {INTERFACE}",
57                   "PATH", path, "INTERFACE", interface);
58             throw std::runtime_error("Error reading mapper response");
59         }
60     }
61     catch (const sdbusplus::exception::exception& e)
62     {
63         error("Error in mapper call for path {PATH} and interface {INTERFACE} "
64               "with error {ERROR}",
65               "PATH", path, "INTERFACE", interface, "ERROR", e);
66         throw;
67     }
68 
69     return mapperResponse.begin()->first;
70 }
71 
72 std::string getProperty(sdbusplus::bus::bus& bus, std::string path,
73                         std::string interface, std::string propertyName)
74 {
75     std::variant<std::string> property;
76     std::string service = getService(bus, path, interface);
77 
78     auto method = bus.new_method_call(service.c_str(), path.c_str(),
79                                       PROPERTY_INTERFACE, "Get");
80 
81     method.append(interface, propertyName);
82 
83     try
84     {
85         auto reply = bus.call(method);
86         reply.read(property);
87     }
88     catch (const sdbusplus::exception::exception& e)
89     {
90         error("Error in property Get, error {ERROR}, property {PROPERTY}",
91               "ERROR", e, "PROPERTY", propertyName);
92         throw;
93     }
94 
95     if (std::get<std::string>(property).empty())
96     {
97         error("Error reading property response for {PROPERTY}", "PROPERTY",
98               propertyName);
99         throw std::runtime_error("Error reading property response");
100     }
101 
102     return std::get<std::string>(property);
103 }
104 
105 void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
106                  const std::string& interface, const std::string& property,
107                  const std::string& value)
108 {
109     std::variant<std::string> variantValue = value;
110     std::string service = getService(bus, path, interface);
111 
112     auto method = bus.new_method_call(service.c_str(), path.c_str(),
113                                       PROPERTY_INTERFACE, "Set");
114 
115     method.append(interface, property, variantValue);
116     bus.call_noreply(method);
117 
118     return;
119 }
120 
121 } // namespace manager
122 } // namespace state
123 } // namespace phosphor
124 
125 int main(int argc, char** argv)
126 {
127     using namespace phosphor::logging;
128 
129     std::string hostPath = "/xyz/openbmc_project/state/host0";
130     int arg;
131     int optIndex = 0;
132 
133     static struct option longOpts[] = {{"host", required_argument, 0, 'h'},
134                                        {0, 0, 0, 0}};
135 
136     while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1)
137     {
138         switch (arg)
139         {
140             case 'h':
141                 hostPath =
142                     std::string("/xyz/openbmc_project/state/host") + optarg;
143                 break;
144             default:
145                 break;
146         }
147     }
148 
149     auto bus = sdbusplus::bus::new_default();
150 
151     using namespace settings;
152     Objects settings(bus);
153 
154     using namespace phosphor::state::manager;
155     namespace server = sdbusplus::xyz::openbmc_project::State::server;
156 
157     // This application is only run if chassis power is off
158 
159     /* The logic here is to first check the one-time PowerRestorePolicy setting.
160      * If this property is not the default then look at the persistent
161      * user setting in the non one-time object, otherwise honor the one-time
162      * setting.
163      */
164     auto methodOneTime = bus.new_method_call(
165         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
166         settings.powerRestorePolicyOneTime.c_str(),
167         "org.freedesktop.DBus.Properties", "Get");
168     methodOneTime.append(powerRestoreIntf, "PowerRestorePolicy");
169 
170     auto methodUserSetting = bus.new_method_call(
171         settings.service(settings.powerRestorePolicy, powerRestoreIntf).c_str(),
172         settings.powerRestorePolicy.c_str(), "org.freedesktop.DBus.Properties",
173         "Get");
174     methodUserSetting.append(powerRestoreIntf, "PowerRestorePolicy");
175 
176     std::variant<std::string> result;
177     try
178     {
179         auto reply = bus.call(methodOneTime);
180         reply.read(result);
181         auto powerPolicy = std::get<std::string>(result);
182 
183         if (RestorePolicy::Policy::None ==
184             RestorePolicy::convertPolicyFromString(powerPolicy))
185         {
186             // one_time is set to None so use the customer setting
187             info("One time not set, check user setting of power policy");
188             auto reply = bus.call(methodUserSetting);
189             reply.read(result);
190             powerPolicy = std::get<std::string>(result);
191         }
192         else
193         {
194             // one_time setting was set so we're going to use it. Reset it
195             // to default for next time.
196             info("One time set, use it and reset to default");
197             setProperty(bus, settings.powerRestorePolicyOneTime.c_str(),
198                         powerRestoreIntf, "PowerRestorePolicy",
199                         convertForMessage(RestorePolicy::Policy::None));
200         }
201 
202         info("Host power is off, processing power policy {POWER_POLICY}",
203              "POWER_POLICY", powerPolicy);
204 
205         if (RestorePolicy::Policy::AlwaysOn ==
206             RestorePolicy::convertPolicyFromString(powerPolicy))
207         {
208             info("power_policy=ALWAYS_POWER_ON, powering host on");
209             setProperty(bus, hostPath, HOST_BUSNAME, "RestartCause",
210                         convertForMessage(
211                             server::Host::RestartCause::PowerPolicyAlwaysOn));
212             setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
213                         convertForMessage(server::Host::Transition::On));
214         }
215         else if (RestorePolicy::Policy::Restore ==
216                  RestorePolicy::convertPolicyFromString(powerPolicy))
217         {
218             info("power_policy=RESTORE, restoring last state");
219             setProperty(
220                 bus, hostPath, HOST_BUSNAME, "RestartCause",
221                 convertForMessage(
222                     server::Host::RestartCause::PowerPolicyPreviousState));
223             // Read last requested state and re-request it to execute it
224             auto hostReqState = getProperty(bus, hostPath, HOST_BUSNAME,
225                                             "RequestedHostTransition");
226             setProperty(bus, hostPath, HOST_BUSNAME, "RequestedHostTransition",
227                         hostReqState);
228         }
229     }
230     catch (const sdbusplus::exception::exception& e)
231     {
232         error("Error in PowerRestorePolicy Get: {ERROR}", "ERROR", e);
233         elog<InternalFailure>();
234     }
235 
236     return 0;
237 }
238