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 } // namespace dump 339 } // namespace phosphor 340