xref: /openbmc/phosphor-logging/extensions/openpower-pels/registry.cpp (revision 40fb54935ce7367636a7156039396ee91cc4d5e2)
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