1 #include "utils.hpp"
2 
3 #include <systemd/sd-event.h>
4 #include <unistd.h>
5 
6 #include <phosphor-logging/elog-errors.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
10 #include <xyz/openbmc_project/State/Host/server.hpp>
11 
12 #include <format>
13 #include <string>
14 namespace open_power
15 {
16 namespace occ
17 {
18 namespace utils
19 {
20 // For throwing exceptions
21 using namespace phosphor::logging;
22 using InternalFailure =
23     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
24 
25 using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::server::
26     Progress::ProgressStages;
27 constexpr auto HOST_STATE_OBJ_PATH = "/xyz/openbmc_project/state/host0";
28 
29 const std::string getService(const std::string& path,
30                              const std::string& interface)
31 {
32     using InterfaceList = std::vector<std::string>;
33     std::map<std::string, std::vector<std::string>> mapperResponse;
34 
35     auto& bus = getBus();
36 
37     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
38                                       MAPPER_IFACE, "GetObject");
39     mapper.append(path, InterfaceList({interface}));
40 
41     auto mapperResponseMsg = bus.call(mapper);
42     mapperResponseMsg.read(mapperResponse);
43     if (mapperResponse.empty())
44     {
45         log<level::ERR>("ERROR reading mapper response",
46                         entry("PATH=%s", path.c_str()),
47                         entry("INTERFACE=%s", interface.c_str()));
48 
49         elog<InternalFailure>();
50     }
51 
52     // the value here will be the service name
53     return mapperResponse.cbegin()->first;
54 }
55 
56 const PropertyValue getProperty(const std::string& objectPath,
57                                 const std::string& interface,
58                                 const std::string& propertyName)
59 {
60     PropertyValue value{};
61 
62     auto& bus = getBus();
63     auto service = getService(objectPath, interface);
64     if (service.empty())
65     {
66         return value;
67     }
68 
69     auto method = bus.new_method_call(service.c_str(), objectPath.c_str(),
70                                       DBUS_PROPERTY_IFACE, "Get");
71     method.append(interface, propertyName);
72 
73     auto reply = bus.call(method);
74     reply.read(value);
75 
76     return value;
77 }
78 
79 /**
80  * @brief Sets a given object's property value
81  *
82  * @param[in] objectPath - Name of the object containing the property
83  * @param[in] interface - Interface name containing the property
84  * @param[in] propertyName - Property name
85  * @param[in] value - Property value
86  */
87 void setProperty(const std::string& objectPath, const std::string& interface,
88                  const std::string& propertyName, PropertyValue&& value)
89 {
90     using namespace std::literals::string_literals;
91     PropertyValue varValue(std::forward<PropertyValue>(value));
92 
93     try
94     {
95         auto& bus = getBus();
96         auto service = getService(objectPath, interface);
97         if (service.empty())
98         {
99             return;
100         }
101 
102         auto method = bus.new_method_call(service.c_str(), objectPath.c_str(),
103                                           DBUS_PROPERTY_IFACE, "Set");
104         method.append(interface, propertyName, varValue);
105 
106         auto reply = bus.call(method);
107         if (reply.is_method_error())
108         {
109             log<level::ERR>(
110                 std::format("util::setProperty: Failed to set property {}",
111                             propertyName)
112                     .c_str());
113         }
114     }
115     catch (const std::exception& e)
116     {
117         auto error = errno;
118         log<level::ERR>(
119             std::format("setProperty: failed to Set {}, errno={}, what={}",
120                         propertyName.c_str(), error, e.what())
121                 .c_str());
122     }
123 }
124 
125 std::vector<std::string>
126     getSubtreePaths(const std::vector<std::string>& interfaces,
127                     const std::string& path)
128 {
129     std::vector<std::string> paths;
130 
131     auto& bus = getBus();
132     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
133                                       MAPPER_IFACE, "GetSubTreePaths");
134     method.append(path, 0, interfaces);
135 
136     auto reply = bus.call(method);
137     reply.read(paths);
138 
139     return paths;
140 }
141 
142 // Get the service and object path for an interface
143 std::string getServiceUsingSubTree(const std::string& interface,
144                                    std::string& path)
145 {
146     using Path = std::string;
147     using Intf = std::string;
148     using Serv = std::string;
149     using Intfs = std::vector<Intf>;
150     using Objects = std::map<Path, std::map<Serv, Intfs>>;
151     Serv service;
152     Objects rspObjects;
153 
154     auto& bus = getBus();
155     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
156                                       MAPPER_IFACE, "GetSubTree");
157     method.append(path, 0, std::vector{interface});
158 
159     auto mapperResponseMsg = bus.call(method);
160     mapperResponseMsg.read(rspObjects);
161     if (rspObjects.empty())
162     {
163         log<level::ERR>(
164             std::format(
165                 "util::getServiceUsingSubTree: Failed getSubTree({},0,{})",
166                 path.c_str(), interface)
167                 .c_str());
168     }
169     else
170     {
171         path = rspObjects.begin()->first;
172         if (!rspObjects.begin()->second.empty())
173         {
174             service = rspObjects.begin()->second.begin()->first;
175         }
176         else
177         {
178             log<level::ERR>(
179                 std::format(
180                     "getServiceUsingSubTree: service not found for interface {} (path={})",
181                     interface, path.c_str())
182                     .c_str());
183         }
184     }
185 
186     return service;
187 }
188 
189 std::string getStateValue(const std::string& intf, const std::string& objPath,
190                           const std::string& state)
191 {
192     std::string stateVal;
193     try
194     {
195         auto& bus = getBus();
196         auto service = getService(objPath, intf);
197         if (service.empty())
198         {
199             throw std::runtime_error("getStateValue: Failed to get service");
200         }
201 
202         auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
203                                           "org.freedesktop.DBus.Properties",
204                                           "Get");
205 
206         method.append(intf, state);
207 
208         auto reply = bus.call(method);
209 
210         std::variant<std::string> propertyVal;
211 
212         reply.read(propertyVal);
213 
214         stateVal = std::get<std::string>(propertyVal);
215     }
216     catch (const sdbusplus::exception_t& e)
217     {
218         log<level::ERR>(std::format("D-Bus call exception, OBJPATH({}), "
219                                     "INTERFACE({}), PROPERTY({}) EXCEPTION({})",
220                                     objPath, intf, state, e.what())
221                             .c_str());
222         throw std::runtime_error("Failed to get host state property");
223     }
224     catch (const std::bad_variant_access& e)
225     {
226         log<level::ERR>(
227             std::format("Exception raised while read host state({}) property "
228                         "value,  OBJPATH({}), INTERFACE({}), EXCEPTION({})",
229                         state, objPath, intf, e.what())
230                 .c_str());
231         throw std::runtime_error("Failed to get host state property");
232     }
233 
234     return stateVal;
235 }
236 
237 BootProgress getBootProgress()
238 {
239     BootProgress bootProgessStage;
240     constexpr auto bootProgressInterface =
241         "xyz.openbmc_project.State.Boot.Progress";
242     std::string value = getStateValue(bootProgressInterface,
243                                       HOST_STATE_OBJ_PATH, "BootProgress");
244     bootProgessStage = sdbusplus::xyz::openbmc_project::State::Boot::server::
245         Progress::convertProgressStagesFromString(value);
246     return bootProgessStage;
247 }
248 
249 bool isHostRunning()
250 {
251     BootProgress bootProgressStatus = getBootProgress();
252     if ((bootProgressStatus == BootProgress::SystemInitComplete) ||
253         (bootProgressStatus == BootProgress::SystemSetup) ||
254         (bootProgressStatus == BootProgress::OSStart) ||
255         (bootProgressStatus == BootProgress::OSRunning))
256     {
257         return true;
258     }
259     return false;
260 }
261 
262 } // namespace utils
263 } // namespace occ
264 } // namespace open_power
265