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