blob: 3abdc740b6583be344c1e1ca10a21fb9543865c3 [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
25#include <CLI/CLI.hpp>
Aatir7b291ec2019-11-19 10:37:37 -060026#include <bitset>
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +080027#include <fstream>
Aatir186ce8c2019-10-20 15:13:39 -050028#include <iostream>
Aatir7b291ec2019-11-19 10:37:37 -060029#include <phosphor-logging/log.hpp>
30#include <regex>
Aatir186ce8c2019-10-20 15:13:39 -050031#include <string>
Aatir7b291ec2019-11-19 10:37:37 -060032#include <xyz/openbmc_project/Common/File/error.hpp>
Aatir186ce8c2019-10-20 15:13:39 -050033
Aatir7b291ec2019-11-19 10:37:37 -060034namespace fs = std::filesystem;
Aatir186ce8c2019-10-20 15:13:39 -050035using namespace phosphor::logging;
36using namespace openpower::pels;
Matt Spinler27de6f32020-02-21 08:35:57 -060037using sdbusplus::exception::SdBusError;
Aatir7b291ec2019-11-19 10:37:37 -060038namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
39namespace message = openpower::pels::message;
40namespace pv = openpower::pels::pel_values;
Aatir186ce8c2019-10-20 15:13:39 -050041
Matt Spinler27de6f32020-02-21 08:35:57 -060042using PELFunc = std::function<void(const PEL&)>;
Matt Spinler0d804ef2020-05-12 16:16:26 -050043message::Registry registry(getPELReadOnlyDataPath() / message::registryFileName,
Matt Spinlerd8e29002020-04-09 09:11:22 -050044 false);
Matt Spinler27de6f32020-02-21 08:35:57 -060045namespace service
46{
47constexpr auto logging = "xyz.openbmc_project.Logging";
48} // namespace service
49
50namespace interface
51{
52constexpr auto deleteObj = "xyz.openbmc_project.Object.Delete";
53constexpr auto deleteAll = "xyz.openbmc_project.Collection.DeleteAll";
54} // namespace interface
55
56namespace object_path
57{
58constexpr auto logEntry = "/xyz/openbmc_project/logging/entry/";
59constexpr auto logging = "/xyz/openbmc_project/logging";
60} // namespace object_path
61
Aatire340c132019-12-09 14:19:29 -060062/**
Aatir37822f62019-12-10 14:40:27 -060063 * @brief helper function to get PEL commit timestamp from file name
64 * @retrun BCDTime - PEL commit timestamp
65 * @param[in] std::string - file name
66 */
67BCDTime fileNameToTimestamp(const std::string& fileName)
68{
69 std::string token = fileName.substr(0, fileName.find("_"));
70 int i = 0;
71 BCDTime tmp;
72 if (token.length() >= 14)
73 {
74 try
75 {
76 tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
77 }
78 catch (std::exception& err)
79 {
80 std::cout << "Conversion failure: " << err.what() << std::endl;
81 }
82 i += 2;
83 try
84 {
85 tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
86 }
87 catch (std::exception& err)
88 {
89 std::cout << "Conversion failure: " << err.what() << std::endl;
90 }
91 i += 2;
92 try
93 {
94 tmp.month = std::stoi(token.substr(i, 2), 0, 16);
95 }
96 catch (std::exception& err)
97 {
98 std::cout << "Conversion failure: " << err.what() << std::endl;
99 }
100 i += 2;
101 try
102 {
103 tmp.day = std::stoi(token.substr(i, 2), 0, 16);
104 }
105 catch (std::exception& err)
106 {
107 std::cout << "Conversion failure: " << err.what() << std::endl;
108 }
109 i += 2;
110 try
111 {
112 tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
113 }
114 catch (std::exception& err)
115 {
116 std::cout << "Conversion failure: " << err.what() << std::endl;
117 }
118 i += 2;
119 try
120 {
121 tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
122 }
123 catch (std::exception& err)
124 {
125 std::cout << "Conversion failure: " << err.what() << std::endl;
126 }
127 i += 2;
128 try
129 {
130 tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
131 }
132 catch (std::exception& err)
133 {
134 std::cout << "Conversion failure: " << err.what() << std::endl;
135 }
136 i += 2;
137 try
138 {
139 tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
140 }
141 catch (std::exception& err)
142 {
143 std::cout << "Conversion failure: " << err.what() << std::endl;
144 }
145 }
146 return tmp;
147}
148
149/**
150 * @brief helper function to get PEL id from file name
151 * @retrun uint32_t - PEL id
152 * @param[in] std::string - file name
153 */
154uint32_t fileNameToPELId(const std::string& fileName)
155{
156 uint32_t num = 0;
157 try
158 {
159 num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
160 }
161 catch (std::exception& err)
162 {
163 std::cout << "Conversion failure: " << err.what() << std::endl;
164 }
165 return num;
166}
167
168/**
Aatire340c132019-12-09 14:19:29 -0600169 * @brief helper function to check string suffix
170 * @retrun bool - true with suffix matches
171 * @param[in] std::string - string to check for suffix
172 * @param[in] std::string - suffix string
173 */
174bool ends_with(const std::string& str, const std::string& end)
175{
176 size_t slen = str.size(), elen = end.size();
177 if (slen < elen)
178 return false;
179 while (elen)
180 {
181 if (str[--slen] != end[--elen])
182 return false;
183 }
184 return true;
185}
186
Aatir37822f62019-12-10 14:40:27 -0600187/**
188 * @brief get data form raw PEL file.
189 * @param[in] std::string Name of file with raw PEL
190 * @return std::vector<uint8_t> char vector read from raw PEL file.
191 */
Aatirbad5f8a2019-12-10 15:27:16 -0600192std::vector<uint8_t> getFileData(const std::string& name)
Aatir37822f62019-12-10 14:40:27 -0600193{
194 std::ifstream file(name, std::ifstream::in);
195 if (file.good())
196 {
197 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
198 std::istreambuf_iterator<char>()};
199 return data;
200 }
201 else
202 {
Aatir37822f62019-12-10 14:40:27 -0600203 return {};
204 }
205}
206
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800207/**
208 * @brief Creates JSON string of a PEL entry if fullPEL is false or prints to
209 * stdout the full PEL in JSON if fullPEL is true
210 * @param[in] itr - std::map iterator of <uint32_t, BCDTime>
211 * @param[in] hidden - Boolean to include hidden PELs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800212 * @param[in] includeInfo - Boolean to include informational PELs
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800213 * @param[in] fullPEL - Boolean to print full JSON representation of PEL
214 * @param[in] foundPEL - Boolean to check if any PEL is present
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800215 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800216 * @return std::string - JSON string of PEL entry (empty if fullPEL is true)
217 */
Aatir7b291ec2019-11-19 10:37:37 -0600218template <typename T>
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800219std::string genPELJSON(T itr, bool hidden, bool includeInfo, bool fullPEL,
220 bool& foundPEL,
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800221 const std::optional<std::regex>& scrubRegex)
Aatir7b291ec2019-11-19 10:37:37 -0600222{
223 std::size_t found;
224 std::string val;
225 char tmpValStr[50];
226 std::string listStr;
227 char name[50];
228 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
229 itr.second.yearLSB, itr.second.month, itr.second.day,
230 itr.second.hour, itr.second.minutes, itr.second.seconds,
231 itr.second.hundredths, itr.first);
232 std::string fileName(name);
233 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
234 try
235 {
Aatir37822f62019-12-10 14:40:27 -0600236 std::vector<uint8_t> data = getFileData(fileName);
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800237 if (data.empty())
Aatir37822f62019-12-10 14:40:27 -0600238 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800239 log<level::ERR>("Empty PEL file",
240 entry("FILENAME=%s", fileName.c_str()));
241 return listStr;
242 }
243 PEL pel{data};
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800244 if (!pel.valid())
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800245 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800246 return listStr;
247 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800248 if (!includeInfo && pel.userHeader().severity() == 0)
249 {
250 return listStr;
251 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800252 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
253 if (!hidden && actionFlags.test(hiddenFlagBit))
254 {
255 return listStr;
256 }
257 if (pel.primarySRC() && scrubRegex)
258 {
259 val = pel.primarySRC().value()->asciiString();
260 if (std::regex_search(trimEnd(val), scrubRegex.value(),
261 std::regex_constants::match_not_null))
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800262 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800263 return listStr;
264 }
265 }
266 if (fullPEL)
267 {
268 if (!foundPEL)
269 {
270 std::cout << "[\n";
271 foundPEL = true;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800272 }
273 else
Aatir7b291ec2019-11-19 10:37:37 -0600274 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800275 std::cout << ",\n\n";
276 }
277 pel.toJSON(registry);
278 }
279 else
280 {
281 // id
282 listStr += "\t\"" +
283 getNumberString("0x%X", pel.privateHeader().id()) +
284 "\": {\n";
285 // ASCII
286 if (pel.primarySRC())
287 {
288 val = pel.primarySRC().value()->asciiString();
289 listStr += "\t\t\"SRC\": \"" + trimEnd(val) + "\",\n";
290 // Registry message
291 auto regVal = pel.primarySRC().value()->getErrorDetails(
292 registry, DetailLevel::message, true);
293 if (regVal)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800294 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800295 val = regVal.value();
296 listStr += "\t\t\"Message\": \"" + val + "\",\n";
Aatir37822f62019-12-10 14:40:27 -0600297 }
Aatir7b291ec2019-11-19 10:37:37 -0600298 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800299 else
300 {
301 listStr += "\t\t\"SRC\": \"No SRC\",\n";
302 }
303 // platformid
304 listStr += "\t\t\"PLID\": \"" +
305 getNumberString("0x%X", pel.privateHeader().plid()) +
306 "\",\n";
307 // creatorid
308 std::string creatorID =
309 getNumberString("%c", pel.privateHeader().creatorID());
310 val = pv::creatorIDs.count(creatorID) ? pv::creatorIDs.at(creatorID)
311 : "Unknown Creator ID";
312 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
313 // subsytem
314 std::string subsystem = pv::getValue(pel.userHeader().subsystem(),
315 pel_values::subsystemValues);
316 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
317 // commit time
318 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
319 pel.privateHeader().commitTimestamp().month,
320 pel.privateHeader().commitTimestamp().day,
321 pel.privateHeader().commitTimestamp().yearMSB,
322 pel.privateHeader().commitTimestamp().yearLSB,
323 pel.privateHeader().commitTimestamp().hour,
324 pel.privateHeader().commitTimestamp().minutes,
325 pel.privateHeader().commitTimestamp().seconds);
326 val = std::string(tmpValStr);
327 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
328 // severity
329 std::string severity = pv::getValue(pel.userHeader().severity(),
330 pel_values::severityValues);
331 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
332 // compID
333 listStr += "\t\t\"CompID\": \"" +
334 getNumberString(
335 "0x%X", pel.privateHeader().header().componentID) +
336 "\",\n ";
337 found = listStr.rfind(",");
338 if (found != std::string::npos)
339 {
340 listStr.replace(found, 1, "");
341 listStr += "\t},\n";
342 }
343 foundPEL = true;
Aatir7b291ec2019-11-19 10:37:37 -0600344 }
345 }
346 catch (std::exception& e)
347 {
348 log<level::ERR>("Hit exception while reading PEL File",
349 entry("FILENAME=%s", fileName.c_str()),
350 entry("ERROR=%s", e.what()));
351 }
352 return listStr;
353}
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800354
Aatir7b291ec2019-11-19 10:37:37 -0600355/**
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800356 * @brief Print a list of PELs or a JSON array of PELs
357 * @param[in] order - Boolean to print in reverse orser
358 * @param[in] hidden - Boolean to include hidden PELs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800359 * @param[in] includeInfo - Boolean to include informational PELs
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800360 * @param[in] fullPEL - Boolean to print full PEL into a JSON array
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800361 * @param[in] scrubRegex - SRC regex object
Aatir7b291ec2019-11-19 10:37:37 -0600362 */
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800363void printPELs(bool order, bool hidden, bool includeInfo, bool fullPEL,
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800364 const std::optional<std::regex>& scrubRegex)
Aatir7b291ec2019-11-19 10:37:37 -0600365{
366 std::string listStr;
367 std::map<uint32_t, BCDTime> PELs;
Aatir7b291ec2019-11-19 10:37:37 -0600368 listStr = "{\n";
369 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
370 it != fs::directory_iterator(); ++it)
371 {
372 if (!fs::is_regular_file((*it).path()))
373 {
374 continue;
375 }
Aatir37822f62019-12-10 14:40:27 -0600376 else
Aatir7b291ec2019-11-19 10:37:37 -0600377 {
Aatir37822f62019-12-10 14:40:27 -0600378 PELs.emplace(fileNameToPELId((*it).path().filename()),
379 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600380 }
381 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800382 bool foundPEL = false;
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800383 auto buildJSON = [&listStr, &hidden, &includeInfo, &fullPEL, &foundPEL,
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800384 &scrubRegex](const auto& i) {
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800385 listStr +=
386 genPELJSON(i, hidden, includeInfo, fullPEL, foundPEL, scrubRegex);
Aatir37822f62019-12-10 14:40:27 -0600387 };
Aatir7b291ec2019-11-19 10:37:37 -0600388 if (order)
389 {
390 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
391 }
392 else
393 {
394 std::for_each(PELs.begin(), PELs.end(), buildJSON);
395 }
396
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800397 if (foundPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600398 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800399 if (fullPEL)
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800400 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800401 std::cout << "]" << std::endl;
402 }
403 else
404 {
405 std::size_t found;
406 found = listStr.rfind(",");
407 if (found != std::string::npos)
408 {
409 listStr.replace(found, 1, "");
410 listStr += "}\n";
411 printf("%s", listStr.c_str());
412 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800413 }
414 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800415 else
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800416 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800417 std::string emptyJSON = fullPEL ? "[]" : "{}";
418 std::cout << emptyJSON << std::endl;
Aatir7b291ec2019-11-19 10:37:37 -0600419 }
420}
Aatir186ce8c2019-10-20 15:13:39 -0500421
Matt Spinler27de6f32020-02-21 08:35:57 -0600422/**
423 * @brief Calls the function passed in on the PEL with the ID
424 * passed in.
425 *
426 * @param[in] id - The string version of the PEL ID, either with or
427 * without the 0x prefix.
428 * @param[in] func - The std::function<void(const PEL&)> function to run.
429 */
430void callFunctionOnPEL(const std::string& id, const PELFunc& func)
431{
432 std::string pelID{id};
433 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
434
435 if (pelID.find("0X") == 0)
436 {
437 pelID.erase(0, 2);
438 }
439
440 bool found = false;
441
442 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
443 it != fs::directory_iterator(); ++it)
444 {
445 // The PEL ID is part of the filename, so use that to find the PEL.
446
447 if (!fs::is_regular_file((*it).path()))
448 {
449 continue;
450 }
451
452 if (ends_with((*it).path(), pelID))
453 {
454 found = true;
455
456 auto data = getFileData((*it).path());
457 if (!data.empty())
458 {
459 PEL pel{data};
460
461 try
462 {
463 func(pel);
464 }
465 catch (std::exception& e)
466 {
467 std::cerr
468 << " Internal function threw an exception: " << e.what()
469 << "\n";
470 exit(1);
471 }
472 }
473 else
474 {
475 std::cerr << "Could not read PEL file\n";
476 exit(1);
477 }
478 break;
479 }
480 }
481
482 if (!found)
483 {
484 std::cerr << "PEL not found\n";
485 exit(1);
486 }
487}
488
489/**
490 * @brief Delete a PEL by deleting its corresponding event log.
491 *
492 * @param[in] pel - The PEL to delete
493 */
494void deletePEL(const PEL& pel)
495{
496 std::string path{object_path::logEntry};
497 path += std::to_string(pel.obmcLogID());
498
499 try
500 {
501 auto bus = sdbusplus::bus::new_default();
502 auto method = bus.new_method_call(service::logging, path.c_str(),
503 interface::deleteObj, "Delete");
504 auto reply = bus.call(method);
505 }
506 catch (const SdBusError& e)
507 {
508 std::cerr << "D-Bus call to delete event log " << pel.obmcLogID()
509 << " failed: " << e.what() << "\n";
510 exit(1);
511 }
512}
513
514/**
515 * @brief Delete all PELs by deleting all event logs.
516 */
517void deleteAllPELs()
518{
519 try
520 {
521 // This may move to an audit log some day
522 log<level::INFO>("peltool deleting all event logs");
523
524 auto bus = sdbusplus::bus::new_default();
525 auto method =
526 bus.new_method_call(service::logging, object_path::logging,
527 interface::deleteAll, "DeleteAll");
528 auto reply = bus.call(method);
529 }
530 catch (const SdBusError& e)
531 {
532 std::cerr << "D-Bus call to delete all event logs failed: " << e.what()
533 << "\n";
534 exit(1);
535 }
536}
537
Matt Spinler1b420bc2020-02-21 08:54:25 -0600538/**
539 * @brief Display a single PEL
540 *
541 * @param[in] pel - the PEL to display
542 */
543void displayPEL(const PEL& pel)
544{
545 if (pel.valid())
546 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800547 pel.toJSON(registry);
Matt Spinler1b420bc2020-02-21 08:54:25 -0600548 }
549 else
550 {
551 std::cerr << "PEL was malformed\n";
552 exit(1);
553 }
554}
555
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800556/**
557 * @brief Print number of PELs
558 * @param[in] hidden - Bool to include hidden logs
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800559 * @param[in] includeInfo - Bool to include informational logs
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800560 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800561 */
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800562void printPELCount(bool hidden, bool includeInfo,
563 const std::optional<std::regex>& scrubRegex)
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800564{
565 std::size_t count = 0;
566 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
567 it != fs::directory_iterator(); ++it)
568 {
569 if (!fs::is_regular_file((*it).path()))
570 {
571 continue;
572 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800573 std::vector<uint8_t> data = getFileData((*it).path());
574 if (data.empty())
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800575 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800576 continue;
577 }
578 PEL pel{data};
579 if (!pel.valid())
580 {
581 continue;
582 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800583 if (!includeInfo && pel.userHeader().severity() == 0)
584 {
585 continue;
586 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800587 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
588 if (!hidden && actionFlags.test(hiddenFlagBit))
589 {
590 continue;
591 }
592 if (pel.primarySRC() && scrubRegex)
593 {
594 std::string val = pel.primarySRC().value()->asciiString();
595 if (std::regex_search(trimEnd(val), scrubRegex.value(),
596 std::regex_constants::match_not_null))
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800597 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800598 continue;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800599 }
600 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800601 count++;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800602 }
603 std::cout << "{\n"
604 << " \"Number of PELs found\": "
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800605 << getNumberString("%d", count) << "\n}\n";
606}
607
608/**
609 * @brief Generate regex pattern object from file contents
610 * @param[in] scrubFile - File containing regex pattern
611 * @return std::regex - SRC regex object
612 */
613std::regex genRegex(std::string& scrubFile)
614{
615 std::string pattern;
616 std::ifstream contents(scrubFile);
617 if (contents.fail())
618 {
619 std::cerr << "Can't open \"" << scrubFile << "\"\n";
620 exit(1);
621 }
622 std::string line;
623 while (std::getline(contents, line))
624 {
625 if (!line.empty())
626 {
627 pattern.append(line + "|");
628 }
629 }
630 try
631 {
632 std::regex scrubRegex(pattern, std::regex::icase);
633 return scrubRegex;
634 }
635 catch (std::regex_error& e)
636 {
637 if (e.code() == std::regex_constants::error_collate)
638 std::cerr << "Invalid collating element request\n";
639 else if (e.code() == std::regex_constants::error_ctype)
640 std::cerr << "Invalid character class\n";
641 else if (e.code() == std::regex_constants::error_escape)
642 std::cerr << "Invalid escape character or trailing escape\n";
643 else if (e.code() == std::regex_constants::error_backref)
644 std::cerr << "Invalid back reference\n";
645 else if (e.code() == std::regex_constants::error_brack)
646 std::cerr << "Mismatched bracket ([ or ])\n";
647 else if (e.code() == std::regex_constants::error_paren)
648 {
649 // to catch return code error_badrepeat when error_paren is retured
650 // instead
651 size_t pos = pattern.find_first_of("*+?{");
652 while (pos != std::string::npos)
653 {
654 if (pos == 0 || pattern.substr(pos - 1, 1) == "|")
655 {
656 std::cerr
657 << "A repetition character (*, ?, +, or {) was not "
658 "preceded by a valid regular expression\n";
659 exit(1);
660 }
661 pos = pattern.find_first_of("*+?{", pos + 1);
662 }
663 std::cerr << "Mismatched parentheses (( or ))\n";
664 }
665 else if (e.code() == std::regex_constants::error_brace)
666 std::cerr << "Mismatched brace ({ or })\n";
667 else if (e.code() == std::regex_constants::error_badbrace)
668 std::cerr << "Invalid range inside a { }\n";
669 else if (e.code() == std::regex_constants::error_range)
670 std::cerr << "Invalid character range (e.g., [z-a])\n";
671 else if (e.code() == std::regex_constants::error_space)
672 std::cerr << "Insufficient memory to handle regular expression\n";
673 else if (e.code() == std::regex_constants::error_badrepeat)
674 std::cerr << "A repetition character (*, ?, +, or {) was not "
675 "preceded by a valid regular expression\n";
676 else if (e.code() == std::regex_constants::error_complexity)
677 std::cerr << "The requested match is too complex\n";
678 else if (e.code() == std::regex_constants::error_stack)
679 std::cerr << "Insufficient memory to evaluate a match\n";
680 exit(1);
681 }
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800682}
683
Aatir186ce8c2019-10-20 15:13:39 -0500684static void exitWithError(const std::string& help, const char* err)
685{
686 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
687 exit(-1);
688}
689
690int main(int argc, char** argv)
691{
692 CLI::App app{"OpenBMC PEL Tool"};
693 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600694 std::string idPEL;
Matt Spinler27de6f32020-02-21 08:35:57 -0600695 std::string idToDelete;
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800696 std::string scrubFile;
697 std::optional<std::regex> scrubRegex;
Aatire340c132019-12-09 14:19:29 -0600698 bool listPEL = false;
699 bool listPELDescOrd = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800700 bool hidden = false;
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800701 bool includeInfo = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600702 bool deleteAll = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800703 bool showPELCount = false;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800704 bool fullPEL = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600705
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800706 app.set_help_flag("--help", "Print this help message and exit");
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800707 app.add_option("--file", fileName, "Display a PEL using its Raw PEL file");
Aatire340c132019-12-09 14:19:29 -0600708 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800709 app.add_flag("-a", fullPEL, "Display all PELs");
Aatirbad5f8a2019-12-10 15:27:16 -0600710 app.add_flag("-l", listPEL, "List PELs");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800711 app.add_flag("-n", showPELCount, "Show number of PELs");
Aatir7b291ec2019-11-19 10:37:37 -0600712 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800713 app.add_flag("-h", hidden, "Include hidden PELs");
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800714 app.add_flag("-f,--info", includeInfo, "Include informational PELs");
Matt Spinler27de6f32020-02-21 08:35:57 -0600715 app.add_option("-d, --delete", idToDelete, "Delete a PEL based on its ID");
716 app.add_flag("-D, --delete-all", deleteAll, "Delete all PELs");
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800717 app.add_option("-s, --scrub", scrubFile,
718 "File containing SRC regular expressions to ignore");
Matt Spinler27de6f32020-02-21 08:35:57 -0600719
Aatir186ce8c2019-10-20 15:13:39 -0500720 CLI11_PARSE(app, argc, argv);
721
722 if (!fileName.empty())
723 {
724 std::vector<uint8_t> data = getFileData(fileName);
725 if (!data.empty())
726 {
727 PEL pel{data};
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800728 pel.toJSON(registry);
Aatir186ce8c2019-10-20 15:13:39 -0500729 }
730 else
731 {
732 exitWithError(app.help("", CLI::AppFormatMode::All),
733 "Raw PEL file can't be read.");
734 }
735 }
Aatire340c132019-12-09 14:19:29 -0600736 else if (!idPEL.empty())
737 {
Matt Spinler1b420bc2020-02-21 08:54:25 -0600738 callFunctionOnPEL(idPEL, displayPEL);
Aatire340c132019-12-09 14:19:29 -0600739 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800740 else if (fullPEL || listPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600741 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800742 if (!scrubFile.empty())
743 {
744 scrubRegex = genRegex(scrubFile);
745 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800746 printPELs(listPELDescOrd, hidden, includeInfo, fullPEL, scrubRegex);
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800747 }
748 else if (showPELCount)
749 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800750 if (!scrubFile.empty())
751 {
752 scrubRegex = genRegex(scrubFile);
753 }
Harisuddin Mohamed Isa0c57a672020-03-27 14:16:28 +0800754 printPELCount(hidden, includeInfo, scrubRegex);
Aatir7b291ec2019-11-19 10:37:37 -0600755 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600756 else if (!idToDelete.empty())
757 {
758 callFunctionOnPEL(idToDelete, deletePEL);
759 }
760 else if (deleteAll)
761 {
762 deleteAllPELs();
763 }
Aatir186ce8c2019-10-20 15:13:39 -0500764 else
765 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800766 std::cout << app.help("", CLI::AppFormatMode::All) << std::endl;
Aatir186ce8c2019-10-20 15:13:39 -0500767 }
768 return 0;
769}