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