1 #pragma once
2 #include "dump_manager.hpp"
3 
4 #include <systemd/sd-event.h>
5 #include <unistd.h>
6 
7 #include <phosphor-logging/elog-errors.hpp>
8 #include <phosphor-logging/elog.hpp>
9 #include <phosphor-logging/lg2.hpp>
10 #include <sdbusplus/bus.hpp>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 #include <xyz/openbmc_project/Dump/Create/server.hpp>
13 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
14 #include <xyz/openbmc_project/State/Host/server.hpp>
15 
16 #include <memory>
17 
18 namespace phosphor
19 {
20 namespace dump
21 {
22 
23 using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::server::
24     Progress::ProgressStages;
25 using HostState =
26     sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
27 
28 using namespace phosphor::logging;
29 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
30 
31 /* Need a custom deleter for freeing up sd_event */
32 struct EventDeleter
33 {
34     void operator()(sd_event* event) const
35     {
36         event = sd_event_unref(event);
37     }
38 };
39 using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
40 
41 /** @struct CustomFd
42  *
43  *  RAII wrapper for file descriptor.
44  */
45 struct CustomFd
46 {
47   private:
48     /** @brief File descriptor */
49     int fd = -1;
50 
51   public:
52     CustomFd() = delete;
53     CustomFd(const CustomFd&) = delete;
54     CustomFd& operator=(const CustomFd&) = delete;
55     CustomFd(CustomFd&&) = delete;
56     CustomFd& operator=(CustomFd&&) = delete;
57 
58     /** @brief Saves File descriptor and uses it to do file operation
59      *
60      *  @param[in] fd - File descriptor
61      */
62     CustomFd(int fd) : fd(fd) {}
63 
64     ~CustomFd()
65     {
66         if (fd >= 0)
67         {
68             close(fd);
69         }
70     }
71 
72     int operator()() const
73     {
74         return fd;
75     }
76 };
77 
78 /**
79  * @brief Get the bus service
80  *
81  * @param[in] bus - Bus to attach to.
82  * @param[in] path - D-Bus path name.
83  * @param[in] interface - D-Bus interface name.
84  * @return the bus service as a string
85  *
86  * @throws sdbusplus::exception::SdBusError - If any D-Bus error occurs during
87  * the call.
88  **/
89 std::string getService(sdbusplus::bus_t& bus, const std::string& path,
90                        const std::string& interface);
91 
92 /**
93  * @brief Read property value from the specified object and interface
94  * @param[in] bus D-Bus handle
95  * @param[in] service service which has implemented the interface
96  * @param[in] object object having has implemented the interface
97  * @param[in] intf interface having the property
98  * @param[in] prop name of the property to read
99  * @throws sdbusplus::exception::SdBusError if an error occurs in the dbus call
100  * @return property value
101  */
102 template <typename T>
103 T readDBusProperty(sdbusplus::bus_t& bus, const std::string& service,
104                    const std::string& object, const std::string& intf,
105                    const std::string& prop)
106 {
107     T retVal{};
108     try
109     {
110         auto properties = bus.new_method_call(service.c_str(), object.c_str(),
111                                               "org.freedesktop.DBus.Properties",
112                                               "Get");
113         properties.append(intf);
114         properties.append(prop);
115         auto result = bus.call(properties);
116         result.read(retVal);
117     }
118     catch (const std::exception& ex)
119     {
120         lg2::error(
121             "Failed to get the property: {PROPERTY} interface: {INTERFACE} "
122             "object path: {OBJECT_PATH} error: {ERROR} ",
123             "PROPERTY", prop, "INTERFACE", intf, "OBJECT_PATH", object, "ERROR",
124             ex);
125         throw;
126     }
127     return retVal;
128 }
129 
130 /**
131  * @brief Get the state value
132  *
133  * @param[in] intf - Interface to get the value
134  * @param[in] objPath - Object path of the service
135  * @param[in] state - State name to get
136  *
137  * @return The state value as type T on successful retrieval.
138  *
139  * @throws sdbusplus::exception for D-Bus failures and std::bad_variant_access
140  * for invalid value
141  */
142 template <typename T>
143 T getStateValue(const std::string& intf, const std::string& objPath,
144                 const std::string& state)
145 {
146     try
147     {
148         auto bus = sdbusplus::bus::new_default();
149         auto service = getService(bus, objPath, intf);
150         return std::get<T>(readDBusProperty<std::variant<T>>(
151             bus, service, objPath, intf, state));
152     }
153     catch (const sdbusplus::exception_t& e)
154     {
155         lg2::error(
156             "D-Bus call exception, OBJPATH: {OBJPATH}, "
157             "INTERFACE: {INTERFACE}, PROPERTY: {PROPERTY}, error: {ERROR}",
158             "OBJPATH", objPath, "INTERFACE", intf, "PROPERTY", state, "ERROR",
159             e);
160         throw;
161     }
162     catch (const std::bad_variant_access& e)
163     {
164         lg2::error("Exception raised while read state: {STATE} property "
165                    "value,  OBJPATH: {OBJPATH}, INTERFACE: {INTERFACE}, "
166                    "error: {ERROR}",
167                    "STATE", state, "OBJPATH", objPath, "INTERFACE", intf,
168                    "ERROR", e);
169         throw;
170     }
171 }
172 
173 /**
174  * @brief Get the host state
175  *
176  * @return HostState on success
177  *
178  * @throws std::runtime_error - If getting the state property fails
179  */
180 inline HostState getHostState()
181 {
182     constexpr auto hostStateInterface = "xyz.openbmc_project.State.Host";
183     // TODO Need to change host instance if multiple instead "0"
184     constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
185     return getStateValue<HostState>(hostStateInterface, hostStateObjPath,
186                                     "CurrentHostState");
187 }
188 
189 /**
190  * @brief Get the host boot progress stage
191  *
192  * @return BootProgress on success
193  *
194  * @throws std::runtime_error - If getting the state property fails
195  */
196 inline BootProgress getBootProgress()
197 {
198     constexpr auto bootProgressInterface =
199         "xyz.openbmc_project.State.Boot.Progress";
200     // TODO Need to change host instance if multiple instead "0"
201     constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
202     return getStateValue<BootProgress>(bootProgressInterface, hostStateObjPath,
203                                        "BootProgress");
204 }
205 
206 /**
207  * @brief Check whether host is running
208  *
209  * @return true if the host running else false.
210  *
211  * @throws std::runtime_error - If getting the boot progress failed
212  */
213 inline bool isHostRunning()
214 {
215     // TODO #ibm-openbmc/dev/2858 Revisit the method for finding whether host
216     // is running.
217     BootProgress bootProgressStatus = getBootProgress();
218     if ((bootProgressStatus == BootProgress::SystemInitComplete) ||
219         (bootProgressStatus == BootProgress::SystemSetup) ||
220         (bootProgressStatus == BootProgress::OSStart) ||
221         (bootProgressStatus == BootProgress::OSRunning) ||
222         (bootProgressStatus == BootProgress::PCIInit))
223     {
224         return true;
225     }
226     return false;
227 }
228 
229 inline void extractOriginatorProperties(phosphor::dump::DumpCreateParams params,
230                                         std::string& originatorId,
231                                         originatorTypes& originatorType)
232 {
233     using InvalidArgument =
234         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
235     using Argument = xyz::openbmc_project::Common::InvalidArgument;
236     using CreateParametersXYZ =
237         sdbusplus::xyz::openbmc_project::Dump::server::Create::CreateParameters;
238 
239     auto iter = params.find(
240         sdbusplus::xyz::openbmc_project::Dump::server::Create::
241             convertCreateParametersToString(CreateParametersXYZ::OriginatorId));
242     if (iter == params.end())
243     {
244         lg2::info("OriginatorId is not provided");
245     }
246     else
247     {
248         try
249         {
250             originatorId = std::get<std::string>(iter->second);
251         }
252         catch (const std::bad_variant_access& e)
253         {
254             // Exception will be raised if the input is not string
255             lg2::error("An invalid originatorId passed. It should be a string, "
256                        "errormsg: {ERROR}",
257                        "ERROR", e);
258             elog<InvalidArgument>(Argument::ARGUMENT_NAME("ORIGINATOR_ID"),
259                                   Argument::ARGUMENT_VALUE("INVALID INPUT"));
260         }
261     }
262 
263     iter = params.find(sdbusplus::xyz::openbmc_project::Dump::server::Create::
264                            convertCreateParametersToString(
265                                CreateParametersXYZ::OriginatorType));
266     if (iter == params.end())
267     {
268         lg2::info("OriginatorType is not provided. Replacing the string "
269                   "with the default value");
270         originatorType = originatorTypes::Internal;
271     }
272     else
273     {
274         try
275         {
276             std::string type = std::get<std::string>(iter->second);
277             originatorType = sdbusplus::xyz::openbmc_project::Common::server::
278                 OriginatedBy::convertOriginatorTypesFromString(type);
279         }
280         catch (const std::bad_variant_access& e)
281         {
282             // Exception will be raised if the input is not string
283             lg2::error("An invalid originatorType passed, errormsg: {ERROR}",
284                        "ERROR", e);
285             elog<InvalidArgument>(Argument::ARGUMENT_NAME("ORIGINATOR_TYPE"),
286                                   Argument::ARGUMENT_VALUE("INVALID INPUT"));
287         }
288     }
289 }
290 
291 /**
292  * @brief Check whether host is quiesced
293  *
294  * @return true if the host is quiesced else false.
295  *
296  * @throws std::runtime_error - If getting the state failed
297  */
298 inline bool isHostQuiesced()
299 {
300     return (getHostState() == HostState::Quiesced);
301 }
302 
303 } // namespace dump
304 } // namespace phosphor
305