1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "registry.hpp" 17 18 #include "json_utils.hpp" 19 #include "pel_types.hpp" 20 #include "pel_values.hpp" 21 22 #include <phosphor-logging/lg2.hpp> 23 24 #include <algorithm> 25 #include <fstream> 26 27 namespace openpower 28 { 29 namespace pels 30 { 31 namespace message 32 { 33 34 namespace pv = pel_values; 35 namespace fs = std::filesystem; 36 37 constexpr auto debugFilePath = "/etc/phosphor-logging/"; 38 39 namespace helper 40 { 41 42 uint8_t getSubsystem(const std::string& subsystemName) 43 { 44 // Get the actual value to use in the PEL for the string name 45 auto ss = pv::findByName(subsystemName, pv::subsystemValues); 46 if (ss == pv::subsystemValues.end()) 47 { 48 // Schema validation should be catching this. 49 lg2::error("Invalid subsystem name used in message registry: {SUBSYS}", 50 "SUBSYS", subsystemName); 51 52 throw std::runtime_error("Invalid subsystem used in message registry"); 53 } 54 55 return std::get<pv::fieldValuePos>(*ss); 56 } 57 58 uint8_t getSeverity(const std::string& severityName) 59 { 60 auto s = pv::findByName(severityName, pv::severityValues); 61 if (s == pv::severityValues.end()) 62 { 63 // Schema validation should be catching this. 64 lg2::error("Invalid severity name used in message registry: {SEV}", 65 "SEV", severityName); 66 67 throw std::runtime_error("Invalid severity used in message registry"); 68 } 69 70 return std::get<pv::fieldValuePos>(*s); 71 } 72 73 std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity) 74 { 75 std::vector<RegistrySeverity> severities; 76 77 // The plain string value, like "unrecoverable" 78 if (severity.is_string()) 79 { 80 RegistrySeverity s; 81 s.severity = getSeverity(severity.get<std::string>()); 82 severities.push_back(std::move(s)); 83 } 84 else 85 { 86 // An array, with an element like: 87 // { 88 // "SevValue": "unrecoverable", 89 // "System", "systemA" 90 // } 91 for (const auto& sev : severity) 92 { 93 RegistrySeverity s; 94 s.severity = getSeverity(sev["SevValue"].get<std::string>()); 95 96 if (sev.contains("System")) 97 { 98 s.system = sev["System"].get<std::string>(); 99 } 100 101 severities.push_back(std::move(s)); 102 } 103 } 104 105 return severities; 106 } 107 108 uint16_t getActionFlags(const std::vector<std::string>& flags) 109 { 110 uint16_t actionFlags = 0; 111 112 // Make the bitmask based on the array of flag names 113 for (const auto& flag : flags) 114 { 115 auto s = pv::findByName(flag, pv::actionFlagsValues); 116 if (s == pv::actionFlagsValues.end()) 117 { 118 // Schema validation should be catching this. 119 lg2::error( 120 "Invalid action flag name used in message registry: {FLAG}", 121 "FLAG", flag); 122 123 throw std::runtime_error( 124 "Invalid action flag used in message registry"); 125 } 126 127 actionFlags |= std::get<pv::fieldValuePos>(*s); 128 } 129 130 return actionFlags; 131 } 132 133 uint8_t getEventType(const std::string& eventTypeName) 134 { 135 auto t = pv::findByName(eventTypeName, pv::eventTypeValues); 136 if (t == pv::eventTypeValues.end()) 137 { 138 lg2::error("Invalid event type used in message registry: {TYPE}", 139 "TYPE", eventTypeName); 140 141 throw std::runtime_error("Invalid event type used in message registry"); 142 } 143 return std::get<pv::fieldValuePos>(*t); 144 } 145 146 uint8_t getEventScope(const std::string& eventScopeName) 147 { 148 auto s = pv::findByName(eventScopeName, pv::eventScopeValues); 149 if (s == pv::eventScopeValues.end()) 150 { 151 lg2::error("Invalid event scope used in registry: {SCOPE}", "SCOPE", 152 eventScopeName); 153 154 throw std::runtime_error( 155 "Invalid event scope used in message registry"); 156 } 157 return std::get<pv::fieldValuePos>(*s); 158 } 159 160 uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name) 161 { 162 std::string rc = src["ReasonCode"]; 163 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16); 164 if (reasonCode == 0) 165 { 166 lg2::error( 167 "Invalid reason code {RC} in message registry, error name = {ERROR}", 168 "RC", rc, "ERROR", name); 169 170 throw std::runtime_error("Invalid reason code in message registry"); 171 } 172 return reasonCode; 173 } 174 175 uint8_t getSRCType(const nlohmann::json& src, const std::string& name) 176 { 177 // Looks like: "22" 178 std::string srcType = src["Type"]; 179 size_t type = strtoul(srcType.c_str(), nullptr, 16); 180 if ((type == 0) || (srcType.size() != 2)) // 1 hex byte 181 { 182 lg2::error( 183 "Invalid SRC Type {TYPE} in message registry, error name = {ERROR}", 184 "TYPE", srcType, "ERROR", name); 185 186 throw std::runtime_error("Invalid SRC Type in message registry"); 187 } 188 189 return type; 190 } 191 192 bool getSRCDeconfigFlag(const nlohmann::json& src) 193 { 194 return src["DeconfigFlag"].get<bool>(); 195 } 196 197 bool getSRCCheckstopFlag(const nlohmann::json& src) 198 { 199 return src["CheckstopFlag"].get<bool>(); 200 } 201 202 std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>> 203 getSRCHexwordFields(const nlohmann::json& src, const std::string& name) 204 { 205 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields; 206 207 // Build the map of which AdditionalData fields to use for which SRC words 208 209 // Like: 210 // { 211 // "8": 212 // { 213 // "AdditionalDataPropSource": "TEST" 214 // } 215 // 216 // } 217 218 for (const auto& word : src["Words6To9"].items()) 219 { 220 std::string num = word.key(); 221 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10); 222 223 if (wordNum == 0) 224 { 225 lg2::error( 226 "Invalid SRC word number {NUM} in message registry, error name = {ERROR}", 227 "NUM", num, "ERROR", name); 228 229 throw std::runtime_error("Invalid SRC word in message registry"); 230 } 231 232 auto attributes = word.value(); 233 234 // Use an empty string for the description if it does not exist. 235 auto itr = attributes.find("Description"); 236 std::string desc = (attributes.end() != itr) ? *itr : ""; 237 238 std::tuple<std::string, std::string> adPropSourceDesc( 239 attributes["AdditionalDataPropSource"], desc); 240 hexwordFields[wordNum] = std::move(adPropSourceDesc); 241 } 242 243 if (!hexwordFields.empty()) 244 { 245 return hexwordFields; 246 } 247 248 return std::nullopt; 249 } 250 std::optional<std::vector<SRC::WordNum>> 251 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name) 252 { 253 std::vector<SRC::WordNum> symptomIDFields; 254 255 // Looks like: 256 // "SymptomIDFields": ["SRCWord3", "SRCWord6"], 257 258 for (const std::string field : src["SymptomIDFields"]) 259 { 260 // Just need the last digit off the end, e.g. SRCWord6. 261 // The schema enforces the format of these. 262 auto srcWordNum = field.substr(field.size() - 1); 263 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10); 264 if (num == 0) 265 { 266 lg2::error( 267 "Invalid symptom ID field {FIELD} in message registry, error name = {ERROR}", 268 "FIELD", field, "ERROR", name); 269 270 throw std::runtime_error("Invalid symptom ID in message registry"); 271 } 272 symptomIDFields.push_back(num); 273 } 274 if (!symptomIDFields.empty()) 275 { 276 return symptomIDFields; 277 } 278 279 return std::nullopt; 280 } 281 282 uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode, 283 const nlohmann::json& pelEntry, const std::string& name) 284 { 285 uint16_t id = 0; 286 287 // If the ComponentID field is there, use that. Otherwise, if it's a 288 // 0xBD BMC error SRC, use the reasoncode. 289 if (pelEntry.contains("ComponentID")) 290 { 291 std::string componentID = pelEntry["ComponentID"]; 292 id = strtoul(componentID.c_str(), nullptr, 16); 293 } 294 else 295 { 296 // On BMC error SRCs (BD), can just get the component ID from 297 // the first byte of the reason code. 298 if (srcType == static_cast<uint8_t>(SRCType::bmcError)) 299 { 300 id = reasonCode & 0xFF00; 301 } 302 else 303 { 304 lg2::error( 305 "Missing component ID field in message registry, error name = {ERROR}", 306 "ERROR", name); 307 308 throw std::runtime_error( 309 "Missing component ID field in message registry"); 310 } 311 } 312 313 return id; 314 } 315 316 /** 317 * @brief Says if the JSON is the format that contains AdditionalData keys 318 * as in index into them. 319 * 320 * @param[in] json - The highest level callout JSON 321 * 322 * @return bool - If it is the AdditionalData format or not 323 */ 324 bool calloutUsesAdditionalData(const nlohmann::json& json) 325 { 326 return (json.contains("ADName") && 327 json.contains("CalloutsWithTheirADValues")); 328 } 329 330 /** 331 * @brief Finds the callouts to use when there is no AdditionalData, 332 * but the system type may be used as a key. 333 * 334 * A sample calloutList array looks like the following. The System and Systems 335 * key are optional. 336 * 337 * System key - Value of the key will be the system name as a string. The 338 * callouts for a specific system can define under this key. 339 * 340 * Systems key - Value of the key will be an array of system names in the form 341 * of string. The callouts common to the systems mentioned in the array can 342 * define under this key. 343 * 344 * If both System and Systems not present it means that entry applies to every 345 * configuration that doesn't have another entry with a matching System and 346 * Systems key. 347 * 348 * { 349 * "System": "system1", 350 * "CalloutList": 351 * [ 352 * { 353 * "Priority": "high", 354 * "LocCode": "P1-C1" 355 * }, 356 * { 357 * "Priority": "low", 358 * "LocCode": "P1" 359 * } 360 * ] 361 * }, 362 * { 363 * "Systems": ["system1", 'system2"], 364 * "CalloutList": 365 * [ 366 * { 367 * "Priority": "high", 368 * "LocCode": "P0-C1" 369 * }, 370 * { 371 * "Priority": "low", 372 * "LocCode": "P0" 373 * } 374 * ] 375 * } 376 * 377 * @param[in] json - The callout JSON 378 * @param[in] systemNames - List of compatible system type names 379 * @param[out] calloutLists - The JSON array which will hold the calloutlist to 380 * use specific to the system. 381 * 382 * @return - Throws runtime exception if json is not an array or if calloutLists 383 * is empty. 384 */ 385 static void findCalloutList(const nlohmann::json& json, 386 const std::vector<std::string>& systemNames, 387 nlohmann::json& calloutLists) 388 { 389 if (!json.is_array()) 390 { 391 throw std::runtime_error{"findCalloutList was not passed a JSON array"}; 392 } 393 394 // Flag to indicate whether system specific callouts found or not 395 bool foundCallouts = false; 396 397 for (const auto& callouts : json) 398 { 399 if (callouts.contains("System")) 400 { 401 if (std::ranges::find(systemNames, 402 callouts["System"].get<std::string>()) != 403 systemNames.end()) 404 { 405 calloutLists.insert(calloutLists.end(), 406 callouts["CalloutList"].begin(), 407 callouts["CalloutList"].end()); 408 foundCallouts = true; 409 } 410 continue; 411 } 412 413 if (callouts.contains("Systems")) 414 { 415 std::vector<std::string> systems = 416 callouts["Systems"].get<std::vector<std::string>>(); 417 auto inSystemNames = [systemNames](const auto& system) { 418 return (std::ranges::find(systemNames, system) != 419 systemNames.end()); 420 }; 421 if (std::ranges::any_of(systems, inSystemNames)) 422 { 423 calloutLists.insert(calloutLists.end(), 424 callouts["CalloutList"].begin(), 425 callouts["CalloutList"].end()); 426 foundCallouts = true; 427 } 428 continue; 429 } 430 431 // Any entry if neither System/Systems key matches with system name 432 if (!foundCallouts) 433 { 434 calloutLists.insert(calloutLists.end(), 435 callouts["CalloutList"].begin(), 436 callouts["CalloutList"].end()); 437 } 438 } 439 if (calloutLists.empty()) 440 { 441 std::string types; 442 std::for_each(systemNames.begin(), systemNames.end(), 443 [&types](const auto& t) { types += t + '|'; }); 444 lg2::warning( 445 "No matching system name entry or default system name entry " 446 " for PEL callout list, names = {TYPES}", 447 "TYPES", types); 448 449 throw std::runtime_error{ 450 "Could not find a CalloutList JSON for this error and system name"}; 451 } 452 } 453 454 /** 455 * @brief Creates a RegistryCallout based on the input JSON. 456 * 457 * The JSON looks like: 458 * { 459 * "Priority": "high", 460 * "LocCode": "E1" 461 * ... 462 * } 463 * 464 * Schema validation enforces what keys are present. 465 * 466 * @param[in] json - The JSON dictionary entry for a callout 467 * 468 * @return RegistryCallout - A filled in RegistryCallout 469 */ 470 RegistryCallout makeRegistryCallout(const nlohmann::json& json) 471 { 472 RegistryCallout callout; 473 474 callout.priority = "high"; 475 callout.useInventoryLocCode = false; 476 477 if (json.contains("Priority")) 478 { 479 callout.priority = json["Priority"].get<std::string>(); 480 } 481 482 if (json.contains("LocCode")) 483 { 484 callout.locCode = json["LocCode"].get<std::string>(); 485 } 486 487 if (json.contains("Procedure")) 488 { 489 callout.procedure = json["Procedure"].get<std::string>(); 490 } 491 else if (json.contains("SymbolicFRU")) 492 { 493 callout.symbolicFRU = json["SymbolicFRU"].get<std::string>(); 494 } 495 else if (json.contains("SymbolicFRUTrusted")) 496 { 497 callout.symbolicFRUTrusted = 498 json["SymbolicFRUTrusted"].get<std::string>(); 499 } 500 501 if (json.contains("UseInventoryLocCode")) 502 { 503 callout.useInventoryLocCode = json["UseInventoryLocCode"].get<bool>(); 504 } 505 506 return callout; 507 } 508 509 /** 510 * @brief Returns the callouts to use when an AdditionalData key is 511 * required to find the correct entries. 512 * 513 * The System property is used to find which CalloutList to use. 514 * If System is missing, then that CalloutList is valid for 515 * everything. 516 * 517 * The JSON looks like: 518 * { 519 * "System": "system1", 520 * "CalloutList": 521 * [ 522 * { 523 * "Priority": "high", 524 * "LocCode": "P1-C1" 525 * }, 526 * { 527 * "Priority": "low", 528 * "LocCode": "P1" 529 * } 530 * ] 531 * }, 532 * { 533 * "Systems": ["system1", 'system2"], 534 * "CalloutList": 535 * [ 536 * { 537 * "Priority": "high", 538 * "LocCode": "P0-C1" 539 * }, 540 * { 541 * "Priority": "low", 542 * "LocCode": "P0" 543 * } 544 * ] 545 * } 546 * 547 * @param[in] json - The callout JSON 548 * @param[in] systemNames - List of compatible system type names 549 * 550 * @return std::vector<RegistryCallout> - The callouts to use 551 */ 552 std::vector<RegistryCallout> 553 getCalloutsWithoutAD(const nlohmann::json& json, 554 const std::vector<std::string>& systemNames) 555 { 556 std::vector<RegistryCallout> calloutEntries; 557 558 nlohmann::json calloutLists = nlohmann::json::array(); 559 560 // Find the CalloutList to use based on the system type 561 findCalloutList(json, systemNames, calloutLists); 562 563 // We finally found the callouts, make the objects. 564 for (const auto& callout : calloutLists) 565 { 566 calloutEntries.push_back(std::move(makeRegistryCallout(callout))); 567 } 568 569 return calloutEntries; 570 } 571 572 /** 573 * @brief Returns the callouts to use when an AdditionalData key is 574 * required to find the correct entries. 575 * 576 * The JSON looks like: 577 * { 578 * "ADName": "PROC_NUM", 579 * "CalloutsWithTheirADValues": 580 * [ 581 * { 582 * "ADValue": "0", 583 * "Callouts": 584 * [ 585 * { 586 * "CalloutList": 587 * [ 588 * { 589 * "Priority": "high", 590 * "LocCode": "P1-C5" 591 * } 592 * ] 593 * } 594 * ] 595 * } 596 * ] 597 * } 598 * 599 * Note that the "Callouts" entry above is the same as the top level 600 * entry used when there is no AdditionalData key. 601 * 602 * @param[in] json - The callout JSON 603 * @param[in] systemNames - List of compatible system type names 604 * @param[in] additionalData - The AdditionalData property 605 * 606 * @return std::vector<RegistryCallout> - The callouts to use 607 */ 608 std::vector<RegistryCallout> 609 getCalloutsUsingAD(const nlohmann::json& json, 610 const std::vector<std::string>& systemNames, 611 const AdditionalData& additionalData) 612 { 613 // This indicates which AD field we'll be using 614 auto keyName = json["ADName"].get<std::string>(); 615 616 // Get the actual value from the AD data 617 auto adValue = additionalData.getValue(keyName); 618 619 if (!adValue) 620 { 621 // The AdditionalData did not contain the necessary key 622 lg2::warning("The PEL message registry callouts JSON " 623 "said to use an AdditionalData key that isn't in the " 624 "AdditionalData event log property, key = {KEY}", 625 "KEY", keyName); 626 throw std::runtime_error{ 627 "Missing AdditionalData entry for this callout"}; 628 } 629 630 const auto& callouts = json["CalloutsWithTheirADValues"]; 631 632 // find the entry with that AD value 633 auto it = std::find_if(callouts.begin(), callouts.end(), 634 [adValue](const nlohmann::json& j) { 635 return *adValue == j["ADValue"].get<std::string>(); 636 }); 637 638 if (it == callouts.end()) 639 { 640 // This can happen if not all possible values were in the 641 // message registry and that's fine. There may be a 642 // "CalloutsWhenNoADMatch" section that contains callouts 643 // to use in this case. 644 if (json.contains("CalloutsWhenNoADMatch")) 645 { 646 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"], 647 systemNames); 648 } 649 return std::vector<RegistryCallout>{}; 650 } 651 652 // Proceed to find the callouts possibly based on system type. 653 return getCalloutsWithoutAD((*it)["Callouts"], systemNames); 654 } 655 656 /** 657 * @brief Returns the journal capture information 658 * 659 * The JSON looks like: 660 * "JournalCapture": { 661 * "NumLines": 30 662 * } 663 * 664 * "JournalCapture": 665 * { 666 * "Sections": [ 667 * { 668 * "SyslogID": "phosphor-log-manager", 669 * "NumLines": 20 670 * } 671 * ] 672 * } 673 * 674 * @param json - The journal capture JSON 675 * @return JournalCapture - The filled in variant 676 */ 677 JournalCapture getJournalCapture(const nlohmann::json& json) 678 { 679 JournalCapture capt; 680 681 // Primary key is either NumLines or Sections. 682 if (json.contains("NumLines")) 683 { 684 capt = json.at("NumLines").get<size_t>(); 685 } 686 else if (json.contains("Sections")) 687 { 688 AppCaptureList captures; 689 for (const auto& capture : json.at("Sections")) 690 { 691 AppCapture ac; 692 ac.syslogID = capture.at("SyslogID").get<std::string>(); 693 ac.numLines = capture.at("NumLines").get<size_t>(); 694 captures.push_back(std::move(ac)); 695 } 696 697 capt = captures; 698 } 699 else 700 { 701 lg2::error("JournalCapture section not the right format"); 702 throw std::runtime_error{"JournalCapture section not the right format"}; 703 } 704 705 return capt; 706 } 707 708 } // namespace helper 709 710 std::optional<Entry> Registry::lookup(const std::string& name, LookupType type, 711 bool toCache) 712 { 713 std::optional<nlohmann::json> registryTmp; 714 auto& registryOpt = (_registry) ? _registry : registryTmp; 715 if (!registryOpt) 716 { 717 registryOpt = readRegistry(_registryFile); 718 if (!registryOpt) 719 { 720 return std::nullopt; 721 } 722 else if (toCache) 723 { 724 // Save message registry in memory for peltool 725 _registry = std::move(registryTmp); 726 } 727 } 728 auto& reg = (_registry) ? _registry : registryTmp; 729 const auto& registry = reg.value(); 730 // Find an entry with this name in the PEL array. 731 auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(), 732 [&name, &type](const nlohmann::json& j) { 733 return ((name == j.at("Name").get<std::string>() && 734 type == LookupType::name) || 735 (name == j.at("SRC").at("ReasonCode").get<std::string>() && 736 type == LookupType::reasonCode)); 737 }); 738 739 if (e != registry["PELs"].end()) 740 { 741 // Fill in the Entry structure from the JSON. Most, but not all, fields 742 // are optional. 743 744 try 745 { 746 Entry entry; 747 entry.name = (*e)["Name"]; 748 749 if (e->contains("Subsystem")) 750 { 751 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]); 752 } 753 754 if (e->contains("ActionFlags")) 755 { 756 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]); 757 } 758 759 if (e->contains("MfgActionFlags")) 760 { 761 entry.mfgActionFlags = 762 helper::getActionFlags((*e)["MfgActionFlags"]); 763 } 764 765 if (e->contains("Severity")) 766 { 767 entry.severity = helper::getSeverities((*e)["Severity"]); 768 } 769 770 if (e->contains("MfgSeverity")) 771 { 772 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]); 773 } 774 775 if (e->contains("EventType")) 776 { 777 entry.eventType = helper::getEventType((*e)["EventType"]); 778 } 779 780 if (e->contains("EventScope")) 781 { 782 entry.eventScope = helper::getEventScope((*e)["EventScope"]); 783 } 784 785 auto& src = (*e)["SRC"]; 786 entry.src.reasonCode = helper::getSRCReasonCode(src, name); 787 788 if (src.contains("Type")) 789 { 790 entry.src.type = helper::getSRCType(src, name); 791 } 792 else 793 { 794 entry.src.type = static_cast<uint8_t>(SRCType::bmcError); 795 } 796 797 // Now that we know the SRC type and reason code, 798 // we can get the component ID. 799 entry.componentID = helper::getComponentID( 800 entry.src.type, entry.src.reasonCode, *e, name); 801 802 if (src.contains("Words6To9")) 803 { 804 entry.src.hexwordADFields = helper::getSRCHexwordFields(src, 805 name); 806 } 807 808 if (src.contains("SymptomIDFields")) 809 { 810 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name); 811 } 812 813 if (src.contains("DeconfigFlag")) 814 { 815 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src); 816 } 817 818 if (src.contains("CheckstopFlag")) 819 { 820 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src); 821 } 822 823 auto& doc = (*e)["Documentation"]; 824 entry.doc.message = doc["Message"]; 825 entry.doc.description = doc["Description"]; 826 if (doc.contains("MessageArgSources")) 827 { 828 entry.doc.messageArgSources = doc["MessageArgSources"]; 829 } 830 831 // If there are callouts defined, save the JSON for later 832 if (_loadCallouts) 833 { 834 if (e->contains("Callouts")) 835 { 836 entry.callouts = (*e)["Callouts"]; 837 } 838 else if (e->contains("CalloutsUsingAD")) 839 { 840 entry.callouts = (*e)["CalloutsUsingAD"]; 841 } 842 } 843 844 if (e->contains("JournalCapture")) 845 { 846 entry.journalCapture = 847 helper::getJournalCapture((*e)["JournalCapture"]); 848 } 849 850 return entry; 851 } 852 catch (const std::exception& ex) 853 { 854 lg2::error("Found invalid message registry field. Error: {ERROR}", 855 "ERROR", ex); 856 } 857 } 858 859 return std::nullopt; 860 } 861 862 std::optional<nlohmann::json> 863 Registry::readRegistry(const std::filesystem::path& registryFile) 864 { 865 // Look in /etc first in case someone put a test file there 866 fs::path debugFile{fs::path{debugFilePath} / registryFileName}; 867 nlohmann::json registry; 868 std::ifstream file; 869 870 if (fs::exists(debugFile)) 871 { 872 lg2::info("Using debug PEL message registry"); 873 file.open(debugFile); 874 } 875 else 876 { 877 file.open(registryFile); 878 } 879 880 try 881 { 882 registry = nlohmann::json::parse(file); 883 } 884 catch (const std::exception& e) 885 { 886 lg2::error("Error parsing message registry JSON. Error: {ERROR}", 887 "ERROR", e); 888 return std::nullopt; 889 } 890 return registry; 891 } 892 893 std::vector<RegistryCallout> 894 Registry::getCallouts(const nlohmann::json& calloutJSON, 895 const std::vector<std::string>& systemNames, 896 const AdditionalData& additionalData) 897 { 898 // The JSON may either use an AdditionalData key 899 // as an index, or not. 900 if (helper::calloutUsesAdditionalData(calloutJSON)) 901 { 902 return helper::getCalloutsUsingAD(calloutJSON, systemNames, 903 additionalData); 904 } 905 906 return helper::getCalloutsWithoutAD(calloutJSON, systemNames); 907 } 908 909 } // namespace message 910 } // namespace pels 911 } // namespace openpower 912