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
getSubsystem(const std::string & subsystemName)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
getSeverity(const std::string & severityName)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
getSeverities(const nlohmann::json & severity)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
getActionFlags(const std::vector<std::string> & flags)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
getEventType(const std::string & eventTypeName)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
getEventScope(const std::string & eventScopeName)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
getSRCReasonCode(const nlohmann::json & src,const std::string & name)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
getSRCType(const nlohmann::json & src,const std::string & name)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
getSRCDeconfigFlag(const nlohmann::json & src)191 bool getSRCDeconfigFlag(const nlohmann::json& src)
192 {
193 return src["DeconfigFlag"].get<bool>();
194 }
195
getSRCCheckstopFlag(const nlohmann::json & src)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>>
getSRCHexwordFields(const nlohmann::json & src,const std::string & name)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 }
getSRCSymptomIDFields(const nlohmann::json & src,const std::string & name)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
getComponentID(uint8_t srcType,uint16_t reasonCode,const nlohmann::json & pelEntry,const std::string & name)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 */
calloutUsesAdditionalData(const nlohmann::json & json)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 */
findCalloutList(const nlohmann::json & json,const std::vector<std::string> & systemNames,nlohmann::json & calloutLists)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 */
makeRegistryCallout(const nlohmann::json & json)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 */
getCalloutsWithoutAD(const nlohmann::json & json,const std::vector<std::string> & systemNames)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 */
getCalloutsUsingAD(const nlohmann::json & json,const std::vector<std::string> & systemNames,const AdditionalData & additionalData)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 */
getJournalCapture(const nlohmann::json & json)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
lookup(const std::string & name,LookupType type,bool toCache)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
readRegistry(const std::filesystem::path & registryFile)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
getCallouts(const nlohmann::json & calloutJSON,const std::vector<std::string> & systemNames,const AdditionalData & additionalData)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