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