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
getSubsystem(const std::string & subsystemName)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
getSeverity(const std::string & severityName)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
getSeverities(const nlohmann::json & severity)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
getActionFlags(const std::vector<std::string> & flags)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
getEventType(const std::string & eventTypeName)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
getEventScope(const std::string & eventScopeName)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
getSRCReasonCode(const nlohmann::json & src,const std::string & name)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
getSRCType(const nlohmann::json & src,const std::string & name)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
getSRCDeconfigFlag(const nlohmann::json & src)192 bool getSRCDeconfigFlag(const nlohmann::json& src)
193 {
194 return src["DeconfigFlag"].get<bool>();
195 }
196
getSRCCheckstopFlag(const nlohmann::json & src)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>>
getSRCHexwordFields(const nlohmann::json & src,const std::string & name)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>>
getSRCSymptomIDFields(const nlohmann::json & src,const std::string & name)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
getComponentID(uint8_t srcType,uint16_t reasonCode,const nlohmann::json & pelEntry,const std::string & name)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 */
calloutUsesAdditionalData(const nlohmann::json & json)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 */
findCalloutList(const nlohmann::json & json,const std::vector<std::string> & systemNames,nlohmann::json & calloutLists)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 */
makeRegistryCallout(const nlohmann::json & json)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 */
getCalloutsWithoutAD(const nlohmann::json & json,const std::vector<std::string> & systemNames)552 std::vector<RegistryCallout> getCalloutsWithoutAD(
553 const nlohmann::json& json, const std::vector<std::string>& systemNames)
554 {
555 std::vector<RegistryCallout> calloutEntries;
556
557 nlohmann::json calloutLists = nlohmann::json::array();
558
559 // Find the CalloutList to use based on the system type
560 findCalloutList(json, systemNames, calloutLists);
561
562 // We finally found the callouts, make the objects.
563 for (const auto& callout : calloutLists)
564 {
565 calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
566 }
567
568 return calloutEntries;
569 }
570
571 /**
572 * @brief Returns the callouts to use when an AdditionalData key is
573 * required to find the correct entries.
574 *
575 * The JSON looks like:
576 * {
577 * "ADName": "PROC_NUM",
578 * "CalloutsWithTheirADValues":
579 * [
580 * {
581 * "ADValue": "0",
582 * "Callouts":
583 * [
584 * {
585 * "CalloutList":
586 * [
587 * {
588 * "Priority": "high",
589 * "LocCode": "P1-C5"
590 * }
591 * ]
592 * }
593 * ]
594 * }
595 * ]
596 * }
597 *
598 * Note that the "Callouts" entry above is the same as the top level
599 * entry used when there is no AdditionalData key.
600 *
601 * @param[in] json - The callout JSON
602 * @param[in] systemNames - List of compatible system type names
603 * @param[in] additionalData - The AdditionalData property
604 *
605 * @return std::vector<RegistryCallout> - The callouts to use
606 */
getCalloutsUsingAD(const nlohmann::json & json,const std::vector<std::string> & systemNames,const AdditionalData & additionalData)607 std::vector<RegistryCallout> getCalloutsUsingAD(
608 const nlohmann::json& json, const std::vector<std::string>& systemNames,
609 const AdditionalData& additionalData)
610 {
611 // This indicates which AD field we'll be using
612 auto keyName = json["ADName"].get<std::string>();
613
614 // Get the actual value from the AD data
615 auto adValue = additionalData.getValue(keyName);
616
617 if (!adValue)
618 {
619 // The AdditionalData did not contain the necessary key
620 lg2::warning("The PEL message registry callouts JSON "
621 "said to use an AdditionalData key that isn't in the "
622 "AdditionalData event log property, key = {KEY}",
623 "KEY", keyName);
624 throw std::runtime_error{
625 "Missing AdditionalData entry for this callout"};
626 }
627
628 const auto& callouts = json["CalloutsWithTheirADValues"];
629
630 // find the entry with that AD value
631 auto it = std::find_if(
632 callouts.begin(), callouts.end(), [adValue](const nlohmann::json& j) {
633 return *adValue == j["ADValue"].get<std::string>();
634 });
635
636 if (it == callouts.end())
637 {
638 // This can happen if not all possible values were in the
639 // message registry and that's fine. There may be a
640 // "CalloutsWhenNoADMatch" section that contains callouts
641 // to use in this case.
642 if (json.contains("CalloutsWhenNoADMatch"))
643 {
644 return getCalloutsWithoutAD(json["CalloutsWhenNoADMatch"],
645 systemNames);
646 }
647 return std::vector<RegistryCallout>{};
648 }
649
650 // Proceed to find the callouts possibly based on system type.
651 return getCalloutsWithoutAD((*it)["Callouts"], systemNames);
652 }
653
654 /**
655 * @brief Returns the journal capture information
656 *
657 * The JSON looks like:
658 * "JournalCapture": {
659 * "NumLines": 30
660 * }
661 *
662 * "JournalCapture":
663 * {
664 * "Sections": [
665 * {
666 * "SyslogID": "phosphor-log-manager",
667 * "NumLines": 20
668 * }
669 * ]
670 * }
671 *
672 * @param json - The journal capture JSON
673 * @return JournalCapture - The filled in variant
674 */
getJournalCapture(const nlohmann::json & json)675 JournalCapture getJournalCapture(const nlohmann::json& json)
676 {
677 JournalCapture capt;
678
679 // Primary key is either NumLines or Sections.
680 if (json.contains("NumLines"))
681 {
682 capt = json.at("NumLines").get<size_t>();
683 }
684 else if (json.contains("Sections"))
685 {
686 AppCaptureList captures;
687 for (const auto& capture : json.at("Sections"))
688 {
689 AppCapture ac;
690 ac.syslogID = capture.at("SyslogID").get<std::string>();
691 ac.numLines = capture.at("NumLines").get<size_t>();
692 captures.push_back(std::move(ac));
693 }
694
695 capt = captures;
696 }
697 else
698 {
699 lg2::error("JournalCapture section not the right format");
700 throw std::runtime_error{"JournalCapture section not the right format"};
701 }
702
703 return capt;
704 }
705
706 } // namespace helper
707
lookup(const std::string & name,LookupType type,bool toCache)708 std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
709 bool toCache)
710 {
711 std::optional<nlohmann::json> registryTmp;
712 auto& registryOpt = (_registry) ? _registry : registryTmp;
713 if (!registryOpt)
714 {
715 registryOpt = readRegistry(_registryFile);
716 if (!registryOpt)
717 {
718 return std::nullopt;
719 }
720 else if (toCache)
721 {
722 // Save message registry in memory for peltool
723 _registry = std::move(registryTmp);
724 }
725 }
726 auto& reg = (_registry) ? _registry : registryTmp;
727 const auto& registry = reg.value();
728 // Find an entry with this name in the PEL array.
729 auto e = std::find_if(
730 registry["PELs"].begin(), registry["PELs"].end(),
731 [&name, &type](const nlohmann::json& j) {
732 return ((name == j.at("Name").get<std::string>() &&
733 type == LookupType::name) ||
734 (name == j.at("SRC").at("ReasonCode").get<std::string>() &&
735 type == LookupType::reasonCode));
736 });
737
738 if (e != registry["PELs"].end())
739 {
740 // Fill in the Entry structure from the JSON. Most, but not all, fields
741 // are optional.
742
743 try
744 {
745 Entry entry;
746 entry.name = (*e)["Name"];
747
748 if (e->contains("Subsystem"))
749 {
750 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
751 }
752
753 if (e->contains("ActionFlags"))
754 {
755 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
756 }
757
758 if (e->contains("MfgActionFlags"))
759 {
760 entry.mfgActionFlags =
761 helper::getActionFlags((*e)["MfgActionFlags"]);
762 }
763
764 if (e->contains("Severity"))
765 {
766 entry.severity = helper::getSeverities((*e)["Severity"]);
767 }
768
769 if (e->contains("MfgSeverity"))
770 {
771 entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
772 }
773
774 if (e->contains("EventType"))
775 {
776 entry.eventType = helper::getEventType((*e)["EventType"]);
777 }
778
779 if (e->contains("EventScope"))
780 {
781 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
782 }
783
784 auto& src = (*e)["SRC"];
785 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
786
787 if (src.contains("Type"))
788 {
789 entry.src.type = helper::getSRCType(src, name);
790 }
791 else
792 {
793 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
794 }
795
796 // Now that we know the SRC type and reason code,
797 // we can get the component ID.
798 entry.componentID = helper::getComponentID(
799 entry.src.type, entry.src.reasonCode, *e, name);
800
801 if (src.contains("Words6To9"))
802 {
803 entry.src.hexwordADFields =
804 helper::getSRCHexwordFields(src, name);
805 }
806
807 if (src.contains("SymptomIDFields"))
808 {
809 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
810 }
811
812 if (src.contains("DeconfigFlag"))
813 {
814 entry.src.deconfigFlag = helper::getSRCDeconfigFlag(src);
815 }
816
817 if (src.contains("CheckstopFlag"))
818 {
819 entry.src.checkstopFlag = helper::getSRCCheckstopFlag(src);
820 }
821
822 auto& doc = (*e)["Documentation"];
823 entry.doc.message = doc["Message"];
824 entry.doc.description = doc["Description"];
825 if (doc.contains("MessageArgSources"))
826 {
827 entry.doc.messageArgSources = doc["MessageArgSources"];
828 }
829
830 // If there are callouts defined, save the JSON for later
831 if (_loadCallouts)
832 {
833 if (e->contains("Callouts"))
834 {
835 entry.callouts = (*e)["Callouts"];
836 }
837 else if (e->contains("CalloutsUsingAD"))
838 {
839 entry.callouts = (*e)["CalloutsUsingAD"];
840 }
841 }
842
843 if (e->contains("JournalCapture"))
844 {
845 entry.journalCapture =
846 helper::getJournalCapture((*e)["JournalCapture"]);
847 }
848
849 return entry;
850 }
851 catch (const std::exception& ex)
852 {
853 lg2::error("Found invalid message registry field. Error: {ERROR}",
854 "ERROR", ex);
855 }
856 }
857
858 return std::nullopt;
859 }
860
861 std::optional<nlohmann::json>
readRegistry(const std::filesystem::path & registryFile)862 Registry::readRegistry(const std::filesystem::path& registryFile)
863 {
864 // Look in /etc first in case someone put a test file there
865 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
866 nlohmann::json registry;
867 std::ifstream file;
868
869 if (fs::exists(debugFile))
870 {
871 lg2::info("Using debug PEL message registry");
872 file.open(debugFile);
873 }
874 else
875 {
876 file.open(registryFile);
877 }
878
879 try
880 {
881 registry = nlohmann::json::parse(file);
882 }
883 catch (const std::exception& e)
884 {
885 lg2::error("Error parsing message registry JSON. Error: {ERROR}",
886 "ERROR", e);
887 return std::nullopt;
888 }
889 return registry;
890 }
891
892 std::vector<RegistryCallout>
getCallouts(const nlohmann::json & calloutJSON,const std::vector<std::string> & systemNames,const AdditionalData & additionalData)893 Registry::getCallouts(const nlohmann::json& calloutJSON,
894 const std::vector<std::string>& systemNames,
895 const AdditionalData& additionalData)
896 {
897 // The JSON may either use an AdditionalData key
898 // as an index, or not.
899 if (helper::calloutUsesAdditionalData(calloutJSON))
900 {
901 return helper::getCalloutsUsingAD(calloutJSON, systemNames,
902 additionalData);
903 }
904
905 return helper::getCalloutsWithoutAD(calloutJSON, systemNames);
906 }
907
908 } // namespace message
909 } // namespace pels
910 } // namespace openpower
911