1 #pragma once
2 #include "dump_manager.hpp"
3 #include "dump_types.hpp"
4
5 #include <systemd/sd-event.h>
6 #include <unistd.h>
7
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <sdbusplus/bus.hpp>
12 #include <xyz/openbmc_project/Common/error.hpp>
13 #include <xyz/openbmc_project/Dump/Create/common.hpp>
14 #include <xyz/openbmc_project/Dump/Create/server.hpp>
15 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
16 #include <xyz/openbmc_project/State/Host/server.hpp>
17
18 #include <memory>
19
20 namespace phosphor
21 {
22 namespace dump
23 {
24
25 using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::server::
26 Progress::ProgressStages;
27 using HostState =
28 sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
29
30 using namespace phosphor::logging;
31 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
32
33 /* Need a custom deleter for freeing up sd_event */
34 struct EventDeleter
35 {
operator ()phosphor::dump::EventDeleter36 void operator()(sd_event* event) const
37 {
38 sd_event_unref(event);
39 }
40 };
41 using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
42
43 /** @struct CustomFd
44 *
45 * RAII wrapper for file descriptor.
46 */
47 struct CustomFd
48 {
49 private:
50 /** @brief File descriptor */
51 int fd = -1;
52
53 public:
54 CustomFd() = delete;
55 CustomFd(const CustomFd&) = delete;
56 CustomFd& operator=(const CustomFd&) = delete;
57 CustomFd(CustomFd&&) = delete;
58 CustomFd& operator=(CustomFd&&) = delete;
59
60 /** @brief Saves File descriptor and uses it to do file operation
61 *
62 * @param[in] fd - File descriptor
63 */
CustomFdphosphor::dump::CustomFd64 CustomFd(int fd) : fd(fd) {}
65
~CustomFdphosphor::dump::CustomFd66 ~CustomFd()
67 {
68 if (fd >= 0)
69 {
70 close(fd);
71 }
72 }
73
operator ()phosphor::dump::CustomFd74 int operator()() const
75 {
76 return fd;
77 }
78 };
79
80 /**
81 * @brief Get the bus service
82 *
83 * @param[in] bus - Bus to attach to.
84 * @param[in] path - D-Bus path name.
85 * @param[in] interface - D-Bus interface name.
86 * @return the bus service as a string
87 *
88 * @throws sdbusplus::exception::SdBusError - If any D-Bus error occurs during
89 * the call.
90 **/
91 std::string getService(sdbusplus::bus_t& bus, const std::string& path,
92 const std::string& interface);
93
94 /**
95 * @brief Read property value from the specified object and interface
96 * @param[in] bus D-Bus handle
97 * @param[in] service service which has implemented the interface
98 * @param[in] object object having has implemented the interface
99 * @param[in] intf interface having the property
100 * @param[in] prop name of the property to read
101 * @throws sdbusplus::exception::SdBusError if an error occurs in the dbus call
102 * @return property value
103 */
104 template <typename T>
readDBusProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & object,const std::string & intf,const std::string & prop)105 T readDBusProperty(sdbusplus::bus_t& bus, const std::string& service,
106 const std::string& object, const std::string& intf,
107 const std::string& prop)
108 {
109 T retVal{};
110 try
111 {
112 auto properties =
113 bus.new_method_call(service.c_str(), object.c_str(),
114 "org.freedesktop.DBus.Properties", "Get");
115 properties.append(intf);
116 properties.append(prop);
117 auto result = bus.call(properties);
118 result.read(retVal);
119 }
120 catch (const std::exception& ex)
121 {
122 lg2::error(
123 "Failed to get the property: {PROPERTY} interface: {INTERFACE} "
124 "object path: {OBJECT_PATH} error: {ERROR} ",
125 "PROPERTY", prop, "INTERFACE", intf, "OBJECT_PATH", object, "ERROR",
126 ex);
127 throw;
128 }
129 return retVal;
130 }
131
132 /**
133 * @brief Get the state value
134 *
135 * @param[in] intf - Interface to get the value
136 * @param[in] objPath - Object path of the service
137 * @param[in] state - State name to get
138 *
139 * @return The state value as type T on successful retrieval.
140 *
141 * @throws sdbusplus::exception for D-Bus failures and std::bad_variant_access
142 * for invalid value
143 */
144 template <typename T>
getStateValue(const std::string & intf,const std::string & objPath,const std::string & state)145 T getStateValue(const std::string& intf, const std::string& objPath,
146 const std::string& state)
147 {
148 try
149 {
150 auto bus = sdbusplus::bus::new_default();
151 auto service = getService(bus, objPath, intf);
152 return std::get<T>(readDBusProperty<std::variant<T>>(
153 bus, service, objPath, intf, state));
154 }
155 catch (const sdbusplus::exception_t& e)
156 {
157 lg2::error(
158 "D-Bus call exception, OBJPATH: {OBJPATH}, "
159 "INTERFACE: {INTERFACE}, PROPERTY: {PROPERTY}, error: {ERROR}",
160 "OBJPATH", objPath, "INTERFACE", intf, "PROPERTY", state, "ERROR",
161 e);
162 throw;
163 }
164 catch (const std::bad_variant_access& e)
165 {
166 lg2::error("Exception raised while read state: {STATE} property "
167 "value, OBJPATH: {OBJPATH}, INTERFACE: {INTERFACE}, "
168 "error: {ERROR}",
169 "STATE", state, "OBJPATH", objPath, "INTERFACE", intf,
170 "ERROR", e);
171 throw;
172 }
173 }
174
175 /**
176 * @brief Get the host state
177 *
178 * @return HostState on success
179 *
180 * @throws std::runtime_error - If getting the state property fails
181 */
getHostState()182 inline HostState getHostState()
183 {
184 constexpr auto hostStateInterface = "xyz.openbmc_project.State.Host";
185 // TODO Need to change host instance if multiple instead "0"
186 constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
187 return getStateValue<HostState>(hostStateInterface, hostStateObjPath,
188 "CurrentHostState");
189 }
190
191 /**
192 * @brief Get the host boot progress stage
193 *
194 * @return BootProgress on success
195 *
196 * @throws std::runtime_error - If getting the state property fails
197 */
getBootProgress()198 inline BootProgress getBootProgress()
199 {
200 constexpr auto bootProgressInterface =
201 "xyz.openbmc_project.State.Boot.Progress";
202 // TODO Need to change host instance if multiple instead "0"
203 constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
204 return getStateValue<BootProgress>(bootProgressInterface, hostStateObjPath,
205 "BootProgress");
206 }
207
208 /**
209 * @brief Check whether host is running
210 *
211 * @return true if the host running else false.
212 *
213 * @throws std::runtime_error - If getting the boot progress failed
214 */
isHostRunning()215 inline bool isHostRunning()
216 {
217 // TODO #ibm-openbmc/dev/2858 Revisit the method for finding whether host
218 // is running.
219 BootProgress bootProgressStatus = getBootProgress();
220 if ((bootProgressStatus == BootProgress::SystemInitComplete) ||
221 (bootProgressStatus == BootProgress::SystemSetup) ||
222 (bootProgressStatus == BootProgress::OSStart) ||
223 (bootProgressStatus == BootProgress::OSRunning) ||
224 (bootProgressStatus == BootProgress::PCIInit))
225 {
226 return true;
227 }
228 return false;
229 }
230
extractOriginatorProperties(phosphor::dump::DumpCreateParams params,std::string & originatorId,originatorTypes & originatorType)231 inline void extractOriginatorProperties(phosphor::dump::DumpCreateParams params,
232 std::string& originatorId,
233 originatorTypes& originatorType)
234 {
235 using InvalidArgument =
236 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
237 using Argument = xyz::openbmc_project::Common::InvalidArgument;
238 using CreateParametersXYZ =
239 sdbusplus::xyz::openbmc_project::Dump::server::Create::CreateParameters;
240
241 auto iter = params.find(
242 sdbusplus::xyz::openbmc_project::Dump::server::Create::
243 convertCreateParametersToString(CreateParametersXYZ::OriginatorId));
244 if (iter == params.end())
245 {
246 lg2::info("OriginatorId is not provided");
247 }
248 else
249 {
250 try
251 {
252 originatorId = std::get<std::string>(iter->second);
253 }
254 catch (const std::bad_variant_access& e)
255 {
256 // Exception will be raised if the input is not string
257 lg2::error("An invalid originatorId passed. It should be a string, "
258 "errormsg: {ERROR}",
259 "ERROR", e);
260 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ORIGINATOR_ID"),
261 Argument::ARGUMENT_VALUE("INVALID INPUT"));
262 }
263 }
264
265 iter = params.find(sdbusplus::xyz::openbmc_project::Dump::server::Create::
266 convertCreateParametersToString(
267 CreateParametersXYZ::OriginatorType));
268 if (iter == params.end())
269 {
270 lg2::info("OriginatorType is not provided. Replacing the string "
271 "with the default value");
272 originatorType = originatorTypes::Internal;
273 }
274 else
275 {
276 try
277 {
278 std::string type = std::get<std::string>(iter->second);
279 originatorType = sdbusplus::xyz::openbmc_project::Common::server::
280 OriginatedBy::convertOriginatorTypesFromString(type);
281 }
282 catch (const std::bad_variant_access& e)
283 {
284 // Exception will be raised if the input is not string
285 lg2::error("An invalid originatorType passed, errormsg: {ERROR}",
286 "ERROR", e);
287 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ORIGINATOR_TYPE"),
288 Argument::ARGUMENT_VALUE("INVALID INPUT"));
289 }
290 }
291 }
292
293 /**
294 * @brief Check whether host is quiesced
295 *
296 * @return true if the host is quiesced else false.
297 *
298 * @throws std::runtime_error - If getting the state failed
299 */
isHostQuiesced()300 inline bool isHostQuiesced()
301 {
302 return (getHostState() == HostState::Quiesced);
303 }
304
305 /** @brief Extract the dump create parameters
306 * @param[in] key - The name of the parameter
307 * @param[in] params - The map of parameters passed as input
308 *
309 * @return On success, a std::optional containing the value of the parameter
310 * (of type T). On failure (key not found in the map or the value is not of type
311 * T), returns an empty std::optional.
312 */
313 template <typename T>
extractParameter(const std::string & key,phosphor::dump::DumpCreateParams & params)314 T extractParameter(const std::string& key,
315 phosphor::dump::DumpCreateParams& params)
316 {
317 using InvalidArgument =
318 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
319 using Argument = xyz::openbmc_project::Common::InvalidArgument;
320
321 auto it = params.find(key);
322 if (it != params.end())
323 {
324 const auto& [foundKey, variantValue] = *it;
325 if (std::holds_alternative<T>(variantValue))
326 {
327 return std::get<T>(variantValue);
328 }
329 else
330 {
331 lg2::error("An invalid input passed for key: {KEY}", "KEY", key);
332 elog<InvalidArgument>(Argument::ARGUMENT_NAME(key.c_str()),
333 Argument::ARGUMENT_VALUE("INVALID INPUT"));
334 }
335 }
336 return T{};
337 }
338
339 /**
340 * @brief This function fetches the dump type associated with a particular
341 * error.
342 *
343 * @param[in] params The map of parameters passed as input.
344 *
345 * @return The dump type associated with the error.
346 *
347 * @throw std::invalid_argument If the dump type associated with the error
348 * type is not found in the map.
349 */
getErrorDumpType(phosphor::dump::DumpCreateParams & params)350 inline DumpTypes getErrorDumpType(phosphor::dump::DumpCreateParams& params)
351 {
352 using CreateParameters =
353 sdbusplus::xyz::openbmc_project::Dump::server::Create::CreateParameters;
354 using DumpIntr = sdbusplus::common::xyz::openbmc_project::dump::Create;
355 using InvalidArgument =
356 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
357 using Argument = xyz::openbmc_project::Common::InvalidArgument;
358
359 std::string errorType = extractParameter<std::string>(
360 DumpIntr::convertCreateParametersToString(CreateParameters::ErrorType),
361 params);
362 if (!isErrorTypeValid(errorType))
363 {
364 lg2::error("An invalid error type passed type: {ERROR_TYPE}",
365 "ERROR_TYPE", errorType);
366 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ERROR_TYPE"),
367 Argument::ARGUMENT_VALUE(errorType.c_str()));
368 }
369 auto type = stringToDumpType(errorType);
370 if (type.has_value())
371 {
372 return type.value();
373 }
374
375 // Ideally this should never happen, because if the error type is valid
376 // it should be present in the dumpTypeToStringMap
377 throw std::invalid_argument{"Dump type not found"};
378 }
379
380 /**
381 * @brief Extracts the dump ID and timestamp from a BMC dump file name.
382 *
383 * @param[in] file The path to the dump file.
384 *
385 * @return A std::optional containing a tuple with the dump ID, timestamp
386 * and size of the file if the extraction is successful, or std::nullopt
387 * if the file name does not match the expected format.
388 */
389 std::optional<std::tuple<uint32_t, uint64_t, uint64_t>>
390 extractDumpDetails(const std::filesystem::path& file);
391
392 } // namespace dump
393 } // namespace phosphor
394