blob: 7d1e304169a6ffccbf8b059e90b7703861c6201a [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&)>;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +080043message::Registry registry(getMessageRegistryPath() /
44 message::registryFileName);
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
212 * @param[in] fullPEL - Boolean to print full JSON representation of PEL
213 * @param[in] foundPEL - Boolean to check if any PEL is present
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800214 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800215 * @return std::string - JSON string of PEL entry (empty if fullPEL is true)
216 */
Aatir7b291ec2019-11-19 10:37:37 -0600217template <typename T>
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800218std::string genPELJSON(T itr, bool hidden, bool fullPEL, bool& foundPEL,
219 const std::optional<std::regex>& scrubRegex)
Aatir7b291ec2019-11-19 10:37:37 -0600220{
221 std::size_t found;
222 std::string val;
223 char tmpValStr[50];
224 std::string listStr;
225 char name[50];
226 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
227 itr.second.yearLSB, itr.second.month, itr.second.day,
228 itr.second.hour, itr.second.minutes, itr.second.seconds,
229 itr.second.hundredths, itr.first);
230 std::string fileName(name);
231 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
232 try
233 {
Aatir37822f62019-12-10 14:40:27 -0600234 std::vector<uint8_t> data = getFileData(fileName);
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800235 if (data.empty())
Aatir37822f62019-12-10 14:40:27 -0600236 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800237 log<level::ERR>("Empty PEL file",
238 entry("FILENAME=%s", fileName.c_str()));
239 return listStr;
240 }
241 PEL pel{data};
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800242 if (!pel.valid())
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800243 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800244 return listStr;
245 }
246 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
247 if (!hidden && actionFlags.test(hiddenFlagBit))
248 {
249 return listStr;
250 }
251 if (pel.primarySRC() && scrubRegex)
252 {
253 val = pel.primarySRC().value()->asciiString();
254 if (std::regex_search(trimEnd(val), scrubRegex.value(),
255 std::regex_constants::match_not_null))
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800256 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800257 return listStr;
258 }
259 }
260 if (fullPEL)
261 {
262 if (!foundPEL)
263 {
264 std::cout << "[\n";
265 foundPEL = true;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800266 }
267 else
Aatir7b291ec2019-11-19 10:37:37 -0600268 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800269 std::cout << ",\n\n";
270 }
271 pel.toJSON(registry);
272 }
273 else
274 {
275 // id
276 listStr += "\t\"" +
277 getNumberString("0x%X", pel.privateHeader().id()) +
278 "\": {\n";
279 // ASCII
280 if (pel.primarySRC())
281 {
282 val = pel.primarySRC().value()->asciiString();
283 listStr += "\t\t\"SRC\": \"" + trimEnd(val) + "\",\n";
284 // Registry message
285 auto regVal = pel.primarySRC().value()->getErrorDetails(
286 registry, DetailLevel::message, true);
287 if (regVal)
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800288 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800289 val = regVal.value();
290 listStr += "\t\t\"Message\": \"" + val + "\",\n";
Aatir37822f62019-12-10 14:40:27 -0600291 }
Aatir7b291ec2019-11-19 10:37:37 -0600292 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800293 else
294 {
295 listStr += "\t\t\"SRC\": \"No SRC\",\n";
296 }
297 // platformid
298 listStr += "\t\t\"PLID\": \"" +
299 getNumberString("0x%X", pel.privateHeader().plid()) +
300 "\",\n";
301 // creatorid
302 std::string creatorID =
303 getNumberString("%c", pel.privateHeader().creatorID());
304 val = pv::creatorIDs.count(creatorID) ? pv::creatorIDs.at(creatorID)
305 : "Unknown Creator ID";
306 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
307 // subsytem
308 std::string subsystem = pv::getValue(pel.userHeader().subsystem(),
309 pel_values::subsystemValues);
310 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
311 // commit time
312 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
313 pel.privateHeader().commitTimestamp().month,
314 pel.privateHeader().commitTimestamp().day,
315 pel.privateHeader().commitTimestamp().yearMSB,
316 pel.privateHeader().commitTimestamp().yearLSB,
317 pel.privateHeader().commitTimestamp().hour,
318 pel.privateHeader().commitTimestamp().minutes,
319 pel.privateHeader().commitTimestamp().seconds);
320 val = std::string(tmpValStr);
321 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
322 // severity
323 std::string severity = pv::getValue(pel.userHeader().severity(),
324 pel_values::severityValues);
325 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
326 // compID
327 listStr += "\t\t\"CompID\": \"" +
328 getNumberString(
329 "0x%X", pel.privateHeader().header().componentID) +
330 "\",\n ";
331 found = listStr.rfind(",");
332 if (found != std::string::npos)
333 {
334 listStr.replace(found, 1, "");
335 listStr += "\t},\n";
336 }
337 foundPEL = true;
Aatir7b291ec2019-11-19 10:37:37 -0600338 }
339 }
340 catch (std::exception& e)
341 {
342 log<level::ERR>("Hit exception while reading PEL File",
343 entry("FILENAME=%s", fileName.c_str()),
344 entry("ERROR=%s", e.what()));
345 }
346 return listStr;
347}
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800348
Aatir7b291ec2019-11-19 10:37:37 -0600349/**
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800350 * @brief Print a list of PELs or a JSON array of PELs
351 * @param[in] order - Boolean to print in reverse orser
352 * @param[in] hidden - Boolean to include hidden PELs
353 * @param[in] fullPEL - Boolean to print full PEL into a JSON array
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800354 * @param[in] scrubRegex - SRC regex object
Aatir7b291ec2019-11-19 10:37:37 -0600355 */
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800356void printPELs(bool order, bool hidden, bool fullPEL,
357 const std::optional<std::regex>& scrubRegex)
Aatir7b291ec2019-11-19 10:37:37 -0600358{
359 std::string listStr;
360 std::map<uint32_t, BCDTime> PELs;
Aatir7b291ec2019-11-19 10:37:37 -0600361 listStr = "{\n";
362 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
363 it != fs::directory_iterator(); ++it)
364 {
365 if (!fs::is_regular_file((*it).path()))
366 {
367 continue;
368 }
Aatir37822f62019-12-10 14:40:27 -0600369 else
Aatir7b291ec2019-11-19 10:37:37 -0600370 {
Aatir37822f62019-12-10 14:40:27 -0600371 PELs.emplace(fileNameToPELId((*it).path().filename()),
372 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600373 }
374 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800375 bool foundPEL = false;
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800376 auto buildJSON = [&listStr, &hidden, &fullPEL, &foundPEL,
377 &scrubRegex](const auto& i) {
378 listStr += genPELJSON(i, hidden, fullPEL, foundPEL, scrubRegex);
Aatir37822f62019-12-10 14:40:27 -0600379 };
Aatir7b291ec2019-11-19 10:37:37 -0600380 if (order)
381 {
382 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
383 }
384 else
385 {
386 std::for_each(PELs.begin(), PELs.end(), buildJSON);
387 }
388
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800389 if (foundPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600390 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800391 if (fullPEL)
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800392 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800393 std::cout << "]" << std::endl;
394 }
395 else
396 {
397 std::size_t found;
398 found = listStr.rfind(",");
399 if (found != std::string::npos)
400 {
401 listStr.replace(found, 1, "");
402 listStr += "}\n";
403 printf("%s", listStr.c_str());
404 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800405 }
406 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800407 else
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800408 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800409 std::string emptyJSON = fullPEL ? "[]" : "{}";
410 std::cout << emptyJSON << std::endl;
Aatir7b291ec2019-11-19 10:37:37 -0600411 }
412}
Aatir186ce8c2019-10-20 15:13:39 -0500413
Matt Spinler27de6f32020-02-21 08:35:57 -0600414/**
415 * @brief Calls the function passed in on the PEL with the ID
416 * passed in.
417 *
418 * @param[in] id - The string version of the PEL ID, either with or
419 * without the 0x prefix.
420 * @param[in] func - The std::function<void(const PEL&)> function to run.
421 */
422void callFunctionOnPEL(const std::string& id, const PELFunc& func)
423{
424 std::string pelID{id};
425 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
426
427 if (pelID.find("0X") == 0)
428 {
429 pelID.erase(0, 2);
430 }
431
432 bool found = false;
433
434 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
435 it != fs::directory_iterator(); ++it)
436 {
437 // The PEL ID is part of the filename, so use that to find the PEL.
438
439 if (!fs::is_regular_file((*it).path()))
440 {
441 continue;
442 }
443
444 if (ends_with((*it).path(), pelID))
445 {
446 found = true;
447
448 auto data = getFileData((*it).path());
449 if (!data.empty())
450 {
451 PEL pel{data};
452
453 try
454 {
455 func(pel);
456 }
457 catch (std::exception& e)
458 {
459 std::cerr
460 << " Internal function threw an exception: " << e.what()
461 << "\n";
462 exit(1);
463 }
464 }
465 else
466 {
467 std::cerr << "Could not read PEL file\n";
468 exit(1);
469 }
470 break;
471 }
472 }
473
474 if (!found)
475 {
476 std::cerr << "PEL not found\n";
477 exit(1);
478 }
479}
480
481/**
482 * @brief Delete a PEL by deleting its corresponding event log.
483 *
484 * @param[in] pel - The PEL to delete
485 */
486void deletePEL(const PEL& pel)
487{
488 std::string path{object_path::logEntry};
489 path += std::to_string(pel.obmcLogID());
490
491 try
492 {
493 auto bus = sdbusplus::bus::new_default();
494 auto method = bus.new_method_call(service::logging, path.c_str(),
495 interface::deleteObj, "Delete");
496 auto reply = bus.call(method);
497 }
498 catch (const SdBusError& e)
499 {
500 std::cerr << "D-Bus call to delete event log " << pel.obmcLogID()
501 << " failed: " << e.what() << "\n";
502 exit(1);
503 }
504}
505
506/**
507 * @brief Delete all PELs by deleting all event logs.
508 */
509void deleteAllPELs()
510{
511 try
512 {
513 // This may move to an audit log some day
514 log<level::INFO>("peltool deleting all event logs");
515
516 auto bus = sdbusplus::bus::new_default();
517 auto method =
518 bus.new_method_call(service::logging, object_path::logging,
519 interface::deleteAll, "DeleteAll");
520 auto reply = bus.call(method);
521 }
522 catch (const SdBusError& e)
523 {
524 std::cerr << "D-Bus call to delete all event logs failed: " << e.what()
525 << "\n";
526 exit(1);
527 }
528}
529
Matt Spinler1b420bc2020-02-21 08:54:25 -0600530/**
531 * @brief Display a single PEL
532 *
533 * @param[in] pel - the PEL to display
534 */
535void displayPEL(const PEL& pel)
536{
537 if (pel.valid())
538 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800539 pel.toJSON(registry);
Matt Spinler1b420bc2020-02-21 08:54:25 -0600540 }
541 else
542 {
543 std::cerr << "PEL was malformed\n";
544 exit(1);
545 }
546}
547
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800548/**
549 * @brief Print number of PELs
550 * @param[in] hidden - Bool to include hidden logs
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800551 * @param[in] scrubRegex - SRC regex object
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800552 */
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800553void printPELCount(bool hidden, const std::optional<std::regex>& scrubRegex)
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800554{
555 std::size_t count = 0;
556 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
557 it != fs::directory_iterator(); ++it)
558 {
559 if (!fs::is_regular_file((*it).path()))
560 {
561 continue;
562 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800563 std::vector<uint8_t> data = getFileData((*it).path());
564 if (data.empty())
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800565 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800566 continue;
567 }
568 PEL pel{data};
569 if (!pel.valid())
570 {
571 continue;
572 }
573 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
574 if (!hidden && actionFlags.test(hiddenFlagBit))
575 {
576 continue;
577 }
578 if (pel.primarySRC() && scrubRegex)
579 {
580 std::string val = pel.primarySRC().value()->asciiString();
581 if (std::regex_search(trimEnd(val), scrubRegex.value(),
582 std::regex_constants::match_not_null))
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800583 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800584 continue;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800585 }
586 }
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800587 count++;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800588 }
589 std::cout << "{\n"
590 << " \"Number of PELs found\": "
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800591 << getNumberString("%d", count) << "\n}\n";
592}
593
594/**
595 * @brief Generate regex pattern object from file contents
596 * @param[in] scrubFile - File containing regex pattern
597 * @return std::regex - SRC regex object
598 */
599std::regex genRegex(std::string& scrubFile)
600{
601 std::string pattern;
602 std::ifstream contents(scrubFile);
603 if (contents.fail())
604 {
605 std::cerr << "Can't open \"" << scrubFile << "\"\n";
606 exit(1);
607 }
608 std::string line;
609 while (std::getline(contents, line))
610 {
611 if (!line.empty())
612 {
613 pattern.append(line + "|");
614 }
615 }
616 try
617 {
618 std::regex scrubRegex(pattern, std::regex::icase);
619 return scrubRegex;
620 }
621 catch (std::regex_error& e)
622 {
623 if (e.code() == std::regex_constants::error_collate)
624 std::cerr << "Invalid collating element request\n";
625 else if (e.code() == std::regex_constants::error_ctype)
626 std::cerr << "Invalid character class\n";
627 else if (e.code() == std::regex_constants::error_escape)
628 std::cerr << "Invalid escape character or trailing escape\n";
629 else if (e.code() == std::regex_constants::error_backref)
630 std::cerr << "Invalid back reference\n";
631 else if (e.code() == std::regex_constants::error_brack)
632 std::cerr << "Mismatched bracket ([ or ])\n";
633 else if (e.code() == std::regex_constants::error_paren)
634 {
635 // to catch return code error_badrepeat when error_paren is retured
636 // instead
637 size_t pos = pattern.find_first_of("*+?{");
638 while (pos != std::string::npos)
639 {
640 if (pos == 0 || pattern.substr(pos - 1, 1) == "|")
641 {
642 std::cerr
643 << "A repetition character (*, ?, +, or {) was not "
644 "preceded by a valid regular expression\n";
645 exit(1);
646 }
647 pos = pattern.find_first_of("*+?{", pos + 1);
648 }
649 std::cerr << "Mismatched parentheses (( or ))\n";
650 }
651 else if (e.code() == std::regex_constants::error_brace)
652 std::cerr << "Mismatched brace ({ or })\n";
653 else if (e.code() == std::regex_constants::error_badbrace)
654 std::cerr << "Invalid range inside a { }\n";
655 else if (e.code() == std::regex_constants::error_range)
656 std::cerr << "Invalid character range (e.g., [z-a])\n";
657 else if (e.code() == std::regex_constants::error_space)
658 std::cerr << "Insufficient memory to handle regular expression\n";
659 else if (e.code() == std::regex_constants::error_badrepeat)
660 std::cerr << "A repetition character (*, ?, +, or {) was not "
661 "preceded by a valid regular expression\n";
662 else if (e.code() == std::regex_constants::error_complexity)
663 std::cerr << "The requested match is too complex\n";
664 else if (e.code() == std::regex_constants::error_stack)
665 std::cerr << "Insufficient memory to evaluate a match\n";
666 exit(1);
667 }
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800668}
669
Aatir186ce8c2019-10-20 15:13:39 -0500670static void exitWithError(const std::string& help, const char* err)
671{
672 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
673 exit(-1);
674}
675
676int main(int argc, char** argv)
677{
678 CLI::App app{"OpenBMC PEL Tool"};
679 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600680 std::string idPEL;
Matt Spinler27de6f32020-02-21 08:35:57 -0600681 std::string idToDelete;
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800682 std::string scrubFile;
683 std::optional<std::regex> scrubRegex;
Aatire340c132019-12-09 14:19:29 -0600684 bool listPEL = false;
685 bool listPELDescOrd = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800686 bool hidden = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600687 bool deleteAll = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800688 bool showPELCount = false;
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800689 bool fullPEL = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600690
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800691 app.set_help_flag("--help", "Print this help message and exit");
Aatire340c132019-12-09 14:19:29 -0600692 app.add_option("-f,--file", fileName,
693 "Display a PEL using its Raw PEL file");
694 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800695 app.add_flag("-a", fullPEL, "Display all PELs");
Aatirbad5f8a2019-12-10 15:27:16 -0600696 app.add_flag("-l", listPEL, "List PELs");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800697 app.add_flag("-n", showPELCount, "Show number of PELs");
Aatir7b291ec2019-11-19 10:37:37 -0600698 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800699 app.add_flag("-h", hidden, "Include hidden PELs");
Matt Spinler27de6f32020-02-21 08:35:57 -0600700 app.add_option("-d, --delete", idToDelete, "Delete a PEL based on its ID");
701 app.add_flag("-D, --delete-all", deleteAll, "Delete all PELs");
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800702 app.add_option("-s, --scrub", scrubFile,
703 "File containing SRC regular expressions to ignore");
Matt Spinler27de6f32020-02-21 08:35:57 -0600704
Aatir186ce8c2019-10-20 15:13:39 -0500705 CLI11_PARSE(app, argc, argv);
706
707 if (!fileName.empty())
708 {
709 std::vector<uint8_t> data = getFileData(fileName);
710 if (!data.empty())
711 {
712 PEL pel{data};
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800713 pel.toJSON(registry);
Aatir186ce8c2019-10-20 15:13:39 -0500714 }
715 else
716 {
717 exitWithError(app.help("", CLI::AppFormatMode::All),
718 "Raw PEL file can't be read.");
719 }
720 }
Aatire340c132019-12-09 14:19:29 -0600721 else if (!idPEL.empty())
722 {
Matt Spinler1b420bc2020-02-21 08:54:25 -0600723 callFunctionOnPEL(idPEL, displayPEL);
Aatire340c132019-12-09 14:19:29 -0600724 }
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800725 else if (fullPEL || listPEL)
Aatir7b291ec2019-11-19 10:37:37 -0600726 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800727 if (!scrubFile.empty())
728 {
729 scrubRegex = genRegex(scrubFile);
730 }
731 printPELs(listPELDescOrd, hidden, fullPEL, scrubRegex);
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800732 }
733 else if (showPELCount)
734 {
Harisuddin Mohamed Isa6f005192020-03-10 17:39:53 +0800735 if (!scrubFile.empty())
736 {
737 scrubRegex = genRegex(scrubFile);
738 }
739 printPELCount(hidden, scrubRegex);
Aatir7b291ec2019-11-19 10:37:37 -0600740 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600741 else if (!idToDelete.empty())
742 {
743 callFunctionOnPEL(idToDelete, deletePEL);
744 }
745 else if (deleteAll)
746 {
747 deleteAllPELs();
748 }
Aatir186ce8c2019-10-20 15:13:39 -0500749 else
750 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800751 std::cout << app.help("", CLI::AppFormatMode::All) << std::endl;
Aatir186ce8c2019-10-20 15:13:39 -0500752 }
753 return 0;
754}