1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
4 #include "config.h"
5
6 #include "config_main.h"
7
8 #include "../bcd_time.hpp"
9 #include "../json_utils.hpp"
10 #include "../paths.hpp"
11 #include "../pel.hpp"
12 #include "../pel_types.hpp"
13 #include "../pel_values.hpp"
14
15 #include <Python.h>
16
17 #include <CLI/CLI.hpp>
18 #include <phosphor-logging/log.hpp>
19
20 #include <bitset>
21 #include <fstream>
22 #include <iostream>
23 #include <regex>
24 #include <string>
25
26 namespace fs = std::filesystem;
27 using namespace phosphor::logging;
28 using namespace openpower::pels;
29 namespace message = openpower::pels::message;
30 namespace pv = openpower::pels::pel_values;
31
32 const uint8_t critSysTermSeverity = 0x51;
33
34 using PELFunc = std::function<void(const PEL&, bool hexDump)>;
35 message::Registry registry(getPELReadOnlyDataPath() / message::registryFileName,
36 false);
37
pelLogDir()38 std::string pelLogDir()
39 {
40 return std::string(phosphor::logging::paths::extension()) + "/pels/logs";
41 }
42
43 /**
44 * @brief helper function to get PEL commit timestamp from file name
45 * @retrun uint64_t - PEL commit timestamp
46 * @param[in] std::string - file name
47 */
fileNameToTimestamp(const std::string & fileName)48 uint64_t fileNameToTimestamp(const std::string& fileName)
49 {
50 std::string token = fileName.substr(0, fileName.find("_"));
51 uint64_t bcdTime = 0;
52 if (token.length() >= 14)
53 {
54 int i = 0;
55
56 try
57 {
58 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
59 bcdTime |= (static_cast<uint64_t>(tmp) << 56);
60 }
61 catch (const std::exception& err)
62 {
63 std::cout << "Conversion failure: " << err.what() << std::endl;
64 }
65 i += 2;
66 try
67 {
68 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
69 bcdTime |= (static_cast<uint64_t>(tmp) << 48);
70 }
71 catch (const std::exception& err)
72 {
73 std::cout << "Conversion failure: " << err.what() << std::endl;
74 }
75 i += 2;
76 try
77 {
78 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
79 bcdTime |= (static_cast<uint64_t>(tmp) << 40);
80 }
81 catch (const std::exception& err)
82 {
83 std::cout << "Conversion failure: " << err.what() << std::endl;
84 }
85 i += 2;
86 try
87 {
88 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
89 bcdTime |= (static_cast<uint64_t>(tmp) << 32);
90 }
91 catch (const std::exception& err)
92 {
93 std::cout << "Conversion failure: " << err.what() << std::endl;
94 }
95 i += 2;
96 try
97 {
98 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
99 bcdTime |= (tmp << 24);
100 }
101 catch (const std::exception& err)
102 {
103 std::cout << "Conversion failure: " << err.what() << std::endl;
104 }
105 i += 2;
106 try
107 {
108 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
109 bcdTime |= (tmp << 16);
110 }
111 catch (const std::exception& err)
112 {
113 std::cout << "Conversion failure: " << err.what() << std::endl;
114 }
115 i += 2;
116 try
117 {
118 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
119 bcdTime |= (tmp << 8);
120 }
121 catch (const std::exception& err)
122 {
123 std::cout << "Conversion failure: " << err.what() << std::endl;
124 }
125 i += 2;
126 try
127 {
128 auto tmp = std::stoul(token.substr(i, 2), nullptr, 16);
129 bcdTime |= tmp;
130 }
131 catch (const std::exception& err)
132 {
133 std::cout << "Conversion failure: " << err.what() << std::endl;
134 }
135 }
136 return bcdTime;
137 }
138
139 /**
140 * @brief helper function to get PEL id from file name
141 * @retrun uint32_t - PEL id
142 * @param[in] std::string - file name
143 */
fileNameToPELId(const std::string & fileName)144 uint32_t fileNameToPELId(const std::string& fileName)
145 {
146 uint32_t num = 0;
147 try
148 {
149 num = std::stoul(fileName.substr(fileName.find("_") + 1), nullptr, 16);
150 }
151 catch (const std::exception& err)
152 {
153 std::cout << "Conversion failure: " << err.what() << std::endl;
154 }
155 return num;
156 }
157
158 /**
159 * @brief Check if the string ends with the PEL ID string passed in
160 * @param[in] str - string to check for PEL ID
161 * @param[in] pelID - PEL id string
162 *
163 * @return bool - true with suffix matches
164 */
endsWithPelID(const std::string & str,const std::string & pelID)165 bool endsWithPelID(const std::string& str, const std::string& pelID)
166 {
167 constexpr size_t pelIDSize = 8;
168
169 if (pelID.size() != pelIDSize)
170 {
171 return false;
172 }
173
174 size_t slen = str.size(), elen = pelID.size();
175 if (slen < elen)
176 return false;
177 while (elen)
178 {
179 if (str[--slen] != pelID[--elen])
180 return false;
181 }
182 return true;
183 }
184
185 /**
186 * @brief get data form raw PEL file.
187 * @param[in] std::string Name of file with raw PEL
188 * @return std::vector<uint8_t> char vector read from raw PEL file.
189 */
getFileData(const std::string & name)190 std::vector<uint8_t> getFileData(const std::string& name)
191 {
192 std::ifstream file(name, std::ifstream::in);
193 if (file.good())
194 {
195 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
196 std::istreambuf_iterator<char>()};
197 return data;
198 }
199 else
200 {
201 return {};
202 }
203 }
204
205 /**
206 * @brief Initialize Python interpreter and gather all UD parser modules under
207 * the paths found in Python sys.path and the current user directory.
208 * This is to prevent calling a non-existant module which causes Python
209 * to print an import error message and breaking JSON output.
210 *
211 * @return std::vector<std::string> Vector of plugins found in filesystem
212 */
getPlugins()213 std::vector<std::string> getPlugins()
214 {
215 Py_Initialize();
216 std::vector<std::string> plugins;
217 std::vector<std::string> siteDirs;
218 std::array<std::string, 2> parserDirs = {"udparsers", "srcparsers"};
219 PyObject* pName = PyUnicode_FromString("sys");
220 PyObject* pModule = PyImport_Import(pName);
221 Py_XDECREF(pName);
222 PyObject* pDict = PyModule_GetDict(pModule);
223 Py_XDECREF(pModule);
224 PyObject* pResult = PyDict_GetItemString(pDict, "path");
225 PyObject* pValue = PyUnicode_FromString(".");
226 PyList_Append(pResult, pValue);
227 Py_XDECREF(pValue);
228 auto list_size = PyList_Size(pResult);
229 for (auto i = 0; i < list_size; i++)
230 {
231 PyObject* item = PyList_GetItem(pResult, i);
232 PyObject* pBytes = PyUnicode_AsEncodedString(item, "utf-8", "~E~");
233 const char* output = PyBytes_AS_STRING(pBytes);
234 Py_XDECREF(pBytes);
235 std::string tmpStr(output);
236 siteDirs.push_back(tmpStr);
237 }
238 for (const auto& dir : siteDirs)
239 {
240 for (const auto& parserDir : parserDirs)
241 {
242 if (fs::exists(dir + "/" + parserDir))
243 {
244 for (const auto& entry :
245 fs::directory_iterator(dir + "/" + parserDir))
246 {
247 if (entry.is_directory() and
248 fs::exists(entry.path().string() + "/" +
249 entry.path().stem().string() + ".py"))
250 {
251 plugins.push_back(entry.path().stem());
252 }
253 }
254 }
255 }
256 }
257 return plugins;
258 }
259
260 /**
261 * @brief Creates JSON string of a PEL entry if fullPEL is false or prints to
262 * stdout the full PEL in JSON if fullPEL is true
263 * @param[in] itr - std::map iterator of <uint32_t, BCDTime>
264 * @param[in] hidden - Boolean to include hidden PELs
265 * @param[in] includeInfo - Boolean to include informational PELs
266 * @param[in] critSysTerm - Boolean to include critical error and system
267 * termination PELs
268 * @param[in] fullPEL - Boolean to print full JSON representation of PEL
269 * @param[in] foundPEL - Boolean to check if any PEL is present
270 * @param[in] scrubRegex - SRC regex object
271 * @param[in] plugins - Vector of strings of plugins found in filesystem
272 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
273 * @return std::string - JSON string of PEL entry (empty if fullPEL is true)
274 */
275 template <typename T>
genPELJSON(T itr,bool hidden,bool includeInfo,bool critSysTerm,bool fullPEL,bool & foundPEL,const std::optional<std::regex> & scrubRegex,const std::vector<std::string> & plugins,bool hexDump,bool archive)276 std::string genPELJSON(
277 T itr, bool hidden, bool includeInfo, bool critSysTerm, bool fullPEL,
278 bool& foundPEL, const std::optional<std::regex>& scrubRegex,
279 const std::vector<std::string>& plugins, bool hexDump, bool archive)
280 {
281 std::string val;
282 std::string listStr;
283 char name[51];
284 sprintf(name, "/%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X",
285 static_cast<uint8_t>((itr.second >> 56) & 0xFF),
286 static_cast<uint8_t>((itr.second >> 48) & 0xFF),
287 static_cast<uint8_t>((itr.second >> 40) & 0xFF),
288 static_cast<uint8_t>((itr.second >> 32) & 0xFF),
289 static_cast<uint8_t>((itr.second >> 24) & 0xFF),
290 static_cast<uint8_t>((itr.second >> 16) & 0xFF),
291 static_cast<uint8_t>((itr.second >> 8) & 0xFF),
292 static_cast<uint8_t>(itr.second & 0xFF), itr.first);
293
294 auto fileName = (archive ? pelLogDir() + "/archive" : pelLogDir()) + name;
295 try
296 {
297 std::vector<uint8_t> data = getFileData(fileName);
298 if (data.empty())
299 {
300 log<level::ERR>("Empty PEL file",
301 entry("FILENAME=%s", fileName.c_str()));
302 return listStr;
303 }
304 PEL pel{data};
305 if (!pel.valid())
306 {
307 return listStr;
308 }
309 if (!includeInfo && pel.userHeader().severity() == 0)
310 {
311 return listStr;
312 }
313 if (critSysTerm && pel.userHeader().severity() != critSysTermSeverity)
314 {
315 return listStr;
316 }
317 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
318 if (!hidden && actionFlags.test(hiddenFlagBit))
319 {
320 return listStr;
321 }
322 if (pel.primarySRC() && scrubRegex)
323 {
324 val = pel.primarySRC().value()->asciiString();
325 if (std::regex_search(trimEnd(val), scrubRegex.value(),
326 std::regex_constants::match_not_null))
327 {
328 return listStr;
329 }
330 }
331 if (hexDump)
332 {
333 std::cout
334 << dumpHex(std::data(pel.data()), pel.size(), 0, false).get()
335 << std::endl;
336 }
337 else if (fullPEL)
338 {
339 if (!foundPEL)
340 {
341 std::cout << "[\n";
342 foundPEL = true;
343 }
344 else
345 {
346 std::cout << ",\n\n";
347 }
348 pel.toJSON(registry, plugins);
349 }
350 else
351 {
352 // id
353 listStr += " \"" +
354 getNumberString("0x%X", pel.privateHeader().id()) +
355 "\": {\n";
356 // ASCII
357 if (pel.primarySRC())
358 {
359 val = pel.primarySRC().value()->asciiString();
360 jsonInsert(listStr, "SRC", trimEnd(val), 2);
361
362 // Registry message
363 auto regVal = pel.primarySRC().value()->getErrorDetails(
364 registry, DetailLevel::message, true);
365 if (regVal)
366 {
367 val = regVal.value();
368 jsonInsert(listStr, "Message", val, 2);
369 }
370 }
371 else
372 {
373 jsonInsert(listStr, "SRC", "No SRC", 2);
374 }
375
376 // platformid
377 jsonInsert(listStr, "PLID",
378 getNumberString("0x%X", pel.privateHeader().plid()), 2);
379
380 // creatorid
381 std::string creatorID =
382 getNumberString("%c", pel.privateHeader().creatorID());
383 val = pv::creatorIDs.count(creatorID) ? pv::creatorIDs.at(creatorID)
384 : "Unknown Creator ID";
385 jsonInsert(listStr, "CreatorID", val, 2);
386
387 // subsystem
388 std::string subsystem = pv::getValue(pel.userHeader().subsystem(),
389 pel_values::subsystemValues);
390 jsonInsert(listStr, "Subsystem", subsystem, 2);
391
392 // commit time
393 char tmpValStr[50];
394 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
395 pel.privateHeader().commitTimestamp().month,
396 pel.privateHeader().commitTimestamp().day,
397 pel.privateHeader().commitTimestamp().yearMSB,
398 pel.privateHeader().commitTimestamp().yearLSB,
399 pel.privateHeader().commitTimestamp().hour,
400 pel.privateHeader().commitTimestamp().minutes,
401 pel.privateHeader().commitTimestamp().seconds);
402 jsonInsert(listStr, "Commit Time", tmpValStr, 2);
403
404 // severity
405 std::string severity = pv::getValue(pel.userHeader().severity(),
406 pel_values::severityValues);
407 jsonInsert(listStr, "Sev", severity, 2);
408
409 // compID
410 jsonInsert(
411 listStr, "CompID",
412 getComponentName(pel.privateHeader().header().componentID,
413 pel.privateHeader().creatorID()),
414 2);
415
416 auto found = listStr.rfind(",");
417 if (found != std::string::npos)
418 {
419 listStr.replace(found, 1, "");
420 listStr += " },\n";
421 }
422 foundPEL = true;
423 }
424 }
425 catch (const std::exception& e)
426 {
427 log<level::ERR>("Hit exception while reading PEL File",
428 entry("FILENAME=%s", fileName.c_str()),
429 entry("ERROR=%s", e.what()));
430 }
431 return listStr;
432 }
433
434 /**
435 * @brief Print a list of PELs or a JSON array of PELs
436 * @param[in] order - Boolean to print in reverse orser
437 * @param[in] hidden - Boolean to include hidden PELs
438 * @param[in] includeInfo - Boolean to include informational PELs
439 * @param[in] critSysTerm - Boolean to include critical error and system
440 * termination PELs
441 * @param[in] fullPEL - Boolean to print full PEL into a JSON array
442 * @param[in] scrubRegex - SRC regex object
443 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
444 */
printPELs(bool order,bool hidden,bool includeInfo,bool critSysTerm,bool fullPEL,const std::optional<std::regex> & scrubRegex,bool hexDump,bool archive=false)445 void printPELs(bool order, bool hidden, bool includeInfo, bool critSysTerm,
446 bool fullPEL, const std::optional<std::regex>& scrubRegex,
447 bool hexDump, bool archive = false)
448 {
449 std::string listStr;
450 std::vector<std::pair<uint32_t, uint64_t>> PELs;
451 std::vector<std::string> plugins;
452 listStr = "{\n";
453 for (auto it = (archive ? fs::directory_iterator(pelLogDir() + "/archive")
454 : fs::directory_iterator(pelLogDir()));
455 it != fs::directory_iterator(); ++it)
456 {
457 if (!fs::is_regular_file((*it).path()))
458 {
459 continue;
460 }
461 else
462 {
463 PELs.emplace_back(fileNameToPELId((*it).path().filename()),
464 fileNameToTimestamp((*it).path().filename()));
465 }
466 }
467
468 // Sort the pairs based on second time parameter
469 std::sort(PELs.begin(), PELs.end(),
470 [](const auto& left, const auto& right) {
471 return left.second < right.second;
472 });
473
474 bool foundPEL = false;
475
476 if (fullPEL && !hexDump)
477 {
478 plugins = getPlugins();
479 }
480 auto buildJSON = [&listStr, &hidden, &includeInfo, &critSysTerm, &fullPEL,
481 &foundPEL, &scrubRegex, &plugins, &hexDump,
482 &archive](const auto& i) {
483 listStr += genPELJSON(i, hidden, includeInfo, critSysTerm, fullPEL,
484 foundPEL, scrubRegex, plugins, hexDump, archive);
485 };
486 if (order)
487 {
488 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
489 }
490 else
491 {
492 std::for_each(PELs.begin(), PELs.end(), buildJSON);
493 }
494 if (hexDump)
495 {
496 return;
497 }
498 if (foundPEL)
499 {
500 if (fullPEL)
501 {
502 std::cout << "]" << std::endl;
503 }
504 else
505 {
506 std::size_t found;
507 found = listStr.rfind(",");
508 if (found != std::string::npos)
509 {
510 listStr.replace(found, 1, "");
511 listStr += "}\n";
512 printf("%s", listStr.c_str());
513 }
514 }
515 }
516 else
517 {
518 std::string emptyJSON = fullPEL ? "[]" : "{}";
519 std::cout << emptyJSON << std::endl;
520 }
521 }
522
523 /**
524 * @brief Calls the function passed in on the PEL with the ID
525 * passed in.
526 *
527 * @param[in] id - The string version of the PEL or BMC Log ID, either with or
528 * without the 0x prefix.
529 * @param[in] func - The std::function<void(const PEL&, bool hexDump)> function
530 * to run.
531 * @param[in] useBMC - if true, search by BMC Log ID, else search by PEL ID
532 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
533 */
callFunctionOnPEL(const std::string & id,const PELFunc & func,bool useBMC=false,bool hexDump=false,bool archive=false)534 void callFunctionOnPEL(const std::string& id, const PELFunc& func,
535 bool useBMC = false, bool hexDump = false,
536 bool archive = false)
537 {
538 std::string pelID{id};
539 if (!useBMC)
540 {
541 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
542
543 if (pelID.starts_with("0X"))
544 {
545 pelID.erase(0, 2);
546 }
547 }
548
549 bool found = false;
550
551 for (auto it = (archive ? fs::directory_iterator(pelLogDir() + "/archive")
552 : fs::directory_iterator(pelLogDir()));
553 it != fs::directory_iterator(); ++it)
554 {
555 // The PEL ID is part of the filename, so use that to find the PEL if
556 // "useBMC" is set to false, otherwise we have to search within the PEL
557
558 if (!fs::is_regular_file((*it).path()))
559 {
560 continue;
561 }
562
563 if ((endsWithPelID((*it).path(), pelID) && !useBMC) || useBMC)
564 {
565 auto data = getFileData((*it).path());
566 if (!data.empty())
567 {
568 PEL pel{data};
569 if (!useBMC ||
570 (useBMC && pel.obmcLogID() == std::stoul(id, nullptr, 0)))
571 {
572 found = true;
573 try
574 {
575 func(pel, hexDump);
576 break;
577 }
578 catch (const std::exception& e)
579 {
580 std::cerr << " Internal function threw an exception: "
581 << e.what() << "\n";
582 exit(1);
583 }
584 }
585 }
586 else
587 {
588 std::cerr << "Could not read PEL file\n";
589 exit(1);
590 }
591 }
592 }
593
594 if (!found)
595 {
596 std::cerr << "PEL not found\n";
597 exit(1);
598 }
599 }
600
601 /**
602 * @brief Delete a PEL file.
603 *
604 * @param[in] id - The PEL ID to delete.
605 */
deletePEL(const std::string & id)606 void deletePEL(const std::string& id)
607 {
608 std::string pelID{id};
609
610 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
611
612 if (pelID.starts_with("0X"))
613 {
614 pelID.erase(0, 2);
615 }
616
617 for (auto it = fs::directory_iterator(pelLogDir());
618 it != fs::directory_iterator(); ++it)
619 {
620 if (endsWithPelID((*it).path(), pelID))
621 {
622 fs::remove((*it).path());
623 }
624 }
625 }
626
627 /**
628 * @brief Delete all PEL files.
629 */
deleteAllPELs()630 void deleteAllPELs()
631 {
632 log<level::INFO>("peltool deleting all event logs");
633
634 for (const auto& entry : fs::directory_iterator(pelLogDir()))
635 {
636 if (!fs::is_regular_file(entry.path()))
637 {
638 continue;
639 }
640 fs::remove(entry.path());
641 }
642 }
643
644 /**
645 * @brief Display a single PEL
646 *
647 * @param[in] pel - the PEL to display
648 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
649 */
displayPEL(const PEL & pel,bool hexDump)650 void displayPEL(const PEL& pel, bool hexDump)
651 {
652 if (pel.valid())
653 {
654 if (hexDump)
655 {
656 std::string dstr =
657 dumpHex(std::data(pel.data()), pel.size(), 0, false).get();
658 std::cout << dstr << std::endl;
659 }
660 else
661 {
662 auto plugins = getPlugins();
663 pel.toJSON(registry, plugins);
664 }
665 }
666 else
667 {
668 std::cerr << "PEL was malformed\n";
669 exit(1);
670 }
671 }
672
673 /**
674 * @brief Print number of PELs
675 * @param[in] hidden - Bool to include hidden logs
676 * @param[in] includeInfo - Bool to include informational logs
677 * @param[in] critSysTerm - Bool to include CritSysTerm
678 * @param[in] scrubRegex - SRC regex object
679 */
printPELCount(bool hidden,bool includeInfo,bool critSysTerm,const std::optional<std::regex> & scrubRegex)680 void printPELCount(bool hidden, bool includeInfo, bool critSysTerm,
681 const std::optional<std::regex>& scrubRegex)
682 {
683 std::size_t count = 0;
684
685 for (auto it = fs::directory_iterator(pelLogDir());
686 it != fs::directory_iterator(); ++it)
687 {
688 if (!fs::is_regular_file((*it).path()))
689 {
690 continue;
691 }
692 std::vector<uint8_t> data = getFileData((*it).path());
693 if (data.empty())
694 {
695 continue;
696 }
697 PEL pel{data};
698 if (!pel.valid())
699 {
700 continue;
701 }
702 if (!includeInfo && pel.userHeader().severity() == 0)
703 {
704 continue;
705 }
706 if (critSysTerm && pel.userHeader().severity() != critSysTermSeverity)
707 {
708 continue;
709 }
710 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
711 if (!hidden && actionFlags.test(hiddenFlagBit))
712 {
713 continue;
714 }
715 if (pel.primarySRC() && scrubRegex)
716 {
717 std::string val = pel.primarySRC().value()->asciiString();
718 if (std::regex_search(trimEnd(val), scrubRegex.value(),
719 std::regex_constants::match_not_null))
720 {
721 continue;
722 }
723 }
724 count++;
725 }
726 std::cout << "{\n"
727 << " \"Number of PELs found\": "
728 << getNumberString("%d", count) << "\n}\n";
729 }
730
731 /**
732 * @brief Generate regex pattern object from file contents
733 * @param[in] scrubFile - File containing regex pattern
734 * @return std::regex - SRC regex object
735 */
genRegex(std::string & scrubFile)736 std::regex genRegex(std::string& scrubFile)
737 {
738 std::string pattern;
739 std::ifstream contents(scrubFile);
740 if (contents.fail())
741 {
742 std::cerr << "Can't open \"" << scrubFile << "\"\n";
743 exit(1);
744 }
745 std::string line;
746 while (std::getline(contents, line))
747 {
748 if (!line.empty())
749 {
750 pattern.append(line + "|");
751 }
752 }
753 try
754 {
755 std::regex scrubRegex(pattern, std::regex::icase);
756 return scrubRegex;
757 }
758 catch (const std::regex_error& e)
759 {
760 if (e.code() == std::regex_constants::error_collate)
761 std::cerr << "Invalid collating element request\n";
762 else if (e.code() == std::regex_constants::error_ctype)
763 std::cerr << "Invalid character class\n";
764 else if (e.code() == std::regex_constants::error_escape)
765 std::cerr << "Invalid escape character or trailing escape\n";
766 else if (e.code() == std::regex_constants::error_backref)
767 std::cerr << "Invalid back reference\n";
768 else if (e.code() == std::regex_constants::error_brack)
769 std::cerr << "Mismatched bracket ([ or ])\n";
770 else if (e.code() == std::regex_constants::error_paren)
771 {
772 // to catch return code error_badrepeat when error_paren is retured
773 // instead
774 size_t pos = pattern.find_first_of("*+?{");
775 while (pos != std::string::npos)
776 {
777 if (pos == 0 || pattern.substr(pos - 1, 1) == "|")
778 {
779 std::cerr
780 << "A repetition character (*, ?, +, or {) was not "
781 "preceded by a valid regular expression\n";
782 exit(1);
783 }
784 pos = pattern.find_first_of("*+?{", pos + 1);
785 }
786 std::cerr << "Mismatched parentheses (( or ))\n";
787 }
788 else if (e.code() == std::regex_constants::error_brace)
789 std::cerr << "Mismatched brace ({ or })\n";
790 else if (e.code() == std::regex_constants::error_badbrace)
791 std::cerr << "Invalid range inside a { }\n";
792 else if (e.code() == std::regex_constants::error_range)
793 std::cerr << "Invalid character range (e.g., [z-a])\n";
794 else if (e.code() == std::regex_constants::error_space)
795 std::cerr << "Insufficient memory to handle regular expression\n";
796 else if (e.code() == std::regex_constants::error_badrepeat)
797 std::cerr << "A repetition character (*, ?, +, or {) was not "
798 "preceded by a valid regular expression\n";
799 else if (e.code() == std::regex_constants::error_complexity)
800 std::cerr << "The requested match is too complex\n";
801 else if (e.code() == std::regex_constants::error_stack)
802 std::cerr << "Insufficient memory to evaluate a match\n";
803 exit(1);
804 }
805 }
806
exitWithError(const std::string & help,const char * err)807 static void exitWithError(const std::string& help, const char* err)
808 {
809 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
810 exit(-1);
811 }
812
main(int argc,char ** argv)813 int main(int argc, char** argv)
814 {
815 CLI::App app{"OpenBMC PEL Tool"};
816 std::string fileName;
817 std::string idPEL;
818 std::string bmcId;
819 std::string idToDelete;
820 std::string scrubFile;
821 std::optional<std::regex> scrubRegex;
822 bool listPEL = false;
823 bool listPELDescOrd = false;
824 bool hidden = false;
825 bool includeInfo = false;
826 bool critSysTerm = false;
827 bool deleteAll = false;
828 bool showPELCount = false;
829 bool fullPEL = false;
830 bool hexDump = false;
831 bool archive = false;
832
833 app.set_help_flag("--help", "Print this help message and exit");
834 app.add_option("--file", fileName, "Display a PEL using its Raw PEL file");
835 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
836 app.add_option("--bmc-id", bmcId,
837 "Display a PEL based on its BMC Event ID");
838 app.add_flag("-a", fullPEL, "Display all PELs");
839 app.add_flag("-l", listPEL, "List PELs");
840 app.add_flag("-n", showPELCount, "Show number of PELs");
841 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
842 app.add_flag("-h", hidden, "Include hidden PELs");
843 app.add_flag("-f,--info", includeInfo, "Include informational PELs");
844 app.add_flag("-t, --termination", critSysTerm,
845 "List only critical system terminating PELs");
846 app.add_option("-d, --delete", idToDelete, "Delete a PEL based on its ID");
847 app.add_flag("-D, --delete-all", deleteAll, "Delete all PELs");
848 app.add_option("-s, --scrub", scrubFile,
849 "File containing SRC regular expressions to ignore");
850 app.add_flag("-x", hexDump, "Display PEL(s) in hexdump instead of JSON");
851 app.add_flag("--archive", archive, "List or display archived PELs");
852
853 CLI11_PARSE(app, argc, argv);
854
855 if (!fileName.empty())
856 {
857 std::vector<uint8_t> data = getFileData(fileName);
858 if (!data.empty())
859 {
860 PEL pel{data};
861 if (hexDump)
862 {
863 std::string dstr =
864 dumpHex(std::data(pel.data()), pel.size(), 0, false).get();
865 std::cout << dstr << std::endl;
866 }
867 else
868 {
869 auto plugins = getPlugins();
870 pel.toJSON(registry, plugins);
871 }
872 }
873 else
874 {
875 exitWithError(app.help("", CLI::AppFormatMode::All),
876 "Raw PEL file can't be read.");
877 }
878 }
879 else if (!idPEL.empty())
880 {
881 callFunctionOnPEL(idPEL, displayPEL, false, hexDump, archive);
882 }
883 else if (!bmcId.empty())
884 {
885 callFunctionOnPEL(bmcId, displayPEL, true, hexDump, archive);
886 }
887 else if (fullPEL || listPEL)
888 {
889 if (!scrubFile.empty())
890 {
891 scrubRegex = genRegex(scrubFile);
892 }
893 printPELs(listPELDescOrd, hidden, includeInfo, critSysTerm, fullPEL,
894 scrubRegex, hexDump, archive);
895 }
896 else if (showPELCount)
897 {
898 if (!scrubFile.empty())
899 {
900 scrubRegex = genRegex(scrubFile);
901 }
902 printPELCount(hidden, includeInfo, critSysTerm, scrubRegex);
903 }
904 else if (!idToDelete.empty())
905 {
906 deletePEL(idToDelete);
907 }
908 else if (deleteAll)
909 {
910 deleteAllPELs();
911 }
912 else
913 {
914 std::cout << app.help("", CLI::AppFormatMode::All) << std::endl;
915 }
916 Py_Finalize();
917 return 0;
918 }
919