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