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