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 "config.h" 17 18 #include "../bcd_time.hpp" 19 #include "../json_utils.hpp" 20 #include "../paths.hpp" 21 #include "../pel.hpp" 22 #include "../pel_types.hpp" 23 #include "../pel_values.hpp" 24 25 #include <Python.h> 26 27 #include <CLI/CLI.hpp> 28 #include <bitset> 29 #include <fstream> 30 #include <iostream> 31 #include <phosphor-logging/log.hpp> 32 #include <regex> 33 #include <string> 34 #include <xyz/openbmc_project/Common/File/error.hpp> 35 36 #include "config_main.h" 37 38 namespace fs = std::filesystem; 39 using namespace phosphor::logging; 40 using namespace openpower::pels; 41 namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error; 42 namespace message = openpower::pels::message; 43 namespace pv = openpower::pels::pel_values; 44 45 const uint8_t critSysTermSeverity = 0x51; 46 47 using PELFunc = std::function<void(const PEL&, bool hexDump)>; 48 message::Registry registry(getPELReadOnlyDataPath() / message::registryFileName, 49 false); 50 namespace service 51 { 52 constexpr auto logging = "xyz.openbmc_project.Logging"; 53 } // namespace service 54 55 namespace interface 56 { 57 constexpr auto deleteObj = "xyz.openbmc_project.Object.Delete"; 58 constexpr auto deleteAll = "xyz.openbmc_project.Collection.DeleteAll"; 59 } // namespace interface 60 61 namespace object_path 62 { 63 constexpr auto logEntry = "/xyz/openbmc_project/logging/entry/"; 64 constexpr auto logging = "/xyz/openbmc_project/logging"; 65 } // namespace object_path 66 67 std::string pelLogDir() 68 { 69 return std::string(EXTENSION_PERSIST_DIR) + "/pels/logs"; 70 } 71 72 /** 73 * @brief helper function to get PEL commit timestamp from file name 74 * @retrun BCDTime - PEL commit timestamp 75 * @param[in] std::string - file name 76 */ 77 BCDTime fileNameToTimestamp(const std::string& fileName) 78 { 79 std::string token = fileName.substr(0, fileName.find("_")); 80 int i = 0; 81 BCDTime tmp; 82 if (token.length() >= 14) 83 { 84 try 85 { 86 tmp.yearMSB = std::stoul(token.substr(i, 2), 0, 16); 87 } 88 catch (const std::exception& err) 89 { 90 std::cout << "Conversion failure: " << err.what() << std::endl; 91 } 92 i += 2; 93 try 94 { 95 tmp.yearLSB = std::stoul(token.substr(i, 2), 0, 16); 96 } 97 catch (const std::exception& err) 98 { 99 std::cout << "Conversion failure: " << err.what() << std::endl; 100 } 101 i += 2; 102 try 103 { 104 tmp.month = std::stoul(token.substr(i, 2), 0, 16); 105 } 106 catch (const std::exception& err) 107 { 108 std::cout << "Conversion failure: " << err.what() << std::endl; 109 } 110 i += 2; 111 try 112 { 113 tmp.day = std::stoul(token.substr(i, 2), 0, 16); 114 } 115 catch (const std::exception& err) 116 { 117 std::cout << "Conversion failure: " << err.what() << std::endl; 118 } 119 i += 2; 120 try 121 { 122 tmp.hour = std::stoul(token.substr(i, 2), 0, 16); 123 } 124 catch (const std::exception& err) 125 { 126 std::cout << "Conversion failure: " << err.what() << std::endl; 127 } 128 i += 2; 129 try 130 { 131 tmp.minutes = std::stoul(token.substr(i, 2), 0, 16); 132 } 133 catch (const std::exception& err) 134 { 135 std::cout << "Conversion failure: " << err.what() << std::endl; 136 } 137 i += 2; 138 try 139 { 140 tmp.seconds = std::stoul(token.substr(i, 2), 0, 16); 141 } 142 catch (const std::exception& err) 143 { 144 std::cout << "Conversion failure: " << err.what() << std::endl; 145 } 146 i += 2; 147 try 148 { 149 tmp.hundredths = std::stoul(token.substr(i, 2), 0, 16); 150 } 151 catch (const std::exception& err) 152 { 153 std::cout << "Conversion failure: " << err.what() << std::endl; 154 } 155 } 156 return tmp; 157 } 158 159 /** 160 * @brief helper function to get PEL id from file name 161 * @retrun uint32_t - PEL id 162 * @param[in] std::string - file name 163 */ 164 uint32_t fileNameToPELId(const std::string& fileName) 165 { 166 uint32_t num = 0; 167 try 168 { 169 num = std::stoul(fileName.substr(fileName.find("_") + 1), 0, 16); 170 } 171 catch (const std::exception& err) 172 { 173 std::cout << "Conversion failure: " << err.what() << std::endl; 174 } 175 return num; 176 } 177 178 /** 179 * @brief helper function to check string suffix 180 * @retrun bool - true with suffix matches 181 * @param[in] std::string - string to check for suffix 182 * @param[in] std::string - suffix string 183 */ 184 bool ends_with(const std::string& str, const std::string& end) 185 { 186 size_t slen = str.size(), elen = end.size(); 187 if (slen < elen) 188 return false; 189 while (elen) 190 { 191 if (str[--slen] != end[--elen]) 192 return false; 193 } 194 return true; 195 } 196 197 /** 198 * @brief get data form raw PEL file. 199 * @param[in] std::string Name of file with raw PEL 200 * @return std::vector<uint8_t> char vector read from raw PEL file. 201 */ 202 std::vector<uint8_t> getFileData(const std::string& name) 203 { 204 std::ifstream file(name, std::ifstream::in); 205 if (file.good()) 206 { 207 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file), 208 std::istreambuf_iterator<char>()}; 209 return data; 210 } 211 else 212 { 213 return {}; 214 } 215 } 216 217 /** 218 * @brief Initialize Python interpreter and gather all UD parser modules under 219 * the paths found in Python sys.path and the current user directory. 220 * This is to prevent calling a non-existant module which causes Python 221 * to print an import error message and breaking JSON output. 222 * 223 * @return std::vector<std::string> Vector of plugins found in filesystem 224 */ 225 std::vector<std::string> getPlugins() 226 { 227 Py_Initialize(); 228 std::vector<std::string> plugins; 229 std::vector<std::string> siteDirs; 230 std::array<std::string, 2> parserDirs = {"udparsers", "srcparsers"}; 231 PyObject* pName = PyUnicode_FromString("sys"); 232 PyObject* pModule = PyImport_Import(pName); 233 Py_XDECREF(pName); 234 PyObject* pDict = PyModule_GetDict(pModule); 235 Py_XDECREF(pModule); 236 PyObject* pResult = PyDict_GetItemString(pDict, "path"); 237 PyObject* pValue = PyUnicode_FromString("."); 238 PyList_Append(pResult, pValue); 239 Py_XDECREF(pValue); 240 auto list_size = PyList_Size(pResult); 241 for (auto i = 0; i < list_size; i++) 242 { 243 PyObject* item = PyList_GetItem(pResult, i); 244 PyObject* pBytes = PyUnicode_AsEncodedString(item, "utf-8", "~E~"); 245 const char* output = PyBytes_AS_STRING(pBytes); 246 Py_XDECREF(pBytes); 247 std::string tmpStr(output); 248 siteDirs.push_back(tmpStr); 249 } 250 for (const auto& dir : siteDirs) 251 { 252 for (const auto& parserDir : parserDirs) 253 { 254 if (fs::exists(dir + "/" + parserDir)) 255 { 256 for (const auto& entry : 257 fs::directory_iterator(dir + "/" + parserDir)) 258 { 259 if (entry.is_directory() and 260 fs::exists(entry.path().string() + "/" + 261 entry.path().stem().string() + ".py")) 262 { 263 plugins.push_back(entry.path().stem()); 264 } 265 } 266 } 267 } 268 } 269 return plugins; 270 } 271 272 /** 273 * @brief Creates JSON string of a PEL entry if fullPEL is false or prints to 274 * stdout the full PEL in JSON if fullPEL is true 275 * @param[in] itr - std::map iterator of <uint32_t, BCDTime> 276 * @param[in] hidden - Boolean to include hidden PELs 277 * @param[in] includeInfo - Boolean to include informational PELs 278 * @param[in] critSysTerm - Boolean to include critical error and system 279 * termination PELs 280 * @param[in] fullPEL - Boolean to print full JSON representation of PEL 281 * @param[in] foundPEL - Boolean to check if any PEL is present 282 * @param[in] scrubRegex - SRC regex object 283 * @param[in] plugins - Vector of strings of plugins found in filesystem 284 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON 285 * @return std::string - JSON string of PEL entry (empty if fullPEL is true) 286 */ 287 template <typename T> 288 std::string genPELJSON(T itr, bool hidden, bool includeInfo, bool critSysTerm, 289 bool fullPEL, bool& foundPEL, 290 const std::optional<std::regex>& scrubRegex, 291 const std::vector<std::string>& plugins, bool hexDump, 292 bool archive) 293 { 294 std::size_t found; 295 std::string val; 296 char tmpValStr[50]; 297 std::string listStr; 298 char name[51]; 299 sprintf(name, "/%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB, 300 itr.second.yearLSB, itr.second.month, itr.second.day, 301 itr.second.hour, itr.second.minutes, itr.second.seconds, 302 itr.second.hundredths, itr.first); 303 auto fileName = (archive ? pelLogDir() + "/archive" : pelLogDir()) + name; 304 try 305 { 306 std::vector<uint8_t> data = getFileData(fileName); 307 if (data.empty()) 308 { 309 log<level::ERR>("Empty PEL file", 310 entry("FILENAME=%s", fileName.c_str())); 311 return listStr; 312 } 313 PEL pel{data}; 314 if (!pel.valid()) 315 { 316 return listStr; 317 } 318 if (!includeInfo && pel.userHeader().severity() == 0) 319 { 320 return listStr; 321 } 322 if (critSysTerm && pel.userHeader().severity() != critSysTermSeverity) 323 { 324 return listStr; 325 } 326 std::bitset<16> actionFlags{pel.userHeader().actionFlags()}; 327 if (!hidden && actionFlags.test(hiddenFlagBit)) 328 { 329 return listStr; 330 } 331 if (pel.primarySRC() && scrubRegex) 332 { 333 val = pel.primarySRC().value()->asciiString(); 334 if (std::regex_search(trimEnd(val), scrubRegex.value(), 335 std::regex_constants::match_not_null)) 336 { 337 return listStr; 338 } 339 } 340 if (hexDump) 341 { 342 std::cout << dumpHex(std::data(pel.data()), pel.size(), 0, false) 343 << std::endl; 344 } 345 else if (fullPEL) 346 { 347 if (!foundPEL) 348 { 349 std::cout << "[\n"; 350 foundPEL = true; 351 } 352 else 353 { 354 std::cout << ",\n\n"; 355 } 356 pel.toJSON(registry, plugins); 357 } 358 else 359 { 360 // id 361 listStr += " \"" + 362 getNumberString("0x%X", pel.privateHeader().id()) + 363 "\": {\n"; 364 // ASCII 365 if (pel.primarySRC()) 366 { 367 val = pel.primarySRC().value()->asciiString(); 368 jsonInsert(listStr, "SRC", trimEnd(val), 2); 369 370 // Registry message 371 auto regVal = pel.primarySRC().value()->getErrorDetails( 372 registry, DetailLevel::message, true); 373 if (regVal) 374 { 375 val = regVal.value(); 376 jsonInsert(listStr, "Message", val, 2); 377 } 378 } 379 else 380 { 381 jsonInsert(listStr, "SRC", "No SRC", 2); 382 } 383 384 // platformid 385 jsonInsert(listStr, "PLID", 386 getNumberString("0x%X", pel.privateHeader().plid()), 2); 387 388 // creatorid 389 std::string creatorID = 390 getNumberString("%c", pel.privateHeader().creatorID()); 391 val = pv::creatorIDs.count(creatorID) ? pv::creatorIDs.at(creatorID) 392 : "Unknown Creator ID"; 393 jsonInsert(listStr, "CreatorID", val, 2); 394 395 // subsystem 396 std::string subsystem = pv::getValue(pel.userHeader().subsystem(), 397 pel_values::subsystemValues); 398 jsonInsert(listStr, "Subsystem", subsystem, 2); 399 400 // commit time 401 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X", 402 pel.privateHeader().commitTimestamp().month, 403 pel.privateHeader().commitTimestamp().day, 404 pel.privateHeader().commitTimestamp().yearMSB, 405 pel.privateHeader().commitTimestamp().yearLSB, 406 pel.privateHeader().commitTimestamp().hour, 407 pel.privateHeader().commitTimestamp().minutes, 408 pel.privateHeader().commitTimestamp().seconds); 409 jsonInsert(listStr, "Commit Time", tmpValStr, 2); 410 411 // severity 412 std::string severity = pv::getValue(pel.userHeader().severity(), 413 pel_values::severityValues); 414 jsonInsert(listStr, "Sev", severity, 2); 415 416 // compID 417 jsonInsert(listStr, "CompID", 418 getNumberString( 419 "0x%X", pel.privateHeader().header().componentID), 420 2); 421 422 found = listStr.rfind(","); 423 if (found != std::string::npos) 424 { 425 listStr.replace(found, 1, ""); 426 listStr += " },\n"; 427 } 428 foundPEL = true; 429 } 430 } 431 catch (const std::exception& e) 432 { 433 log<level::ERR>("Hit exception while reading PEL File", 434 entry("FILENAME=%s", fileName.c_str()), 435 entry("ERROR=%s", e.what())); 436 } 437 return listStr; 438 } 439 440 /** 441 * @brief Print a list of PELs or a JSON array of PELs 442 * @param[in] order - Boolean to print in reverse orser 443 * @param[in] hidden - Boolean to include hidden PELs 444 * @param[in] includeInfo - Boolean to include informational PELs 445 * @param[in] critSysTerm - Boolean to include critical error and system 446 * termination PELs 447 * @param[in] fullPEL - Boolean to print full PEL into a JSON array 448 * @param[in] scrubRegex - SRC regex object 449 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON 450 */ 451 void printPELs(bool order, bool hidden, bool includeInfo, bool critSysTerm, 452 bool fullPEL, const std::optional<std::regex>& scrubRegex, 453 bool hexDump, bool archive = false) 454 { 455 std::string listStr; 456 std::map<uint32_t, BCDTime> PELs; 457 std::vector<std::string> plugins; 458 listStr = "{\n"; 459 for (auto it = (archive ? fs::directory_iterator(pelLogDir() + "/archive") 460 : fs::directory_iterator(pelLogDir())); 461 it != fs::directory_iterator(); ++it) 462 { 463 if (!fs::is_regular_file((*it).path())) 464 { 465 continue; 466 } 467 else 468 { 469 PELs.emplace(fileNameToPELId((*it).path().filename()), 470 fileNameToTimestamp((*it).path().filename())); 471 } 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 */ 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.find("0X") == 0) 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 ((ends_with((*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 */ 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.find("0X") == 0) 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 (ends_with((*it).path(), pelID)) 621 { 622 fs::remove((*it).path()); 623 } 624 } 625 } 626 627 /** 628 * @brief Delete all PEL files. 629 */ 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 */ 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); 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 */ 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 */ 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 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 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); 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