1 #include "config.h" 2 3 #include "event_logger.hpp" 4 5 #include "exceptions.hpp" 6 #include "logger.hpp" 7 8 #include <systemd/sd-bus.h> 9 10 #include <utility/json_utility.hpp> 11 12 #include <filesystem> 13 14 namespace vpd 15 { 16 const std::unordered_map<types::SeverityType, std::string> 17 EventLogger::m_severityMap = { 18 {types::SeverityType::Notice, 19 "xyz.openbmc_project.Logging.Entry.Level.Notice"}, 20 {types::SeverityType::Informational, 21 "xyz.openbmc_project.Logging.Entry.Level.Informational"}, 22 {types::SeverityType::Debug, 23 "xyz.openbmc_project.Logging.Entry.Level.Debug"}, 24 {types::SeverityType::Warning, 25 "xyz.openbmc_project.Logging.Entry.Level.Warning"}, 26 {types::SeverityType::Critical, 27 "xyz.openbmc_project.Logging.Entry.Level.Critical"}, 28 {types::SeverityType::Emergency, 29 "xyz.openbmc_project.Logging.Entry.Level.Emergency"}, 30 {types::SeverityType::Alert, 31 "xyz.openbmc_project.Logging.Entry.Level.Alert"}, 32 {types::SeverityType::Error, 33 "xyz.openbmc_project.Logging.Entry.Level.Error"}}; 34 35 const std::unordered_map<types::ErrorType, std::string> 36 EventLogger::m_errorMsgMap = { 37 {types::ErrorType::DefaultValue, "com.ibm.VPD.Error.DefaultValue"}, 38 {types::ErrorType::UndefinedError, "com.ibm.VPD.Error.UndefinedError"}, 39 {types::ErrorType::InvalidVpdMessage, "com.ibm.VPD.Error.InvalidVPD"}, 40 {types::ErrorType::VpdMismatch, "com.ibm.VPD.Error.Mismatch"}, 41 {types::ErrorType::InvalidEeprom, 42 "com.ibm.VPD.Error.InvalidEepromPath"}, 43 {types::ErrorType::EccCheckFailed, "com.ibm.VPD.Error.EccCheckFailed"}, 44 {types::ErrorType::JsonFailure, "com.ibm.VPD.Error.InvalidJson"}, 45 {types::ErrorType::DbusFailure, "com.ibm.VPD.Error.DbusFailure"}, 46 {types::ErrorType::InvalidSystem, 47 "com.ibm.VPD.Error.UnknownSystemType"}, 48 {types::ErrorType::EssentialFru, 49 "com.ibm.VPD.Error.RequiredFRUMissing"}, 50 {types::ErrorType::GpioError, "com.ibm.VPD.Error.GPIOError"}, 51 {types::ErrorType::InternalFailure, 52 "xyz.openbmc_project.Common.Error.InternalFailure"}, 53 {types::ErrorType::FruMissing, "com.ibm.VPD.Error.RequiredFRUMissing"}, 54 {types::ErrorType::SystemTypeMismatch, 55 "com.ibm.VPD.Error.SystemTypeMismatch"}, 56 {types::ErrorType::UnknownSystemSettings, 57 "com.ibm.VPD.Error.UnknownSystemSettings"}, 58 {types::ErrorType::FirmwareError, "com.ibm.VPD.Error.FirmwareError"}, 59 {types::ErrorType::VpdParseError, "com.ibm.VPD.Error.VPDParseError"}}; 60 61 const std::unordered_map<types::CalloutPriority, std::string> 62 EventLogger::m_priorityMap = { 63 {types::CalloutPriority::High, "H"}, 64 {types::CalloutPriority::Medium, "M"}, 65 {types::CalloutPriority::MediumGroupA, "A"}, 66 {types::CalloutPriority::MediumGroupB, "B"}, 67 {types::CalloutPriority::MediumGroupC, "C"}, 68 {types::CalloutPriority::Low, "L"}}; 69 70 void EventLogger::createAsyncPelWithInventoryCallout( 71 const types::ErrorType& i_errorType, const types::SeverityType& i_severity, 72 const std::vector<types::InventoryCalloutData>& i_callouts, 73 const std::string& i_fileName, const std::string& i_funcName, 74 const uint8_t i_internalRc, const std::string& i_description, 75 const std::optional<std::string> i_userData1, 76 const std::optional<std::string> i_userData2, 77 const std::optional<std::string> i_symFru, 78 const std::optional<std::string> i_procedure) 79 { 80 (void)i_symFru; 81 (void)i_procedure; 82 83 try 84 { 85 if (i_callouts.empty()) 86 { 87 logging::logMessage("Callout information is missing to create PEL"); 88 // TODO: Revisit this instead of simpley returning. 89 return; 90 } 91 92 if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end()) 93 { 94 throw std::runtime_error( 95 "Error type not found in the error message map to create PEL"); 96 // TODO: Need to handle, instead of throwing exception. Create 97 // default message in message_registry.json. 98 } 99 100 const std::string& l_message = m_errorMsgMap.at(i_errorType); 101 102 const std::string& l_severity = 103 (m_severityMap.find(i_severity) != m_severityMap.end() 104 ? m_severityMap.at(i_severity) 105 : m_severityMap.at(types::SeverityType::Informational)); 106 107 std::string l_description = 108 (!i_description.empty() ? i_description : "VPD generic error"); 109 110 std::string l_userData1 = (i_userData1) ? (*i_userData1) : ""; 111 112 std::string l_userData2 = (i_userData2) ? (*i_userData2) : ""; 113 114 const types::InventoryCalloutData& l_invCallout = i_callouts[0]; 115 // TODO: Need to handle multiple inventory path callout's, when multiple 116 // callout's is supported by "Logging" service. 117 118 const types::CalloutPriority& l_priorityEnum = get<1>(l_invCallout); 119 120 const std::string& l_priority = 121 (m_priorityMap.find(l_priorityEnum) != m_priorityMap.end() 122 ? m_priorityMap.at(l_priorityEnum) 123 : m_priorityMap.at(types::CalloutPriority::Low)); 124 125 sd_bus* l_sdBus = nullptr; 126 sd_bus_default(&l_sdBus); 127 128 const uint8_t l_additionalDataCount = 8; 129 auto l_rc = sd_bus_call_method_async( 130 l_sdBus, NULL, constants::eventLoggingServiceName, 131 constants::eventLoggingObjectPath, constants::eventLoggingInterface, 132 "Create", NULL, NULL, "ssa{ss}", l_message.c_str(), 133 l_severity.c_str(), l_additionalDataCount, "FileName", 134 i_fileName.c_str(), "FunctionName", i_funcName.c_str(), 135 "InternalRc", std::to_string(i_internalRc).c_str(), "DESCRIPTION", 136 l_description.c_str(), "UserData1", l_userData1.c_str(), 137 "UserData2", l_userData2.c_str(), "CALLOUT_INVENTORY_PATH", 138 get<0>(l_invCallout).c_str(), "CALLOUT_PRIORITY", 139 l_priority.c_str()); 140 141 if (l_rc < 0) 142 { 143 logging::logMessage( 144 "Error calling sd_bus_call_method_async, Message = " + 145 std::string(strerror(-l_rc))); 146 } 147 } 148 catch (const std::exception& l_ex) 149 { 150 logging::logMessage( 151 "Create PEL failed with error: " + std::string(l_ex.what())); 152 } 153 } 154 155 void EventLogger::createAsyncPelWithI2cDeviceCallout( 156 const types::ErrorType i_errorType, const types::SeverityType i_severity, 157 const std::vector<types::DeviceCalloutData>& i_callouts, 158 const std::string& i_fileName, const std::string& i_funcName, 159 const uint8_t i_internalRc, 160 const std::optional<std::pair<std::string, std::string>> i_userData1, 161 const std::optional<std::pair<std::string, std::string>> i_userData2) 162 { 163 // TODO, implementation needs to be added. 164 (void)i_errorType; 165 (void)i_severity; 166 (void)i_callouts; 167 (void)i_fileName; 168 (void)i_funcName; 169 (void)i_internalRc; 170 (void)i_userData1; 171 (void)i_userData2; 172 } 173 174 void EventLogger::createAsyncPelWithI2cBusCallout( 175 const types::ErrorType i_errorType, const types::SeverityType i_severity, 176 const std::vector<types::I2cBusCalloutData>& i_callouts, 177 const std::string& i_fileName, const std::string& i_funcName, 178 const uint8_t i_internalRc, 179 const std::optional<std::pair<std::string, std::string>> i_userData1, 180 const std::optional<std::pair<std::string, std::string>> i_userData2) 181 { 182 // TODO, implementation needs to be added. 183 (void)i_errorType; 184 (void)i_severity; 185 (void)i_callouts; 186 (void)i_fileName; 187 (void)i_funcName; 188 (void)i_internalRc; 189 (void)i_userData1; 190 (void)i_userData2; 191 } 192 193 void EventLogger::createAsyncPel( 194 const types::ErrorType& i_errorType, const types::SeverityType& i_severity, 195 const std::string& i_fileName, const std::string& i_funcName, 196 const uint8_t i_internalRc, const std::string& i_description, 197 const std::optional<std::string> i_userData1, 198 const std::optional<std::string> i_userData2, 199 const std::optional<std::string> i_symFru, 200 const std::optional<std::string> i_procedure) 201 { 202 (void)i_symFru; 203 (void)i_procedure; 204 try 205 { 206 if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end()) 207 { 208 throw std::runtime_error("Unsupported error type received"); 209 // TODO: Need to handle, instead of throwing an exception. 210 } 211 212 const std::string& l_message = m_errorMsgMap.at(i_errorType); 213 214 const std::string& l_severity = 215 (m_severityMap.find(i_severity) != m_severityMap.end() 216 ? m_severityMap.at(i_severity) 217 : m_severityMap.at(types::SeverityType::Informational)); 218 219 const std::string l_description = 220 ((!i_description.empty() ? i_description : "VPD generic error")); 221 222 const std::string l_userData1 = ((i_userData1) ? (*i_userData1) : ""); 223 224 const std::string l_userData2 = ((i_userData2) ? (*i_userData2) : ""); 225 226 sd_bus* l_sdBus = nullptr; 227 sd_bus_default(&l_sdBus); 228 229 // VALUE_6 represents the additional data pair count passing to create 230 // PEL. If there any change in additional data, we need to pass the 231 // correct number. 232 auto l_rc = sd_bus_call_method_async( 233 l_sdBus, NULL, constants::eventLoggingServiceName, 234 constants::eventLoggingObjectPath, constants::eventLoggingInterface, 235 "Create", NULL, NULL, "ssa{ss}", l_message.c_str(), 236 l_severity.c_str(), constants::VALUE_6, "FileName", 237 i_fileName.c_str(), "FunctionName", i_funcName.c_str(), 238 "InternalRc", std::to_string(i_internalRc).c_str(), "DESCRIPTION", 239 l_description.c_str(), "UserData1", l_userData1.c_str(), 240 "UserData2", l_userData2.c_str()); 241 242 if (l_rc < 0) 243 { 244 logging::logMessage( 245 "Error calling sd_bus_call_method_async, Message = " + 246 std::string(strerror(-l_rc))); 247 } 248 } 249 catch (const sdbusplus::exception::SdBusError& l_ex) 250 { 251 logging::logMessage("Async PEL creation failed with an error: " + 252 std::string(l_ex.what())); 253 } 254 } 255 256 void EventLogger::createSyncPel( 257 const types::ErrorType& i_errorType, const types::SeverityType& i_severity, 258 const std::string& i_fileName, const std::string& i_funcName, 259 const uint8_t i_internalRc, const std::string& i_description, 260 const std::optional<std::string> i_userData1, 261 const std::optional<std::string> i_userData2, 262 const std::optional<std::string> i_symFru, 263 const std::optional<std::string> i_procedure) 264 { 265 (void)i_symFru; 266 (void)i_procedure; 267 try 268 { 269 if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end()) 270 { 271 throw std::runtime_error("Unsupported error type received"); 272 // TODO: Need to handle, instead of throwing an exception. 273 } 274 275 const std::string& l_message = m_errorMsgMap.at(i_errorType); 276 277 const std::string& l_severity = 278 (m_severityMap.find(i_severity) != m_severityMap.end() 279 ? m_severityMap.at(i_severity) 280 : m_severityMap.at(types::SeverityType::Informational)); 281 282 const std::string l_description = 283 ((!i_description.empty() ? i_description : "VPD generic error")); 284 285 const std::string l_userData1 = ((i_userData1) ? (*i_userData1) : ""); 286 287 const std::string l_userData2 = ((i_userData2) ? (*i_userData2) : ""); 288 289 std::map<std::string, std::string> l_additionalData{ 290 {"FileName", i_fileName}, 291 {"FunctionName", i_funcName}, 292 {"DESCRIPTION", l_description}, 293 {"InteranlRc", std::to_string(i_internalRc)}, 294 {"UserData1", l_userData1.c_str()}, 295 {"UserData2", l_userData2.c_str()}}; 296 297 auto l_bus = sdbusplus::bus::new_default(); 298 auto l_method = 299 l_bus.new_method_call(constants::eventLoggingServiceName, 300 constants::eventLoggingObjectPath, 301 constants::eventLoggingInterface, "Create"); 302 l_method.append(l_message, l_severity, l_additionalData); 303 l_bus.call(l_method); 304 } 305 catch (const sdbusplus::exception::SdBusError& l_ex) 306 { 307 logging::logMessage("Sync PEL creation failed with an error: " + 308 std::string(l_ex.what())); 309 } 310 } 311 312 void EventLogger::createSyncPelWithInvCallOut( 313 const types::ErrorType& i_errorType, const types::SeverityType& i_severity, 314 const std::string& i_fileName, const std::string& i_funcName, 315 const uint8_t i_internalRc, const std::string& i_description, 316 const std::vector<types::InventoryCalloutData>& i_callouts, 317 const std::optional<std::string> i_userData1, 318 const std::optional<std::string> i_userData2, 319 [[maybe_unused]] const std::optional<std::string> i_symFru, 320 [[maybe_unused]] const std::optional<std::string> i_procedure) 321 { 322 try 323 { 324 if (i_callouts.empty()) 325 { 326 createSyncPel(i_errorType, i_severity, i_fileName, i_funcName, 327 i_internalRc, i_description, i_userData1, i_userData2, 328 i_symFru, i_procedure); 329 logging::logMessage( 330 "Callout list is empty, creating PEL without call out"); 331 return; 332 } 333 334 if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end()) 335 { 336 throw std::runtime_error("Unsupported error type received"); 337 } 338 339 // Path to hold callout inventory path. 340 std::string l_calloutInvPath; 341 342 // check if callout path is a valid inventory path. if not, get the JSON 343 // object to get inventory path. 344 if (std::get<0>(i_callouts[0]) 345 .compare(constants::VALUE_0, strlen(constants::pimPath), 346 constants::pimPath) != constants::STR_CMP_SUCCESS) 347 { 348 std::error_code l_ec; 349 // implies json dependent execution. 350 if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK, l_ec)) 351 { 352 if (!l_ec) 353 { 354 l_calloutInvPath = jsonUtility::getInventoryObjPathFromJson( 355 jsonUtility::getParsedJson(INVENTORY_JSON_SYM_LINK), 356 std::get<0>(i_callouts[0])); 357 } 358 else 359 { 360 logging::logMessage( 361 "Error finding symlink. Continue with given path"); 362 } 363 } 364 } 365 366 if (l_calloutInvPath.empty()) 367 { 368 l_calloutInvPath = std::get<0>(i_callouts[0]); 369 } 370 371 const std::map<std::string, std::string> l_additionalData{ 372 {"FileName", i_fileName}, 373 {"FunctionName", i_funcName}, 374 {"DESCRIPTION", 375 (!i_description.empty() ? i_description : "VPD generic error")}, 376 {"CALLOUT_INVENTORY_PATH", l_calloutInvPath}, 377 {"InteranlRc", std::to_string(i_internalRc)}, 378 {"UserData1", ((i_userData1) ? (*i_userData1) : "").c_str()}, 379 {"UserData2", ((i_userData2) ? (*i_userData2) : "").c_str()}}; 380 381 const std::string& l_severity = 382 (m_severityMap.find(i_severity) != m_severityMap.end() 383 ? m_severityMap.at(i_severity) 384 : m_severityMap.at(types::SeverityType::Informational)); 385 386 auto l_bus = sdbusplus::bus::new_default(); 387 auto l_method = 388 l_bus.new_method_call(constants::eventLoggingServiceName, 389 constants::eventLoggingObjectPath, 390 constants::eventLoggingInterface, "Create"); 391 l_method.append(m_errorMsgMap.at(i_errorType), l_severity, 392 l_additionalData); 393 l_bus.call(l_method); 394 } 395 catch (const std::exception& l_ex) 396 { 397 logging::logMessage( 398 "Sync PEL creation with inventory path failed with error: " + 399 std::string(l_ex.what())); 400 } 401 } 402 403 types::ExceptionDataMap EventLogger::getExceptionData( 404 const std::exception& i_exception) 405 { 406 types::ExceptionDataMap l_errorInfo{ 407 {"ErrorType", types::ErrorType::UndefinedError}, 408 {"ErrorMsg", i_exception.what()}}; 409 410 try 411 { 412 if (typeid(i_exception) == typeid(DataException)) 413 { 414 const DataException& l_ex = 415 dynamic_cast<const DataException&>(i_exception); 416 l_errorInfo["ErrorType"] = l_ex.getErrorType(); 417 l_errorInfo["ErrorMsg"] = 418 std::string("Data Exception. Reason: ") + i_exception.what(); 419 } 420 else if (typeid(i_exception) == typeid(EccException)) 421 { 422 const EccException& l_ex = 423 dynamic_cast<const EccException&>(i_exception); 424 l_errorInfo["ErrorType"] = l_ex.getErrorType(); 425 l_errorInfo["ErrorMsg"] = 426 std::string("Ecc Exception. Reason: ") + i_exception.what(); 427 } 428 else if (typeid(i_exception) == typeid(JsonException)) 429 { 430 const JsonException& l_ex = 431 dynamic_cast<const JsonException&>(i_exception); 432 l_errorInfo["ErrorType"] = l_ex.getErrorType(); 433 l_errorInfo["ErrorMsg"] = 434 std::string("Json Exception. Reason: ") + i_exception.what(); 435 } 436 else if (typeid(i_exception) == typeid(GpioException)) 437 { 438 const GpioException& l_ex = 439 dynamic_cast<const GpioException&>(i_exception); 440 l_errorInfo["ErrorType"] = l_ex.getErrorType(); 441 l_errorInfo["ErrorMsg"] = 442 std::string("Gpio Exception. Reason: ") + i_exception.what(); 443 } 444 else if (typeid(i_exception) == typeid(DbusException)) 445 { 446 const DbusException& l_ex = 447 dynamic_cast<const DbusException&>(i_exception); 448 l_errorInfo["ErrorType"] = l_ex.getErrorType(); 449 l_errorInfo["ErrorMsg"] = 450 std::string("Dbus Exception. Reason: ") + i_exception.what(); 451 } 452 else if (typeid(i_exception) == typeid(FirmwareException)) 453 { 454 const FirmwareException& l_ex = 455 dynamic_cast<const FirmwareException&>(i_exception); 456 l_errorInfo["ErrorType"] = l_ex.getErrorType(); 457 l_errorInfo["ErrorMsg"] = 458 std::string("Firmware Exception. Reason: ") + 459 i_exception.what(); 460 } 461 else if (typeid(i_exception) == typeid(EepromException)) 462 { 463 const EepromException& l_ex = 464 dynamic_cast<const EepromException&>(i_exception); 465 l_errorInfo["ErrorType"] = l_ex.getErrorType(); 466 l_errorInfo["ErrorMsg"] = 467 std::string("Eeprom Exception. Reason: ") + i_exception.what(); 468 } 469 else if (typeid(i_exception) == typeid(std::runtime_error)) 470 { 471 // Since it is a standard exception no casting is required and error 472 // type is hardcoded. 473 l_errorInfo["ErrorType"] = types::ErrorType::FirmwareError; 474 l_errorInfo["ErrorMsg"] = 475 std::string("Eeprom Exception. Reason: ") + i_exception.what(); 476 } 477 } 478 catch (const std::exception& l_ex) 479 { 480 logging::logMessage( 481 "Failed to get error info, reason: " + std::string(l_ex.what())); 482 } 483 return l_errorInfo; 484 } 485 486 types::ErrorType EventLogger::getErrorType(const std::exception& i_exception) 487 { 488 const auto& l_exceptionDataMap = getExceptionData(i_exception); 489 490 auto l_itrToErrType = l_exceptionDataMap.find("ErrorType"); 491 if (l_itrToErrType == l_exceptionDataMap.end()) 492 { 493 return types::ErrorType::UndefinedError; 494 } 495 496 auto l_ptrToErrType = 497 std::get_if<types::ErrorType>(&l_itrToErrType->second); 498 if (!l_ptrToErrType) 499 { 500 return types::ErrorType::UndefinedError; 501 } 502 503 return *l_ptrToErrType; 504 } 505 506 std::string EventLogger::getErrorMsg(const std::exception& i_exception) 507 { 508 const auto& l_exceptionDataMap = getExceptionData(i_exception); 509 510 auto l_itrToErrMsg = l_exceptionDataMap.find("ErrorMsg"); 511 if (l_itrToErrMsg == l_exceptionDataMap.end()) 512 { 513 return i_exception.what(); 514 } 515 516 auto l_ptrToErrMsg = std::get_if<std::string>(&l_itrToErrMsg->second); 517 if (!l_ptrToErrMsg) 518 { 519 return i_exception.what(); 520 } 521 522 return *l_ptrToErrMsg; 523 } 524 525 std::string EventLogger::getErrorTypeString( 526 const types::ErrorType& i_errorType) noexcept 527 { 528 const auto l_entry = m_errorMsgMap.find(i_errorType); 529 return (l_entry != m_errorMsgMap.end() 530 ? l_entry->second 531 : m_errorMsgMap.at(types::ErrorType::UndefinedError)); 532 } 533 } // namespace vpd 534