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