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