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