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