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