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 {
36     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      */
64     CustomFd(int fd) : fd(fd) {}
65 
66     ~CustomFd()
67     {
68         if (fd >= 0)
69         {
70             close(fd);
71         }
72     }
73 
74     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>
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 = bus.new_method_call(service.c_str(), object.c_str(),
113                                               "org.freedesktop.DBus.Properties",
114                                               "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>
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  */
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  */
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  */
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 
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  */
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>
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  */
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