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