blob: fe3afac754cb84522c39e7bf6fb477d01601eb9d [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
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 */
Aatir7b291ec2019-11-19 10:37:37 -060016#include "config.h"
17
18#include "../bcd_time.hpp"
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +080019#include "../json_utils.hpp"
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080020#include "../paths.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050021#include "../pel.hpp"
Aatir7b291ec2019-11-19 10:37:37 -060022#include "../pel_types.hpp"
23#include "../pel_values.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050024
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +080025#include <Python.h>
26
Aatir186ce8c2019-10-20 15:13:39 -050027#include <CLI/CLI.hpp>
Aatir7b291ec2019-11-19 10:37:37 -060028#include <bitset>
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +080029#include <fstream>
Aatir186ce8c2019-10-20 15:13:39 -050030#include <iostream>
Aatir7b291ec2019-11-19 10:37:37 -060031#include <phosphor-logging/log.hpp>
32#include <regex>
Aatir186ce8c2019-10-20 15:13:39 -050033#include <string>
Aatir7b291ec2019-11-19 10:37:37 -060034#include <xyz/openbmc_project/Common/File/error.hpp>
Aatir186ce8c2019-10-20 15:13:39 -050035
Aatir7b291ec2019-11-19 10:37:37 -060036namespace fs = std::filesystem;
Aatir186ce8c2019-10-20 15:13:39 -050037using namespace phosphor::logging;
38using namespace openpower::pels;
Matt Spinler27de6f32020-02-21 08:35:57 -060039using sdbusplus::exception::SdBusError;
Aatir7b291ec2019-11-19 10:37:37 -060040namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
41namespace message = openpower::pels::message;
42namespace pv = openpower::pels::pel_values;
Aatir186ce8c2019-10-20 15:13:39 -050043
Miguel Gomez011ccae2021-03-25 23:42:09 +000044const uint8_t critSysTermSeverity = 0x51;
45
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +080046using PELFunc = std::function<void(const PEL&, bool hexDump)>;
Matt Spinler0d804ef2020-05-12 16:16:26 -050047message::Registry registry(getPELReadOnlyDataPath() / message::registryFileName,
Matt Spinlerd8e29002020-04-09 09:11:22 -050048 false);
Matt Spinler27de6f32020-02-21 08:35:57 -060049namespace service
50{
51constexpr auto logging = "xyz.openbmc_project.Logging";
52} // namespace service
53
54namespace interface
55{
56constexpr auto deleteObj = "xyz.openbmc_project.Object.Delete";
57constexpr auto deleteAll = "xyz.openbmc_project.Collection.DeleteAll";
58} // namespace interface
59
60namespace object_path
61{
62constexpr auto logEntry = "/xyz/openbmc_project/logging/entry/";
63constexpr auto logging = "/xyz/openbmc_project/logging";
64} // namespace object_path
65
Aatire340c132019-12-09 14:19:29 -060066/**
Aatir37822f62019-12-10 14:40:27 -060067 * @brief helper function to get PEL commit timestamp from file name
68 * @retrun BCDTime - PEL commit timestamp
69 * @param[in] std::string - file name
70 */
71BCDTime fileNameToTimestamp(const std::string& fileName)
72{
73 std::string token = fileName.substr(0, fileName.find("_"));
74 int i = 0;
75 BCDTime tmp;
76 if (token.length() >= 14)
77 {
78 try
79 {
80 tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
81 }
82 catch (std::exception& err)
83 {
84 std::cout << "Conversion failure: " << err.what() << std::endl;
85 }
86 i += 2;
87 try
88 {
89 tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
90 }
91 catch (std::exception& err)
92 {
93 std::cout << "Conversion failure: " << err.what() << std::endl;
94 }
95 i += 2;
96 try
97 {
98 tmp.month = std::stoi(token.substr(i, 2), 0, 16);
99 }
100 catch (std::exception& err)
101 {
102 std::cout << "Conversion failure: " << err.what() << std::endl;
103 }
104 i += 2;
105 try
106 {
107 tmp.day = std::stoi(token.substr(i, 2), 0, 16);
108 }
109 catch (std::exception& err)
110 {
111 std::cout << "Conversion failure: " << err.what() << std::endl;
112 }
113 i += 2;
114 try
115 {
116 tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
117 }
118 catch (std::exception& err)
119 {
120 std::cout << "Conversion failure: " << err.what() << std::endl;
121 }
122 i += 2;
123 try
124 {
125 tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
126 }
127 catch (std::exception& err)
128 {
129 std::cout << "Conversion failure: " << err.what() << std::endl;
130 }
131 i += 2;
132 try
133 {
134 tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
135 }
136 catch (std::exception& err)
137 {
138 std::cout << "Conversion failure: " << err.what() << std::endl;
139 }
140 i += 2;
141 try
142 {
143 tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
144 }
145 catch (std::exception& err)
146 {
147 std::cout << "Conversion failure: " << err.what() << std::endl;
148 }
149 }
150 return tmp;
151}
152
153/**
154 * @brief helper function to get PEL id from file name
155 * @retrun uint32_t - PEL id
156 * @param[in] std::string - file name
157 */
158uint32_t fileNameToPELId(const std::string& fileName)
159{
160 uint32_t num = 0;
161 try
162 {
163 num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
164 }
165 catch (std::exception& err)
166 {
167 std::cout << "Conversion failure: " << err.what() << std::endl;
168 }
169 return num;
170}
171
172/**
Aatire340c132019-12-09 14:19:29 -0600173 * @brief helper function to check string suffix
174 * @retrun bool - true with suffix matches
175 * @param[in] std::string - string to check for suffix
176 * @param[in] std::string - suffix string
177 */
178bool ends_with(const std::string& str, const std::string& end)
179{
180 size_t slen = str.size(), elen = end.size();
181 if (slen < elen)
182 return false;
183 while (elen)
184 {
185 if (str[--slen] != end[--elen])
186 return false;
187 }
188 return true;
189}
190
Aatir37822f62019-12-10 14:40:27 -0600191/**
192 * @brief get data form raw PEL file.
193 * @param[in] std::string Name of file with raw PEL
194 * @return std::vector<uint8_t> char vector read from raw PEL file.
195 */
Aatirbad5f8a2019-12-10 15:27:16 -0600196std::vector<uint8_t> getFileData(const std::string& name)
Aatir37822f62019-12-10 14:40:27 -0600197{
198 std::ifstream file(name, std::ifstream::in);
199 if (file.good())
200 {
201 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
202 std::istreambuf_iterator<char>()};
203 return data;
204 }
205 else
206 {
Aatir37822f62019-12-10 14:40:27 -0600207 return {};
208 }
209}
210
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800211/**
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800212 * @brief Initialize Python interpreter and gather all UD parser modules under
213 * the paths found in Python sys.path and the current user directory.
214 * This is to prevent calling a non-existant module which causes Python
215 * to print an import error message and breaking JSON output.
216 *
217 * @return std::vector<std::string> Vector of plugins found in filesystem
218 */
219std::vector<std::string> getPlugins()
220{
221 Py_Initialize();
222 std::vector<std::string> plugins;
223 std::vector<std::string> siteDirs;
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800224 std::array<std::string, 2> parserDirs = {"udparsers", "srcparsers"};
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800225 PyObject* pName = PyUnicode_FromString("sys");
226 PyObject* pModule = PyImport_Import(pName);
227 Py_XDECREF(pName);
228 PyObject* pDict = PyModule_GetDict(pModule);
229 Py_XDECREF(pModule);
230 PyObject* pResult = PyDict_GetItemString(pDict, "path");
231 PyObject* pValue = PyUnicode_FromString(".");
232 PyList_Append(pResult, pValue);
233 Py_XDECREF(pValue);
234 auto list_size = PyList_Size(pResult);
235 for (auto i = 0; i < list_size; i++)
236 {
237 PyObject* item = PyList_GetItem(pResult, i);
238 PyObject* pBytes = PyUnicode_AsEncodedString(item, "utf-8", "~E~");
239 const char* output = PyBytes_AS_STRING(pBytes);
240 Py_XDECREF(pBytes);
241 std::string tmpStr(output);
242 siteDirs.push_back(tmpStr);
243 }
244 for (const auto& dir : siteDirs)
245 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800246 for (const auto& parserDir : parserDirs)
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800247 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800248 if (fs::exists(dir + "/" + parserDir))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800249 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800250 for (const auto& entry :
251 fs::directory_iterator(dir + "/" + parserDir))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800252 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800253 if (entry.is_directory() and
254 fs::exists(entry.path().string() + "/" +
255 entry.path().stem().string() + ".py"))
256 {
257 plugins.push_back(entry.path().stem());
258 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800259 }
260 }
261 }
262 }
263 return plugins;
264}
265
266/**
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800267 * @brief Creates JSON string of a PEL entry if fullPEL is false or prints to
268 * stdout the full PEL in JSON if fullPEL is true
269 * @param[in] itr - std::map iterator of <uint32_t, BCDTime>
270 * @param[in] hidden - Boolean to include hidden PELs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800271 * @param[in] includeInfo - Boolean to include informational PELs
Miguel Gomez011ccae2021-03-25 23:42:09 +0000272 * @param[in] critSysTerm - Boolean to include critical error and system
273 * termination PELs
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800274 * @param[in] fullPEL - Boolean to print full JSON representation of PEL
275 * @param[in] foundPEL - Boolean to check if any PEL is present
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800276 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800277 * @param[in] plugins - Vector of strings of plugins found in filesystem
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800278 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800279 * @return std::string - JSON string of PEL entry (empty if fullPEL is true)
280 */
Aatir7b291ec2019-11-19 10:37:37 -0600281template <typename T>
Miguel Gomez011ccae2021-03-25 23:42:09 +0000282std::string genPELJSON(T itr, bool hidden, bool includeInfo, bool critSysTerm,
283 bool fullPEL, bool& foundPEL,
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800284 const std::optional<std::regex>& scrubRegex,
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800285 const std::vector<std::string>& plugins, bool hexDump)
Aatir7b291ec2019-11-19 10:37:37 -0600286{
287 std::size_t found;
288 std::string val;
289 char tmpValStr[50];
290 std::string listStr;
291 char name[50];
292 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
293 itr.second.yearLSB, itr.second.month, itr.second.day,
294 itr.second.hour, itr.second.minutes, itr.second.seconds,
295 itr.second.hundredths, itr.first);
296 std::string fileName(name);
297 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
298 try
299 {
Aatir37822f62019-12-10 14:40:27 -0600300 std::vector<uint8_t> data = getFileData(fileName);
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800301 if (data.empty())
Aatir37822f62019-12-10 14:40:27 -0600302 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800303 log<level::ERR>("Empty PEL file",
304 entry("FILENAME=%s", fileName.c_str()));
305 return listStr;
306 }
307 PEL pel{data};
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800308 if (!pel.valid())
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800309 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800310 return listStr;
311 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800312 if (!includeInfo && pel.userHeader().severity() == 0)
313 {
314 return listStr;
315 }
Miguel Gomez011ccae2021-03-25 23:42:09 +0000316 if (critSysTerm && pel.userHeader().severity() != critSysTermSeverity)
317 {
318 return listStr;
319 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800320 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
321 if (!hidden && actionFlags.test(hiddenFlagBit))
322 {
323 return listStr;
324 }
325 if (pel.primarySRC() && scrubRegex)
326 {
327 val = pel.primarySRC().value()->asciiString();
328 if (std::regex_search(trimEnd(val), scrubRegex.value(),
329 std::regex_constants::match_not_null))
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800330 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800331 return listStr;
332 }
333 }
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800334 if (hexDump)
335 {
336 std::cout << dumpHex(std::data(pel.data()), pel.size(), 0, false)
337 << std::endl;
338 }
339 else if (fullPEL)
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800340 {
341 if (!foundPEL)
342 {
343 std::cout << "[\n";
344 foundPEL = true;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800345 }
346 else
Aatir7b291ec2019-11-19 10:37:37 -0600347 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800348 std::cout << ",\n\n";
349 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800350 pel.toJSON(registry, plugins);
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800351 }
352 else
353 {
354 // id
Matt Spinler3025d0b2021-06-02 15:12:52 -0600355 listStr += " \"" +
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800356 getNumberString("0x%X", pel.privateHeader().id()) +
357 "\": {\n";
358 // ASCII
359 if (pel.primarySRC())
360 {
361 val = pel.primarySRC().value()->asciiString();
Matt Spinler3025d0b2021-06-02 15:12:52 -0600362 jsonInsert(listStr, "SRC", trimEnd(val), 2);
363
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800364 // Registry message
365 auto regVal = pel.primarySRC().value()->getErrorDetails(
366 registry, DetailLevel::message, true);
367 if (regVal)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800368 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800369 val = regVal.value();
Matt Spinler3025d0b2021-06-02 15:12:52 -0600370 jsonInsert(listStr, "Message", val, 2);
Aatir37822f62019-12-10 14:40:27 -0600371 }
Aatir7b291ec2019-11-19 10:37:37 -0600372 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800373 else
374 {
Matt Spinler3025d0b2021-06-02 15:12:52 -0600375 jsonInsert(listStr, "SRC", "No SRC", 2);
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800376 }
Matt Spinler3025d0b2021-06-02 15:12:52 -0600377
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800378 // platformid
Matt Spinler3025d0b2021-06-02 15:12:52 -0600379 jsonInsert(listStr, "PLID",
380 getNumberString("0x%X", pel.privateHeader().plid()), 2);
381
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800382 // creatorid
383 std::string creatorID =
384 getNumberString("%c", pel.privateHeader().creatorID());
385 val = pv::creatorIDs.count(creatorID) ? pv::creatorIDs.at(creatorID)
386 : "Unknown Creator ID";
Matt Spinler3025d0b2021-06-02 15:12:52 -0600387 jsonInsert(listStr, "CreatorID", val, 2);
388
389 // subsystem
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800390 std::string subsystem = pv::getValue(pel.userHeader().subsystem(),
391 pel_values::subsystemValues);
Matt Spinler3025d0b2021-06-02 15:12:52 -0600392 jsonInsert(listStr, "Subsystem", subsystem, 2);
393
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800394 // commit time
395 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
396 pel.privateHeader().commitTimestamp().month,
397 pel.privateHeader().commitTimestamp().day,
398 pel.privateHeader().commitTimestamp().yearMSB,
399 pel.privateHeader().commitTimestamp().yearLSB,
400 pel.privateHeader().commitTimestamp().hour,
401 pel.privateHeader().commitTimestamp().minutes,
402 pel.privateHeader().commitTimestamp().seconds);
Matt Spinler3025d0b2021-06-02 15:12:52 -0600403 jsonInsert(listStr, "Commit Time", tmpValStr, 2);
404
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800405 // severity
406 std::string severity = pv::getValue(pel.userHeader().severity(),
407 pel_values::severityValues);
Matt Spinler3025d0b2021-06-02 15:12:52 -0600408 jsonInsert(listStr, "Sev", severity, 2);
409
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800410 // compID
Matt Spinler3025d0b2021-06-02 15:12:52 -0600411 jsonInsert(listStr, "CompID",
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800412 getNumberString(
Matt Spinler3025d0b2021-06-02 15:12:52 -0600413 "0x%X", pel.privateHeader().header().componentID),
414 2);
415
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800416 found = listStr.rfind(",");
417 if (found != std::string::npos)
418 {
419 listStr.replace(found, 1, "");
Matt Spinler3025d0b2021-06-02 15:12:52 -0600420 listStr += " },\n";
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800421 }
422 foundPEL = true;
Aatir7b291ec2019-11-19 10:37:37 -0600423 }
424 }
425 catch (std::exception& e)
426 {
427 log<level::ERR>("Hit exception while reading PEL File",
428 entry("FILENAME=%s", fileName.c_str()),
429 entry("ERROR=%s", e.what()));
430 }
431 return listStr;
432}
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800433
Aatir7b291ec2019-11-19 10:37:37 -0600434/**
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800435 * @brief Print a list of PELs or a JSON array of PELs
436 * @param[in] order - Boolean to print in reverse orser
437 * @param[in] hidden - Boolean to include hidden PELs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800438 * @param[in] includeInfo - Boolean to include informational PELs
Miguel Gomez011ccae2021-03-25 23:42:09 +0000439 * @param[in] critSysTerm - Boolean to include critical error and system
440 * termination PELs
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800441 * @param[in] fullPEL - Boolean to print full PEL into a JSON array
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800442 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800443 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Aatir7b291ec2019-11-19 10:37:37 -0600444 */
Miguel Gomez011ccae2021-03-25 23:42:09 +0000445void printPELs(bool order, bool hidden, bool includeInfo, bool critSysTerm,
446 bool fullPEL, const std::optional<std::regex>& scrubRegex,
447 bool hexDump)
Aatir7b291ec2019-11-19 10:37:37 -0600448{
449 std::string listStr;
450 std::map<uint32_t, BCDTime> PELs;
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800451 std::vector<std::string> plugins;
Aatir7b291ec2019-11-19 10:37:37 -0600452 listStr = "{\n";
453 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
454 it != fs::directory_iterator(); ++it)
455 {
456 if (!fs::is_regular_file((*it).path()))
457 {
458 continue;
459 }
Aatir37822f62019-12-10 14:40:27 -0600460 else
Aatir7b291ec2019-11-19 10:37:37 -0600461 {
Aatir37822f62019-12-10 14:40:27 -0600462 PELs.emplace(fileNameToPELId((*it).path().filename()),
463 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600464 }
465 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800466 bool foundPEL = false;
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800467
468 if (fullPEL && !hexDump)
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800469 {
470 plugins = getPlugins();
471 }
Miguel Gomez011ccae2021-03-25 23:42:09 +0000472 auto buildJSON = [&listStr, &hidden, &includeInfo, &critSysTerm, &fullPEL,
473 &foundPEL, &scrubRegex, &plugins,
474 &hexDump](const auto& i) {
475 listStr += genPELJSON(i, hidden, includeInfo, critSysTerm, fullPEL,
476 foundPEL, scrubRegex, plugins, hexDump);
Aatir37822f62019-12-10 14:40:27 -0600477 };
Aatir7b291ec2019-11-19 10:37:37 -0600478 if (order)
479 {
480 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
481 }
482 else
483 {
484 std::for_each(PELs.begin(), PELs.end(), buildJSON);
485 }
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800486 if (hexDump)
487 {
488 return;
489 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800490 if (foundPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600491 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800492 if (fullPEL)
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800493 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800494 std::cout << "]" << std::endl;
495 }
496 else
497 {
498 std::size_t found;
499 found = listStr.rfind(",");
500 if (found != std::string::npos)
501 {
502 listStr.replace(found, 1, "");
503 listStr += "}\n";
504 printf("%s", listStr.c_str());
505 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800506 }
507 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800508 else
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800509 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800510 std::string emptyJSON = fullPEL ? "[]" : "{}";
511 std::cout << emptyJSON << std::endl;
Aatir7b291ec2019-11-19 10:37:37 -0600512 }
513}
Aatir186ce8c2019-10-20 15:13:39 -0500514
Matt Spinler27de6f32020-02-21 08:35:57 -0600515/**
516 * @brief Calls the function passed in on the PEL with the ID
517 * passed in.
518 *
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800519 * @param[in] id - The string version of the PEL or BMC Log ID, either with or
Matt Spinler27de6f32020-02-21 08:35:57 -0600520 * without the 0x prefix.
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800521 * @param[in] func - The std::function<void(const PEL&, bool hexDump)> function
522 * to run.
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800523 * @param[in] useBMC - if true, search by BMC Log ID, else search by PEL ID
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800524 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Matt Spinler27de6f32020-02-21 08:35:57 -0600525 */
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800526void callFunctionOnPEL(const std::string& id, const PELFunc& func,
527 bool useBMC = false, bool hexDump = false)
Matt Spinler27de6f32020-02-21 08:35:57 -0600528{
529 std::string pelID{id};
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800530 if (!useBMC)
Matt Spinler27de6f32020-02-21 08:35:57 -0600531 {
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800532 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
533
534 if (pelID.find("0X") == 0)
535 {
536 pelID.erase(0, 2);
537 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600538 }
539
540 bool found = false;
541
542 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
543 it != fs::directory_iterator(); ++it)
544 {
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800545 // The PEL ID is part of the filename, so use that to find the PEL if
546 // "useBMC" is set to false, otherwise we have to search within the PEL
Matt Spinler27de6f32020-02-21 08:35:57 -0600547
548 if (!fs::is_regular_file((*it).path()))
549 {
550 continue;
551 }
552
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800553 if ((ends_with((*it).path(), pelID) && !useBMC) || useBMC)
Matt Spinler27de6f32020-02-21 08:35:57 -0600554 {
Matt Spinler27de6f32020-02-21 08:35:57 -0600555 auto data = getFileData((*it).path());
556 if (!data.empty())
557 {
558 PEL pel{data};
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800559 if (!useBMC ||
560 (useBMC && pel.obmcLogID() == std::stoul(id, nullptr, 0)))
Matt Spinler27de6f32020-02-21 08:35:57 -0600561 {
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800562 found = true;
563 try
564 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800565 func(pel, hexDump);
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800566 break;
567 }
568 catch (std::exception& e)
569 {
570 std::cerr << " Internal function threw an exception: "
571 << e.what() << "\n";
572 exit(1);
573 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600574 }
575 }
576 else
577 {
578 std::cerr << "Could not read PEL file\n";
579 exit(1);
580 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600581 }
582 }
583
584 if (!found)
585 {
586 std::cerr << "PEL not found\n";
587 exit(1);
588 }
589}
590
591/**
Matt Spinler25d986c2021-02-10 13:26:18 -0600592 * @brief Delete a PEL file.
Matt Spinler27de6f32020-02-21 08:35:57 -0600593 *
Matt Spinler25d986c2021-02-10 13:26:18 -0600594 * @param[in] id - The PEL ID to delete.
Matt Spinler27de6f32020-02-21 08:35:57 -0600595 */
Matt Spinler25d986c2021-02-10 13:26:18 -0600596void deletePEL(const std::string& id)
Matt Spinler27de6f32020-02-21 08:35:57 -0600597{
Matt Spinler25d986c2021-02-10 13:26:18 -0600598 std::string pelID{id};
Matt Spinler27de6f32020-02-21 08:35:57 -0600599
Matt Spinler25d986c2021-02-10 13:26:18 -0600600 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
601
602 if (pelID.find("0X") == 0)
Matt Spinler27de6f32020-02-21 08:35:57 -0600603 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600604 pelID.erase(0, 2);
Matt Spinler27de6f32020-02-21 08:35:57 -0600605 }
Matt Spinler25d986c2021-02-10 13:26:18 -0600606
607 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
608 it != fs::directory_iterator(); ++it)
Matt Spinler27de6f32020-02-21 08:35:57 -0600609 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600610 if (ends_with((*it).path(), pelID))
611 {
612 fs::remove((*it).path());
613 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600614 }
615}
616
617/**
Matt Spinler25d986c2021-02-10 13:26:18 -0600618 * @brief Delete all PEL files.
Matt Spinler27de6f32020-02-21 08:35:57 -0600619 */
620void deleteAllPELs()
621{
Matt Spinler25d986c2021-02-10 13:26:18 -0600622 log<level::INFO>("peltool deleting all event logs");
Matt Spinler27de6f32020-02-21 08:35:57 -0600623
Matt Spinler25d986c2021-02-10 13:26:18 -0600624 for (const auto& entry :
625 fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs"))
Matt Spinler27de6f32020-02-21 08:35:57 -0600626 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600627 fs::remove(entry.path());
Matt Spinler27de6f32020-02-21 08:35:57 -0600628 }
629}
630
Matt Spinler1b420bc2020-02-21 08:54:25 -0600631/**
632 * @brief Display a single PEL
633 *
634 * @param[in] pel - the PEL to display
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800635 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Matt Spinler1b420bc2020-02-21 08:54:25 -0600636 */
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800637void displayPEL(const PEL& pel, bool hexDump)
Matt Spinler1b420bc2020-02-21 08:54:25 -0600638{
639 if (pel.valid())
640 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800641 if (hexDump)
642 {
643 std::string dstr =
644 dumpHex(std::data(pel.data()), pel.size(), 0, false);
645 std::cout << dstr << std::endl;
646 }
647 else
648 {
649 auto plugins = getPlugins();
650 pel.toJSON(registry, plugins);
651 }
Matt Spinler1b420bc2020-02-21 08:54:25 -0600652 }
653 else
654 {
655 std::cerr << "PEL was malformed\n";
656 exit(1);
657 }
658}
659
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800660/**
661 * @brief Print number of PELs
662 * @param[in] hidden - Bool to include hidden logs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800663 * @param[in] includeInfo - Bool to include informational logs
Miguel Gomez011ccae2021-03-25 23:42:09 +0000664 * @param[in] critSysTerm - Bool to include CritSysTerm
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800665 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800666 */
Miguel Gomez011ccae2021-03-25 23:42:09 +0000667void printPELCount(bool hidden, bool includeInfo, bool critSysTerm,
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800668 const std::optional<std::regex>& scrubRegex)
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800669{
670 std::size_t count = 0;
671 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
672 it != fs::directory_iterator(); ++it)
673 {
674 if (!fs::is_regular_file((*it).path()))
675 {
676 continue;
677 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800678 std::vector<uint8_t> data = getFileData((*it).path());
679 if (data.empty())
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800680 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800681 continue;
682 }
683 PEL pel{data};
684 if (!pel.valid())
685 {
686 continue;
687 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800688 if (!includeInfo && pel.userHeader().severity() == 0)
689 {
690 continue;
691 }
Miguel Gomez011ccae2021-03-25 23:42:09 +0000692 if (critSysTerm && pel.userHeader().severity() != critSysTermSeverity)
693 {
694 continue;
695 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800696 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
697 if (!hidden && actionFlags.test(hiddenFlagBit))
698 {
699 continue;
700 }
701 if (pel.primarySRC() && scrubRegex)
702 {
703 std::string val = pel.primarySRC().value()->asciiString();
704 if (std::regex_search(trimEnd(val), scrubRegex.value(),
705 std::regex_constants::match_not_null))
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800706 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800707 continue;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800708 }
709 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800710 count++;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800711 }
712 std::cout << "{\n"
713 << " \"Number of PELs found\": "
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800714 << getNumberString("%d", count) << "\n}\n";
715}
716
717/**
718 * @brief Generate regex pattern object from file contents
719 * @param[in] scrubFile - File containing regex pattern
720 * @return std::regex - SRC regex object
721 */
722std::regex genRegex(std::string& scrubFile)
723{
724 std::string pattern;
725 std::ifstream contents(scrubFile);
726 if (contents.fail())
727 {
728 std::cerr << "Can't open \"" << scrubFile << "\"\n";
729 exit(1);
730 }
731 std::string line;
732 while (std::getline(contents, line))
733 {
734 if (!line.empty())
735 {
736 pattern.append(line + "|");
737 }
738 }
739 try
740 {
741 std::regex scrubRegex(pattern, std::regex::icase);
742 return scrubRegex;
743 }
744 catch (std::regex_error& e)
745 {
746 if (e.code() == std::regex_constants::error_collate)
747 std::cerr << "Invalid collating element request\n";
748 else if (e.code() == std::regex_constants::error_ctype)
749 std::cerr << "Invalid character class\n";
750 else if (e.code() == std::regex_constants::error_escape)
751 std::cerr << "Invalid escape character or trailing escape\n";
752 else if (e.code() == std::regex_constants::error_backref)
753 std::cerr << "Invalid back reference\n";
754 else if (e.code() == std::regex_constants::error_brack)
755 std::cerr << "Mismatched bracket ([ or ])\n";
756 else if (e.code() == std::regex_constants::error_paren)
757 {
758 // to catch return code error_badrepeat when error_paren is retured
759 // instead
760 size_t pos = pattern.find_first_of("*+?{");
761 while (pos != std::string::npos)
762 {
763 if (pos == 0 || pattern.substr(pos - 1, 1) == "|")
764 {
765 std::cerr
766 << "A repetition character (*, ?, +, or {) was not "
767 "preceded by a valid regular expression\n";
768 exit(1);
769 }
770 pos = pattern.find_first_of("*+?{", pos + 1);
771 }
772 std::cerr << "Mismatched parentheses (( or ))\n";
773 }
774 else if (e.code() == std::regex_constants::error_brace)
775 std::cerr << "Mismatched brace ({ or })\n";
776 else if (e.code() == std::regex_constants::error_badbrace)
777 std::cerr << "Invalid range inside a { }\n";
778 else if (e.code() == std::regex_constants::error_range)
779 std::cerr << "Invalid character range (e.g., [z-a])\n";
780 else if (e.code() == std::regex_constants::error_space)
781 std::cerr << "Insufficient memory to handle regular expression\n";
782 else if (e.code() == std::regex_constants::error_badrepeat)
783 std::cerr << "A repetition character (*, ?, +, or {) was not "
784 "preceded by a valid regular expression\n";
785 else if (e.code() == std::regex_constants::error_complexity)
786 std::cerr << "The requested match is too complex\n";
787 else if (e.code() == std::regex_constants::error_stack)
788 std::cerr << "Insufficient memory to evaluate a match\n";
789 exit(1);
790 }
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800791}
792
Aatir186ce8c2019-10-20 15:13:39 -0500793static void exitWithError(const std::string& help, const char* err)
794{
795 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
796 exit(-1);
797}
798
799int main(int argc, char** argv)
800{
801 CLI::App app{"OpenBMC PEL Tool"};
802 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600803 std::string idPEL;
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800804 std::string bmcId;
Matt Spinler27de6f32020-02-21 08:35:57 -0600805 std::string idToDelete;
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800806 std::string scrubFile;
807 std::optional<std::regex> scrubRegex;
Aatire340c132019-12-09 14:19:29 -0600808 bool listPEL = false;
809 bool listPELDescOrd = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800810 bool hidden = false;
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800811 bool includeInfo = false;
Miguel Gomez011ccae2021-03-25 23:42:09 +0000812 bool critSysTerm = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600813 bool deleteAll = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800814 bool showPELCount = false;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800815 bool fullPEL = false;
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800816 bool hexDump = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600817
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800818 app.set_help_flag("--help", "Print this help message and exit");
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800819 app.add_option("--file", fileName, "Display a PEL using its Raw PEL file");
Aatire340c132019-12-09 14:19:29 -0600820 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800821 app.add_option("--bmc-id", bmcId,
822 "Display a PEL based on its BMC Event ID");
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800823 app.add_flag("-a", fullPEL, "Display all PELs");
Aatirbad5f8a2019-12-10 15:27:16 -0600824 app.add_flag("-l", listPEL, "List PELs");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800825 app.add_flag("-n", showPELCount, "Show number of PELs");
Aatir7b291ec2019-11-19 10:37:37 -0600826 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800827 app.add_flag("-h", hidden, "Include hidden PELs");
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800828 app.add_flag("-f,--info", includeInfo, "Include informational PELs");
Miguel Gomez011ccae2021-03-25 23:42:09 +0000829 app.add_flag("-t, --termination", critSysTerm,
830 "List only critical system terminating PELs");
Matt Spinler27de6f32020-02-21 08:35:57 -0600831 app.add_option("-d, --delete", idToDelete, "Delete a PEL based on its ID");
832 app.add_flag("-D, --delete-all", deleteAll, "Delete all PELs");
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800833 app.add_option("-s, --scrub", scrubFile,
834 "File containing SRC regular expressions to ignore");
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800835 app.add_flag("-x", hexDump, "Display PEL(s) in hexdump instead of JSON");
Matt Spinler27de6f32020-02-21 08:35:57 -0600836
Aatir186ce8c2019-10-20 15:13:39 -0500837 CLI11_PARSE(app, argc, argv);
838
839 if (!fileName.empty())
840 {
841 std::vector<uint8_t> data = getFileData(fileName);
842 if (!data.empty())
843 {
844 PEL pel{data};
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800845 if (hexDump)
846 {
847 std::string dstr =
848 dumpHex(std::data(pel.data()), pel.size(), 0, false);
849 std::cout << dstr << std::endl;
850 }
851 else
852 {
853 auto plugins = getPlugins();
854 pel.toJSON(registry, plugins);
855 }
Aatir186ce8c2019-10-20 15:13:39 -0500856 }
857 else
858 {
859 exitWithError(app.help("", CLI::AppFormatMode::All),
860 "Raw PEL file can't be read.");
861 }
862 }
Aatire340c132019-12-09 14:19:29 -0600863 else if (!idPEL.empty())
864 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800865 callFunctionOnPEL(idPEL, displayPEL, false, hexDump);
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800866 }
867 else if (!bmcId.empty())
868 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800869 callFunctionOnPEL(bmcId, displayPEL, true, hexDump);
Aatire340c132019-12-09 14:19:29 -0600870 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800871 else if (fullPEL || listPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600872 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800873 if (!scrubFile.empty())
874 {
875 scrubRegex = genRegex(scrubFile);
876 }
Miguel Gomez011ccae2021-03-25 23:42:09 +0000877 printPELs(listPELDescOrd, hidden, includeInfo, critSysTerm, fullPEL,
878 scrubRegex, hexDump);
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800879 }
880 else if (showPELCount)
881 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800882 if (!scrubFile.empty())
883 {
884 scrubRegex = genRegex(scrubFile);
885 }
Miguel Gomez011ccae2021-03-25 23:42:09 +0000886 printPELCount(hidden, includeInfo, critSysTerm, scrubRegex);
Aatir7b291ec2019-11-19 10:37:37 -0600887 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600888 else if (!idToDelete.empty())
889 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600890 deletePEL(idToDelete);
Matt Spinler27de6f32020-02-21 08:35:57 -0600891 }
892 else if (deleteAll)
893 {
894 deleteAllPELs();
895 }
Aatir186ce8c2019-10-20 15:13:39 -0500896 else
897 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800898 std::cout << app.help("", CLI::AppFormatMode::All) << std::endl;
Aatir186ce8c2019-10-20 15:13:39 -0500899 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800900 Py_Finalize();
Aatir186ce8c2019-10-20 15:13:39 -0500901 return 0;
902}