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
createAsyncPelWithInventoryCallout(const types::ErrorType & i_errorType,const types::SeverityType & i_severity,const std::vector<types::InventoryCalloutData> & i_callouts,const std::string & i_fileName,const std::string & i_funcName,const uint8_t i_internalRc,const std::string & i_description,const std::optional<std::string> i_userData1,const std::optional<std::string> i_userData2,const std::optional<std::string> i_symFru,const std::optional<std::string> i_procedure)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
createAsyncPelWithI2cDeviceCallout(const types::ErrorType i_errorType,const types::SeverityType i_severity,const std::vector<types::DeviceCalloutData> & i_callouts,const std::string & i_fileName,const std::string & i_funcName,const uint8_t i_internalRc,const std::optional<std::pair<std::string,std::string>> i_userData1,const std::optional<std::pair<std::string,std::string>> i_userData2)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
createAsyncPelWithI2cBusCallout(const types::ErrorType i_errorType,const types::SeverityType i_severity,const std::vector<types::I2cBusCalloutData> & i_callouts,const std::string & i_fileName,const std::string & i_funcName,const uint8_t i_internalRc,const std::optional<std::pair<std::string,std::string>> i_userData1,const std::optional<std::pair<std::string,std::string>> i_userData2)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
createAsyncPel(const types::ErrorType & i_errorType,const types::SeverityType & i_severity,const std::string & i_fileName,const std::string & i_funcName,const uint8_t i_internalRc,const std::string & i_description,const std::optional<std::string> i_userData1,const std::optional<std::string> i_userData2,const std::optional<std::string> i_symFru,const std::optional<std::string> i_procedure)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
createSyncPel(const types::ErrorType & i_errorType,const types::SeverityType & i_severity,const std::string & i_fileName,const std::string & i_funcName,const uint8_t i_internalRc,const std::string & i_description,const std::optional<std::string> i_userData1,const std::optional<std::string> i_userData2,const std::optional<std::string> i_symFru,const std::optional<std::string> i_procedure)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
createSyncPelWithInvCallOut(const types::ErrorType & i_errorType,const types::SeverityType & i_severity,const std::string & i_fileName,const std::string & i_funcName,const uint8_t i_internalRc,const std::string & i_description,const std::vector<types::InventoryCalloutData> & i_callouts,const std::optional<std::string> i_userData1,const std::optional<std::string> i_userData2,const std::optional<std::string> i_symFru,const std::optional<std::string> i_procedure)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
getExceptionData(const std::exception & i_exception)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
getErrorType(const std::exception & i_exception)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
getErrorMsg(const std::exception & i_exception)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
getErrorTypeString(const types::ErrorType & i_errorType)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