1 #include "config.h"
2 
3 #include "utils.hpp"
4 
5 #include <fmt/format.h>
6 #include <fmt/printf.h>
7 #include <gpiod.h>
8 
9 #include <phosphor-logging/lg2.hpp>
10 #include <xyz/openbmc_project/Dump/Create/client.hpp>
11 #include <xyz/openbmc_project/Logging/Create/client.hpp>
12 #include <xyz/openbmc_project/ObjectMapper/client.hpp>
13 #include <xyz/openbmc_project/State/BMC/client.hpp>
14 
15 #include <chrono>
16 #include <filesystem>
17 
18 namespace phosphor
19 {
20 namespace state
21 {
22 namespace manager
23 {
24 namespace utils
25 {
26 
27 using namespace std::literals::chrono_literals;
28 
29 PHOSPHOR_LOG2_USING;
30 
31 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
32 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
33 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
34 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
35 
36 using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
37 
38 void subscribeToSystemdSignals(sdbusplus::bus_t& bus)
39 {
40     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
41                                       SYSTEMD_INTERFACE, "Subscribe");
42 
43     try
44     {
45         // On OpenBMC based systems, systemd has had a few situations where it
46         // has been unable to respond to this call within the default d-bus
47         // timeout of 25 seconds. This is due to the large amount of work being
48         // done by systemd during OpenBMC startup. Set the timeout for this call
49         // to 60 seconds (worst case seen was around 30s so double it).
50         bus.call(method, 60s);
51     }
52     catch (const sdbusplus::exception_t& e)
53     {
54         error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
55         throw std::runtime_error("Unable to subscribe to systemd signals");
56     }
57     return;
58 }
59 
60 std::string getService(sdbusplus::bus_t& bus, std::string path,
61                        std::string interface)
62 {
63     auto mapper = bus.new_method_call(ObjectMapper::default_service,
64                                       ObjectMapper::instance_path,
65                                       ObjectMapper::interface, "GetObject");
66 
67     mapper.append(path, std::vector<std::string>({interface}));
68 
69     std::vector<std::pair<std::string, std::vector<std::string>>>
70         mapperResponse;
71 
72     try
73     {
74         auto mapperResponseMsg = bus.call(mapper);
75 
76         mapperResponseMsg.read(mapperResponse);
77         if (mapperResponse.empty())
78         {
79             error(
80                 "Error no matching service with path {PATH} and interface {INTERFACE}",
81                 "PATH", path, "INTERFACE", interface);
82             throw std::runtime_error("Error no matching service");
83         }
84     }
85     catch (const sdbusplus::exception_t& e)
86     {
87         error("Error in mapper call with path {PATH}, interface "
88               "{INTERFACE}, and exception {ERROR}",
89               "PATH", path, "INTERFACE", interface, "ERROR", e);
90         throw;
91     }
92 
93     return mapperResponse.begin()->first;
94 }
95 
96 std::string getProperty(sdbusplus::bus_t& bus, const std::string& path,
97                         const std::string& interface,
98                         const std::string& propertyName)
99 {
100     std::variant<std::string> property;
101     std::string service = getService(bus, path, interface);
102 
103     auto method = bus.new_method_call(service.c_str(), path.c_str(),
104                                       PROPERTY_INTERFACE, "Get");
105 
106     method.append(interface, propertyName);
107 
108     try
109     {
110         auto reply = bus.call(method);
111         reply.read(property);
112     }
113     catch (const sdbusplus::exception_t& e)
114     {
115         error("Error in property Get, error {ERROR}, property {PROPERTY}",
116               "ERROR", e, "PROPERTY", propertyName);
117         throw;
118     }
119 
120     if (std::get<std::string>(property).empty())
121     {
122         error("Error reading property response for {PROPERTY}", "PROPERTY",
123               propertyName);
124         throw std::runtime_error("Error reading property response");
125     }
126 
127     return std::get<std::string>(property);
128 }
129 
130 void setProperty(sdbusplus::bus_t& bus, const std::string& path,
131                  const std::string& interface, const std::string& property,
132                  const std::string& value)
133 {
134     std::variant<std::string> variantValue = value;
135     std::string service = getService(bus, path, interface);
136 
137     auto method = bus.new_method_call(service.c_str(), path.c_str(),
138                                       PROPERTY_INTERFACE, "Set");
139 
140     method.append(interface, property, variantValue);
141     bus.call_noreply(method);
142 
143     return;
144 }
145 
146 int getGpioValue(const std::string& gpioName)
147 {
148     int gpioval = -1;
149     gpiod_line* line = gpiod_line_find(gpioName.c_str());
150 
151     if (nullptr != line)
152     {
153         // take ownership of gpio
154         if (0 != gpiod_line_request_input(line, "state-manager"))
155         {
156             error("Failed request for {GPIO_NAME} GPIO", "GPIO_NAME", gpioName);
157         }
158         else
159         {
160             // get gpio value
161             gpioval = gpiod_line_get_value(line);
162 
163             // release ownership of gpio
164             gpiod_line_close_chip(line);
165         }
166     }
167     return gpioval;
168 }
169 
170 void createError(
171     sdbusplus::bus_t& bus, const std::string& errorMsg,
172     sdbusplus::server::xyz::openbmc_project::logging::Entry::Level errLevel,
173     std::map<std::string, std::string> additionalData)
174 {
175     try
176     {
177         // Always add the _PID on for some extra logging debug
178         additionalData.emplace("_PID", std::to_string(getpid()));
179 
180         using LoggingCreate =
181             sdbusplus::client::xyz::openbmc_project::logging::Create<>;
182 
183         auto method = bus.new_method_call(LoggingCreate::default_service,
184                                           LoggingCreate::instance_path,
185                                           LoggingCreate::interface, "Create");
186 
187         method.append(errorMsg, errLevel, additionalData);
188         auto resp = bus.call(method);
189     }
190     catch (const sdbusplus::exception_t& e)
191     {
192         error("sdbusplus D-Bus call exception, error {ERROR} trying to create "
193               "an error with {ERROR_MSG}",
194               "ERROR", e, "ERROR_MSG", errorMsg);
195 
196         throw std::runtime_error(
197             "Error in invoking D-Bus logging create interface");
198     }
199     catch (const std::exception& e)
200     {
201         error("D-bus call exception: {ERROR}", "ERROR", e);
202         throw e;
203     }
204 }
205 
206 void createBmcDump(sdbusplus::bus_t& bus)
207 {
208     using DumpCreate = sdbusplus::client::xyz::openbmc_project::dump::Create<>;
209     auto dumpPath =
210         sdbusplus::message::object_path(DumpCreate::namespace_path::value) /
211         DumpCreate::namespace_path::bmc;
212 
213     auto method = bus.new_method_call(DumpCreate::default_service,
214                                       dumpPath.str.c_str(),
215                                       DumpCreate::interface, "CreateDump");
216     method.append(
217         std::vector<
218             std::pair<std::string, std::variant<std::string, uint64_t>>>());
219     try
220     {
221         bus.call_noreply(method);
222     }
223     catch (const sdbusplus::exception_t& e)
224     {
225         error("Failed to create BMC dump, exception:{ERROR}", "ERROR", e);
226         // just continue, this is error path anyway so we're just collecting
227         // what we can
228     }
229 }
230 
231 bool checkACLoss(size_t& chassisId)
232 {
233     std::string chassisLostPowerFileFmt = fmt::sprintf(CHASSIS_LOST_POWER_FILE,
234                                                        chassisId);
235 
236     std::filesystem::path chassisPowerLossFile{chassisLostPowerFileFmt};
237     if (std::filesystem::exists(chassisPowerLossFile))
238     {
239         return true;
240     }
241 
242     return false;
243 }
244 
245 bool isBmcReady(sdbusplus::bus_t& bus)
246 {
247     using BMC = sdbusplus::client::xyz::openbmc_project::state::BMC<>;
248     auto bmcPath = sdbusplus::message::object_path(BMC::namespace_path::value) /
249                    BMC::namespace_path::bmc;
250 
251     auto bmcState = getProperty(bus, bmcPath.str.c_str(), BMC::interface,
252                                 "CurrentBMCState");
253 
254     if (sdbusplus::message::convert_from_string<BMC::BMCState>(bmcState) !=
255         BMC::BMCState::Ready)
256     {
257         debug("BMC State is {BMC_STATE}", "BMC_STATE", bmcState);
258         return false;
259     }
260     return true;
261 }
262 
263 bool waitBmcReady(sdbusplus::bus_t& bus, std::chrono::seconds timeout)
264 {
265     while (timeout.count() != 0)
266     {
267         timeout--;
268         if (isBmcReady(bus))
269         {
270             return true;
271         }
272         std::this_thread::sleep_for(std::chrono::seconds(1));
273     }
274     return false;
275 }
276 
277 } // namespace utils
278 } // namespace manager
279 } // namespace state
280 } // namespace phosphor
281