blob: 427216ab85b0ced3b22c05f9035b3b6c632202a1 [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
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +080044using PELFunc = std::function<void(const PEL&, bool hexDump)>;
Matt Spinler0d804ef2020-05-12 16:16:26 -050045message::Registry registry(getPELReadOnlyDataPath() / message::registryFileName,
Matt Spinlerd8e29002020-04-09 09:11:22 -050046 false);
Matt Spinler27de6f32020-02-21 08:35:57 -060047namespace service
48{
49constexpr auto logging = "xyz.openbmc_project.Logging";
50} // namespace service
51
52namespace interface
53{
54constexpr auto deleteObj = "xyz.openbmc_project.Object.Delete";
55constexpr auto deleteAll = "xyz.openbmc_project.Collection.DeleteAll";
56} // namespace interface
57
58namespace object_path
59{
60constexpr auto logEntry = "/xyz/openbmc_project/logging/entry/";
61constexpr auto logging = "/xyz/openbmc_project/logging";
62} // namespace object_path
63
Aatire340c132019-12-09 14:19:29 -060064/**
Aatir37822f62019-12-10 14:40:27 -060065 * @brief helper function to get PEL commit timestamp from file name
66 * @retrun BCDTime - PEL commit timestamp
67 * @param[in] std::string - file name
68 */
69BCDTime fileNameToTimestamp(const std::string& fileName)
70{
71 std::string token = fileName.substr(0, fileName.find("_"));
72 int i = 0;
73 BCDTime tmp;
74 if (token.length() >= 14)
75 {
76 try
77 {
78 tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
79 }
80 catch (std::exception& err)
81 {
82 std::cout << "Conversion failure: " << err.what() << std::endl;
83 }
84 i += 2;
85 try
86 {
87 tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
88 }
89 catch (std::exception& err)
90 {
91 std::cout << "Conversion failure: " << err.what() << std::endl;
92 }
93 i += 2;
94 try
95 {
96 tmp.month = std::stoi(token.substr(i, 2), 0, 16);
97 }
98 catch (std::exception& err)
99 {
100 std::cout << "Conversion failure: " << err.what() << std::endl;
101 }
102 i += 2;
103 try
104 {
105 tmp.day = std::stoi(token.substr(i, 2), 0, 16);
106 }
107 catch (std::exception& err)
108 {
109 std::cout << "Conversion failure: " << err.what() << std::endl;
110 }
111 i += 2;
112 try
113 {
114 tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
115 }
116 catch (std::exception& err)
117 {
118 std::cout << "Conversion failure: " << err.what() << std::endl;
119 }
120 i += 2;
121 try
122 {
123 tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
124 }
125 catch (std::exception& err)
126 {
127 std::cout << "Conversion failure: " << err.what() << std::endl;
128 }
129 i += 2;
130 try
131 {
132 tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
133 }
134 catch (std::exception& err)
135 {
136 std::cout << "Conversion failure: " << err.what() << std::endl;
137 }
138 i += 2;
139 try
140 {
141 tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
142 }
143 catch (std::exception& err)
144 {
145 std::cout << "Conversion failure: " << err.what() << std::endl;
146 }
147 }
148 return tmp;
149}
150
151/**
152 * @brief helper function to get PEL id from file name
153 * @retrun uint32_t - PEL id
154 * @param[in] std::string - file name
155 */
156uint32_t fileNameToPELId(const std::string& fileName)
157{
158 uint32_t num = 0;
159 try
160 {
161 num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
162 }
163 catch (std::exception& err)
164 {
165 std::cout << "Conversion failure: " << err.what() << std::endl;
166 }
167 return num;
168}
169
170/**
Aatire340c132019-12-09 14:19:29 -0600171 * @brief helper function to check string suffix
172 * @retrun bool - true with suffix matches
173 * @param[in] std::string - string to check for suffix
174 * @param[in] std::string - suffix string
175 */
176bool ends_with(const std::string& str, const std::string& end)
177{
178 size_t slen = str.size(), elen = end.size();
179 if (slen < elen)
180 return false;
181 while (elen)
182 {
183 if (str[--slen] != end[--elen])
184 return false;
185 }
186 return true;
187}
188
Aatir37822f62019-12-10 14:40:27 -0600189/**
190 * @brief get data form raw PEL file.
191 * @param[in] std::string Name of file with raw PEL
192 * @return std::vector<uint8_t> char vector read from raw PEL file.
193 */
Aatirbad5f8a2019-12-10 15:27:16 -0600194std::vector<uint8_t> getFileData(const std::string& name)
Aatir37822f62019-12-10 14:40:27 -0600195{
196 std::ifstream file(name, std::ifstream::in);
197 if (file.good())
198 {
199 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
200 std::istreambuf_iterator<char>()};
201 return data;
202 }
203 else
204 {
Aatir37822f62019-12-10 14:40:27 -0600205 return {};
206 }
207}
208
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800209/**
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800210 * @brief Initialize Python interpreter and gather all UD parser modules under
211 * the paths found in Python sys.path and the current user directory.
212 * This is to prevent calling a non-existant module which causes Python
213 * to print an import error message and breaking JSON output.
214 *
215 * @return std::vector<std::string> Vector of plugins found in filesystem
216 */
217std::vector<std::string> getPlugins()
218{
219 Py_Initialize();
220 std::vector<std::string> plugins;
221 std::vector<std::string> siteDirs;
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800222 std::array<std::string, 2> parserDirs = {"udparsers", "srcparsers"};
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800223 PyObject* pName = PyUnicode_FromString("sys");
224 PyObject* pModule = PyImport_Import(pName);
225 Py_XDECREF(pName);
226 PyObject* pDict = PyModule_GetDict(pModule);
227 Py_XDECREF(pModule);
228 PyObject* pResult = PyDict_GetItemString(pDict, "path");
229 PyObject* pValue = PyUnicode_FromString(".");
230 PyList_Append(pResult, pValue);
231 Py_XDECREF(pValue);
232 auto list_size = PyList_Size(pResult);
233 for (auto i = 0; i < list_size; i++)
234 {
235 PyObject* item = PyList_GetItem(pResult, i);
236 PyObject* pBytes = PyUnicode_AsEncodedString(item, "utf-8", "~E~");
237 const char* output = PyBytes_AS_STRING(pBytes);
238 Py_XDECREF(pBytes);
239 std::string tmpStr(output);
240 siteDirs.push_back(tmpStr);
241 }
242 for (const auto& dir : siteDirs)
243 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800244 for (const auto& parserDir : parserDirs)
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800245 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800246 if (fs::exists(dir + "/" + parserDir))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800247 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800248 for (const auto& entry :
249 fs::directory_iterator(dir + "/" + parserDir))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800250 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800251 if (entry.is_directory() and
252 fs::exists(entry.path().string() + "/" +
253 entry.path().stem().string() + ".py"))
254 {
255 plugins.push_back(entry.path().stem());
256 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800257 }
258 }
259 }
260 }
261 return plugins;
262}
263
264/**
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800265 * @brief Creates JSON string of a PEL entry if fullPEL is false or prints to
266 * stdout the full PEL in JSON if fullPEL is true
267 * @param[in] itr - std::map iterator of <uint32_t, BCDTime>
268 * @param[in] hidden - Boolean to include hidden PELs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800269 * @param[in] includeInfo - Boolean to include informational PELs
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800270 * @param[in] fullPEL - Boolean to print full JSON representation of PEL
271 * @param[in] foundPEL - Boolean to check if any PEL is present
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800272 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800273 * @param[in] plugins - Vector of strings of plugins found in filesystem
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800274 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800275 * @return std::string - JSON string of PEL entry (empty if fullPEL is true)
276 */
Aatir7b291ec2019-11-19 10:37:37 -0600277template <typename T>
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800278std::string genPELJSON(T itr, bool hidden, bool includeInfo, bool fullPEL,
279 bool& foundPEL,
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800280 const std::optional<std::regex>& scrubRegex,
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800281 const std::vector<std::string>& plugins, bool hexDump)
Aatir7b291ec2019-11-19 10:37:37 -0600282{
283 std::size_t found;
284 std::string val;
285 char tmpValStr[50];
286 std::string listStr;
287 char name[50];
288 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
289 itr.second.yearLSB, itr.second.month, itr.second.day,
290 itr.second.hour, itr.second.minutes, itr.second.seconds,
291 itr.second.hundredths, itr.first);
292 std::string fileName(name);
293 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
294 try
295 {
Aatir37822f62019-12-10 14:40:27 -0600296 std::vector<uint8_t> data = getFileData(fileName);
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800297 if (data.empty())
Aatir37822f62019-12-10 14:40:27 -0600298 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800299 log<level::ERR>("Empty PEL file",
300 entry("FILENAME=%s", fileName.c_str()));
301 return listStr;
302 }
303 PEL pel{data};
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800304 if (!pel.valid())
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800305 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800306 return listStr;
307 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800308 if (!includeInfo && pel.userHeader().severity() == 0)
309 {
310 return listStr;
311 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800312 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
313 if (!hidden && actionFlags.test(hiddenFlagBit))
314 {
315 return listStr;
316 }
317 if (pel.primarySRC() && scrubRegex)
318 {
319 val = pel.primarySRC().value()->asciiString();
320 if (std::regex_search(trimEnd(val), scrubRegex.value(),
321 std::regex_constants::match_not_null))
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800322 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800323 return listStr;
324 }
325 }
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800326 if (hexDump)
327 {
328 std::cout << dumpHex(std::data(pel.data()), pel.size(), 0, false)
329 << std::endl;
330 }
331 else if (fullPEL)
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800332 {
333 if (!foundPEL)
334 {
335 std::cout << "[\n";
336 foundPEL = true;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800337 }
338 else
Aatir7b291ec2019-11-19 10:37:37 -0600339 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800340 std::cout << ",\n\n";
341 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800342 pel.toJSON(registry, plugins);
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800343 }
344 else
345 {
346 // id
347 listStr += "\t\"" +
348 getNumberString("0x%X", pel.privateHeader().id()) +
349 "\": {\n";
350 // ASCII
351 if (pel.primarySRC())
352 {
353 val = pel.primarySRC().value()->asciiString();
354 listStr += "\t\t\"SRC\": \"" + trimEnd(val) + "\",\n";
355 // Registry message
356 auto regVal = pel.primarySRC().value()->getErrorDetails(
357 registry, DetailLevel::message, true);
358 if (regVal)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800359 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800360 val = regVal.value();
361 listStr += "\t\t\"Message\": \"" + val + "\",\n";
Aatir37822f62019-12-10 14:40:27 -0600362 }
Aatir7b291ec2019-11-19 10:37:37 -0600363 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800364 else
365 {
366 listStr += "\t\t\"SRC\": \"No SRC\",\n";
367 }
368 // platformid
369 listStr += "\t\t\"PLID\": \"" +
370 getNumberString("0x%X", pel.privateHeader().plid()) +
371 "\",\n";
372 // creatorid
373 std::string creatorID =
374 getNumberString("%c", pel.privateHeader().creatorID());
375 val = pv::creatorIDs.count(creatorID) ? pv::creatorIDs.at(creatorID)
376 : "Unknown Creator ID";
377 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
378 // subsytem
379 std::string subsystem = pv::getValue(pel.userHeader().subsystem(),
380 pel_values::subsystemValues);
381 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
382 // commit time
383 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
384 pel.privateHeader().commitTimestamp().month,
385 pel.privateHeader().commitTimestamp().day,
386 pel.privateHeader().commitTimestamp().yearMSB,
387 pel.privateHeader().commitTimestamp().yearLSB,
388 pel.privateHeader().commitTimestamp().hour,
389 pel.privateHeader().commitTimestamp().minutes,
390 pel.privateHeader().commitTimestamp().seconds);
391 val = std::string(tmpValStr);
392 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
393 // severity
394 std::string severity = pv::getValue(pel.userHeader().severity(),
395 pel_values::severityValues);
396 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
397 // compID
398 listStr += "\t\t\"CompID\": \"" +
399 getNumberString(
400 "0x%X", pel.privateHeader().header().componentID) +
401 "\",\n ";
402 found = listStr.rfind(",");
403 if (found != std::string::npos)
404 {
405 listStr.replace(found, 1, "");
406 listStr += "\t},\n";
407 }
408 foundPEL = true;
Aatir7b291ec2019-11-19 10:37:37 -0600409 }
410 }
411 catch (std::exception& e)
412 {
413 log<level::ERR>("Hit exception while reading PEL File",
414 entry("FILENAME=%s", fileName.c_str()),
415 entry("ERROR=%s", e.what()));
416 }
417 return listStr;
418}
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800419
Aatir7b291ec2019-11-19 10:37:37 -0600420/**
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800421 * @brief Print a list of PELs or a JSON array of PELs
422 * @param[in] order - Boolean to print in reverse orser
423 * @param[in] hidden - Boolean to include hidden PELs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800424 * @param[in] includeInfo - Boolean to include informational PELs
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800425 * @param[in] fullPEL - Boolean to print full PEL into a JSON array
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800426 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800427 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Aatir7b291ec2019-11-19 10:37:37 -0600428 */
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800429void printPELs(bool order, bool hidden, bool includeInfo, bool fullPEL,
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800430 const std::optional<std::regex>& scrubRegex, bool hexDump)
Aatir7b291ec2019-11-19 10:37:37 -0600431{
432 std::string listStr;
433 std::map<uint32_t, BCDTime> PELs;
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800434 std::vector<std::string> plugins;
Aatir7b291ec2019-11-19 10:37:37 -0600435 listStr = "{\n";
436 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
437 it != fs::directory_iterator(); ++it)
438 {
439 if (!fs::is_regular_file((*it).path()))
440 {
441 continue;
442 }
Aatir37822f62019-12-10 14:40:27 -0600443 else
Aatir7b291ec2019-11-19 10:37:37 -0600444 {
Aatir37822f62019-12-10 14:40:27 -0600445 PELs.emplace(fileNameToPELId((*it).path().filename()),
446 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600447 }
448 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800449 bool foundPEL = false;
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800450
451 if (fullPEL && !hexDump)
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800452 {
453 plugins = getPlugins();
454 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800455 auto buildJSON = [&listStr, &hidden, &includeInfo, &fullPEL, &foundPEL,
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800456 &scrubRegex, &plugins, &hexDump](const auto& i) {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800457 listStr += genPELJSON(i, hidden, includeInfo, fullPEL, foundPEL,
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800458 scrubRegex, plugins, hexDump);
Aatir37822f62019-12-10 14:40:27 -0600459 };
Aatir7b291ec2019-11-19 10:37:37 -0600460 if (order)
461 {
462 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
463 }
464 else
465 {
466 std::for_each(PELs.begin(), PELs.end(), buildJSON);
467 }
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800468 if (hexDump)
469 {
470 return;
471 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800472 if (foundPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600473 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800474 if (fullPEL)
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800475 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800476 std::cout << "]" << std::endl;
477 }
478 else
479 {
480 std::size_t found;
481 found = listStr.rfind(",");
482 if (found != std::string::npos)
483 {
484 listStr.replace(found, 1, "");
485 listStr += "}\n";
486 printf("%s", listStr.c_str());
487 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800488 }
489 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800490 else
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800491 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800492 std::string emptyJSON = fullPEL ? "[]" : "{}";
493 std::cout << emptyJSON << std::endl;
Aatir7b291ec2019-11-19 10:37:37 -0600494 }
495}
Aatir186ce8c2019-10-20 15:13:39 -0500496
Matt Spinler27de6f32020-02-21 08:35:57 -0600497/**
498 * @brief Calls the function passed in on the PEL with the ID
499 * passed in.
500 *
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800501 * @param[in] id - The string version of the PEL or BMC Log ID, either with or
Matt Spinler27de6f32020-02-21 08:35:57 -0600502 * without the 0x prefix.
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800503 * @param[in] func - The std::function<void(const PEL&, bool hexDump)> function
504 * to run.
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800505 * @param[in] useBMC - if true, search by BMC Log ID, else search by PEL ID
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800506 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Matt Spinler27de6f32020-02-21 08:35:57 -0600507 */
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800508void callFunctionOnPEL(const std::string& id, const PELFunc& func,
509 bool useBMC = false, bool hexDump = false)
Matt Spinler27de6f32020-02-21 08:35:57 -0600510{
511 std::string pelID{id};
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800512 if (!useBMC)
Matt Spinler27de6f32020-02-21 08:35:57 -0600513 {
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800514 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
515
516 if (pelID.find("0X") == 0)
517 {
518 pelID.erase(0, 2);
519 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600520 }
521
522 bool found = false;
523
524 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
525 it != fs::directory_iterator(); ++it)
526 {
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800527 // The PEL ID is part of the filename, so use that to find the PEL if
528 // "useBMC" is set to false, otherwise we have to search within the PEL
Matt Spinler27de6f32020-02-21 08:35:57 -0600529
530 if (!fs::is_regular_file((*it).path()))
531 {
532 continue;
533 }
534
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800535 if ((ends_with((*it).path(), pelID) && !useBMC) || useBMC)
Matt Spinler27de6f32020-02-21 08:35:57 -0600536 {
Matt Spinler27de6f32020-02-21 08:35:57 -0600537 auto data = getFileData((*it).path());
538 if (!data.empty())
539 {
540 PEL pel{data};
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800541 if (!useBMC ||
542 (useBMC && pel.obmcLogID() == std::stoul(id, nullptr, 0)))
Matt Spinler27de6f32020-02-21 08:35:57 -0600543 {
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800544 found = true;
545 try
546 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800547 func(pel, hexDump);
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800548 break;
549 }
550 catch (std::exception& e)
551 {
552 std::cerr << " Internal function threw an exception: "
553 << e.what() << "\n";
554 exit(1);
555 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600556 }
557 }
558 else
559 {
560 std::cerr << "Could not read PEL file\n";
561 exit(1);
562 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600563 }
564 }
565
566 if (!found)
567 {
568 std::cerr << "PEL not found\n";
569 exit(1);
570 }
571}
572
573/**
Matt Spinler25d986c2021-02-10 13:26:18 -0600574 * @brief Delete a PEL file.
Matt Spinler27de6f32020-02-21 08:35:57 -0600575 *
Matt Spinler25d986c2021-02-10 13:26:18 -0600576 * @param[in] id - The PEL ID to delete.
Matt Spinler27de6f32020-02-21 08:35:57 -0600577 */
Matt Spinler25d986c2021-02-10 13:26:18 -0600578void deletePEL(const std::string& id)
Matt Spinler27de6f32020-02-21 08:35:57 -0600579{
Matt Spinler25d986c2021-02-10 13:26:18 -0600580 std::string pelID{id};
Matt Spinler27de6f32020-02-21 08:35:57 -0600581
Matt Spinler25d986c2021-02-10 13:26:18 -0600582 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
583
584 if (pelID.find("0X") == 0)
Matt Spinler27de6f32020-02-21 08:35:57 -0600585 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600586 pelID.erase(0, 2);
Matt Spinler27de6f32020-02-21 08:35:57 -0600587 }
Matt Spinler25d986c2021-02-10 13:26:18 -0600588
589 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
590 it != fs::directory_iterator(); ++it)
Matt Spinler27de6f32020-02-21 08:35:57 -0600591 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600592 if (ends_with((*it).path(), pelID))
593 {
594 fs::remove((*it).path());
595 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600596 }
597}
598
599/**
Matt Spinler25d986c2021-02-10 13:26:18 -0600600 * @brief Delete all PEL files.
Matt Spinler27de6f32020-02-21 08:35:57 -0600601 */
602void deleteAllPELs()
603{
Matt Spinler25d986c2021-02-10 13:26:18 -0600604 log<level::INFO>("peltool deleting all event logs");
Matt Spinler27de6f32020-02-21 08:35:57 -0600605
Matt Spinler25d986c2021-02-10 13:26:18 -0600606 for (const auto& entry :
607 fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs"))
Matt Spinler27de6f32020-02-21 08:35:57 -0600608 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600609 fs::remove(entry.path());
Matt Spinler27de6f32020-02-21 08:35:57 -0600610 }
611}
612
Matt Spinler1b420bc2020-02-21 08:54:25 -0600613/**
614 * @brief Display a single PEL
615 *
616 * @param[in] pel - the PEL to display
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800617 * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
Matt Spinler1b420bc2020-02-21 08:54:25 -0600618 */
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800619void displayPEL(const PEL& pel, bool hexDump)
Matt Spinler1b420bc2020-02-21 08:54:25 -0600620{
621 if (pel.valid())
622 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800623 if (hexDump)
624 {
625 std::string dstr =
626 dumpHex(std::data(pel.data()), pel.size(), 0, false);
627 std::cout << dstr << std::endl;
628 }
629 else
630 {
631 auto plugins = getPlugins();
632 pel.toJSON(registry, plugins);
633 }
Matt Spinler1b420bc2020-02-21 08:54:25 -0600634 }
635 else
636 {
637 std::cerr << "PEL was malformed\n";
638 exit(1);
639 }
640}
641
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800642/**
643 * @brief Print number of PELs
644 * @param[in] hidden - Bool to include hidden logs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800645 * @param[in] includeInfo - Bool to include informational logs
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800646 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800647 */
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800648void printPELCount(bool hidden, bool includeInfo,
649 const std::optional<std::regex>& scrubRegex)
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800650{
651 std::size_t count = 0;
652 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
653 it != fs::directory_iterator(); ++it)
654 {
655 if (!fs::is_regular_file((*it).path()))
656 {
657 continue;
658 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800659 std::vector<uint8_t> data = getFileData((*it).path());
660 if (data.empty())
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800661 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800662 continue;
663 }
664 PEL pel{data};
665 if (!pel.valid())
666 {
667 continue;
668 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800669 if (!includeInfo && pel.userHeader().severity() == 0)
670 {
671 continue;
672 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800673 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
674 if (!hidden && actionFlags.test(hiddenFlagBit))
675 {
676 continue;
677 }
678 if (pel.primarySRC() && scrubRegex)
679 {
680 std::string val = pel.primarySRC().value()->asciiString();
681 if (std::regex_search(trimEnd(val), scrubRegex.value(),
682 std::regex_constants::match_not_null))
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800683 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800684 continue;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800685 }
686 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800687 count++;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800688 }
689 std::cout << "{\n"
690 << " \"Number of PELs found\": "
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800691 << getNumberString("%d", count) << "\n}\n";
692}
693
694/**
695 * @brief Generate regex pattern object from file contents
696 * @param[in] scrubFile - File containing regex pattern
697 * @return std::regex - SRC regex object
698 */
699std::regex genRegex(std::string& scrubFile)
700{
701 std::string pattern;
702 std::ifstream contents(scrubFile);
703 if (contents.fail())
704 {
705 std::cerr << "Can't open \"" << scrubFile << "\"\n";
706 exit(1);
707 }
708 std::string line;
709 while (std::getline(contents, line))
710 {
711 if (!line.empty())
712 {
713 pattern.append(line + "|");
714 }
715 }
716 try
717 {
718 std::regex scrubRegex(pattern, std::regex::icase);
719 return scrubRegex;
720 }
721 catch (std::regex_error& e)
722 {
723 if (e.code() == std::regex_constants::error_collate)
724 std::cerr << "Invalid collating element request\n";
725 else if (e.code() == std::regex_constants::error_ctype)
726 std::cerr << "Invalid character class\n";
727 else if (e.code() == std::regex_constants::error_escape)
728 std::cerr << "Invalid escape character or trailing escape\n";
729 else if (e.code() == std::regex_constants::error_backref)
730 std::cerr << "Invalid back reference\n";
731 else if (e.code() == std::regex_constants::error_brack)
732 std::cerr << "Mismatched bracket ([ or ])\n";
733 else if (e.code() == std::regex_constants::error_paren)
734 {
735 // to catch return code error_badrepeat when error_paren is retured
736 // instead
737 size_t pos = pattern.find_first_of("*+?{");
738 while (pos != std::string::npos)
739 {
740 if (pos == 0 || pattern.substr(pos - 1, 1) == "|")
741 {
742 std::cerr
743 << "A repetition character (*, ?, +, or {) was not "
744 "preceded by a valid regular expression\n";
745 exit(1);
746 }
747 pos = pattern.find_first_of("*+?{", pos + 1);
748 }
749 std::cerr << "Mismatched parentheses (( or ))\n";
750 }
751 else if (e.code() == std::regex_constants::error_brace)
752 std::cerr << "Mismatched brace ({ or })\n";
753 else if (e.code() == std::regex_constants::error_badbrace)
754 std::cerr << "Invalid range inside a { }\n";
755 else if (e.code() == std::regex_constants::error_range)
756 std::cerr << "Invalid character range (e.g., [z-a])\n";
757 else if (e.code() == std::regex_constants::error_space)
758 std::cerr << "Insufficient memory to handle regular expression\n";
759 else if (e.code() == std::regex_constants::error_badrepeat)
760 std::cerr << "A repetition character (*, ?, +, or {) was not "
761 "preceded by a valid regular expression\n";
762 else if (e.code() == std::regex_constants::error_complexity)
763 std::cerr << "The requested match is too complex\n";
764 else if (e.code() == std::regex_constants::error_stack)
765 std::cerr << "Insufficient memory to evaluate a match\n";
766 exit(1);
767 }
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800768}
769
Aatir186ce8c2019-10-20 15:13:39 -0500770static void exitWithError(const std::string& help, const char* err)
771{
772 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
773 exit(-1);
774}
775
776int main(int argc, char** argv)
777{
778 CLI::App app{"OpenBMC PEL Tool"};
779 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600780 std::string idPEL;
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800781 std::string bmcId;
Matt Spinler27de6f32020-02-21 08:35:57 -0600782 std::string idToDelete;
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800783 std::string scrubFile;
784 std::optional<std::regex> scrubRegex;
Aatire340c132019-12-09 14:19:29 -0600785 bool listPEL = false;
786 bool listPELDescOrd = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800787 bool hidden = false;
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800788 bool includeInfo = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600789 bool deleteAll = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800790 bool showPELCount = false;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800791 bool fullPEL = false;
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800792 bool hexDump = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600793
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800794 app.set_help_flag("--help", "Print this help message and exit");
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800795 app.add_option("--file", fileName, "Display a PEL using its Raw PEL file");
Aatire340c132019-12-09 14:19:29 -0600796 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800797 app.add_option("--bmc-id", bmcId,
798 "Display a PEL based on its BMC Event ID");
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800799 app.add_flag("-a", fullPEL, "Display all PELs");
Aatirbad5f8a2019-12-10 15:27:16 -0600800 app.add_flag("-l", listPEL, "List PELs");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800801 app.add_flag("-n", showPELCount, "Show number of PELs");
Aatir7b291ec2019-11-19 10:37:37 -0600802 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800803 app.add_flag("-h", hidden, "Include hidden PELs");
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800804 app.add_flag("-f,--info", includeInfo, "Include informational PELs");
Matt Spinler27de6f32020-02-21 08:35:57 -0600805 app.add_option("-d, --delete", idToDelete, "Delete a PEL based on its ID");
806 app.add_flag("-D, --delete-all", deleteAll, "Delete all PELs");
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800807 app.add_option("-s, --scrub", scrubFile,
808 "File containing SRC regular expressions to ignore");
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800809 app.add_flag("-x", hexDump, "Display PEL(s) in hexdump instead of JSON");
Matt Spinler27de6f32020-02-21 08:35:57 -0600810
Aatir186ce8c2019-10-20 15:13:39 -0500811 CLI11_PARSE(app, argc, argv);
812
813 if (!fileName.empty())
814 {
815 std::vector<uint8_t> data = getFileData(fileName);
816 if (!data.empty())
817 {
818 PEL pel{data};
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800819 if (hexDump)
820 {
821 std::string dstr =
822 dumpHex(std::data(pel.data()), pel.size(), 0, false);
823 std::cout << dstr << std::endl;
824 }
825 else
826 {
827 auto plugins = getPlugins();
828 pel.toJSON(registry, plugins);
829 }
Aatir186ce8c2019-10-20 15:13:39 -0500830 }
831 else
832 {
833 exitWithError(app.help("", CLI::AppFormatMode::All),
834 "Raw PEL file can't be read.");
835 }
836 }
Aatire340c132019-12-09 14:19:29 -0600837 else if (!idPEL.empty())
838 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800839 callFunctionOnPEL(idPEL, displayPEL, false, hexDump);
Harisuddin Mohamed Isa58d3a982020-09-17 19:48:05 +0800840 }
841 else if (!bmcId.empty())
842 {
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800843 callFunctionOnPEL(bmcId, displayPEL, true, hexDump);
Aatire340c132019-12-09 14:19:29 -0600844 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800845 else if (fullPEL || listPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600846 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800847 if (!scrubFile.empty())
848 {
849 scrubRegex = genRegex(scrubFile);
850 }
Harisuddin Mohamed Isaed32c8d2020-10-01 18:12:39 +0800851 printPELs(listPELDescOrd, hidden, includeInfo, fullPEL, scrubRegex,
852 hexDump);
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800853 }
854 else if (showPELCount)
855 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800856 if (!scrubFile.empty())
857 {
858 scrubRegex = genRegex(scrubFile);
859 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800860 printPELCount(hidden, includeInfo, scrubRegex);
Aatir7b291ec2019-11-19 10:37:37 -0600861 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600862 else if (!idToDelete.empty())
863 {
Matt Spinler25d986c2021-02-10 13:26:18 -0600864 deletePEL(idToDelete);
Matt Spinler27de6f32020-02-21 08:35:57 -0600865 }
866 else if (deleteAll)
867 {
868 deleteAllPELs();
869 }
Aatir186ce8c2019-10-20 15:13:39 -0500870 else
871 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800872 std::cout << app.help("", CLI::AppFormatMode::All) << std::endl;
Aatir186ce8c2019-10-20 15:13:39 -0500873 }
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800874 Py_Finalize();
Aatir186ce8c2019-10-20 15:13:39 -0500875 return 0;
876}