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 } // namespace dump 381 } // namespace phosphor 382