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