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