blob: fb4153e1d8eb096bb32f1114137967c455ded582 [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>
Aatir186ce8c2019-10-20 15:13:39 -050027#include <iostream>
Aatir7b291ec2019-11-19 10:37:37 -060028#include <phosphor-logging/log.hpp>
29#include <regex>
Aatir186ce8c2019-10-20 15:13:39 -050030#include <string>
Aatir7b291ec2019-11-19 10:37:37 -060031#include <xyz/openbmc_project/Common/File/error.hpp>
Aatir186ce8c2019-10-20 15:13:39 -050032
Aatir7b291ec2019-11-19 10:37:37 -060033namespace fs = std::filesystem;
Aatir186ce8c2019-10-20 15:13:39 -050034using namespace phosphor::logging;
35using namespace openpower::pels;
Matt Spinler27de6f32020-02-21 08:35:57 -060036using sdbusplus::exception::SdBusError;
Aatir7b291ec2019-11-19 10:37:37 -060037namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
38namespace message = openpower::pels::message;
39namespace pv = openpower::pels::pel_values;
Aatir186ce8c2019-10-20 15:13:39 -050040
Matt Spinler27de6f32020-02-21 08:35:57 -060041using PELFunc = std::function<void(const PEL&)>;
42
43namespace service
44{
45constexpr auto logging = "xyz.openbmc_project.Logging";
46} // namespace service
47
48namespace interface
49{
50constexpr auto deleteObj = "xyz.openbmc_project.Object.Delete";
51constexpr auto deleteAll = "xyz.openbmc_project.Collection.DeleteAll";
52} // namespace interface
53
54namespace object_path
55{
56constexpr auto logEntry = "/xyz/openbmc_project/logging/entry/";
57constexpr auto logging = "/xyz/openbmc_project/logging";
58} // namespace object_path
59
Aatire340c132019-12-09 14:19:29 -060060/**
Aatir37822f62019-12-10 14:40:27 -060061 * @brief helper function to get PEL commit timestamp from file name
62 * @retrun BCDTime - PEL commit timestamp
63 * @param[in] std::string - file name
64 */
65BCDTime fileNameToTimestamp(const std::string& fileName)
66{
67 std::string token = fileName.substr(0, fileName.find("_"));
68 int i = 0;
69 BCDTime tmp;
70 if (token.length() >= 14)
71 {
72 try
73 {
74 tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
75 }
76 catch (std::exception& err)
77 {
78 std::cout << "Conversion failure: " << err.what() << std::endl;
79 }
80 i += 2;
81 try
82 {
83 tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
84 }
85 catch (std::exception& err)
86 {
87 std::cout << "Conversion failure: " << err.what() << std::endl;
88 }
89 i += 2;
90 try
91 {
92 tmp.month = std::stoi(token.substr(i, 2), 0, 16);
93 }
94 catch (std::exception& err)
95 {
96 std::cout << "Conversion failure: " << err.what() << std::endl;
97 }
98 i += 2;
99 try
100 {
101 tmp.day = std::stoi(token.substr(i, 2), 0, 16);
102 }
103 catch (std::exception& err)
104 {
105 std::cout << "Conversion failure: " << err.what() << std::endl;
106 }
107 i += 2;
108 try
109 {
110 tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
111 }
112 catch (std::exception& err)
113 {
114 std::cout << "Conversion failure: " << err.what() << std::endl;
115 }
116 i += 2;
117 try
118 {
119 tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
120 }
121 catch (std::exception& err)
122 {
123 std::cout << "Conversion failure: " << err.what() << std::endl;
124 }
125 i += 2;
126 try
127 {
128 tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
129 }
130 catch (std::exception& err)
131 {
132 std::cout << "Conversion failure: " << err.what() << std::endl;
133 }
134 i += 2;
135 try
136 {
137 tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
138 }
139 catch (std::exception& err)
140 {
141 std::cout << "Conversion failure: " << err.what() << std::endl;
142 }
143 }
144 return tmp;
145}
146
147/**
148 * @brief helper function to get PEL id from file name
149 * @retrun uint32_t - PEL id
150 * @param[in] std::string - file name
151 */
152uint32_t fileNameToPELId(const std::string& fileName)
153{
154 uint32_t num = 0;
155 try
156 {
157 num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
158 }
159 catch (std::exception& err)
160 {
161 std::cout << "Conversion failure: " << err.what() << std::endl;
162 }
163 return num;
164}
165
166/**
Aatire340c132019-12-09 14:19:29 -0600167 * @brief helper function to check string suffix
168 * @retrun bool - true with suffix matches
169 * @param[in] std::string - string to check for suffix
170 * @param[in] std::string - suffix string
171 */
172bool ends_with(const std::string& str, const std::string& end)
173{
174 size_t slen = str.size(), elen = end.size();
175 if (slen < elen)
176 return false;
177 while (elen)
178 {
179 if (str[--slen] != end[--elen])
180 return false;
181 }
182 return true;
183}
184
Aatir37822f62019-12-10 14:40:27 -0600185/**
186 * @brief get data form raw PEL file.
187 * @param[in] std::string Name of file with raw PEL
188 * @return std::vector<uint8_t> char vector read from raw PEL file.
189 */
Aatirbad5f8a2019-12-10 15:27:16 -0600190std::vector<uint8_t> getFileData(const std::string& name)
Aatir37822f62019-12-10 14:40:27 -0600191{
192 std::ifstream file(name, std::ifstream::in);
193 if (file.good())
194 {
195 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
196 std::istreambuf_iterator<char>()};
197 return data;
198 }
199 else
200 {
Aatir37822f62019-12-10 14:40:27 -0600201 return {};
202 }
203}
204
Aatir7b291ec2019-11-19 10:37:37 -0600205template <typename T>
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800206std::string genPELJSON(T itr, bool hidden, message::Registry& registry)
Aatir7b291ec2019-11-19 10:37:37 -0600207{
208 std::size_t found;
209 std::string val;
210 char tmpValStr[50];
211 std::string listStr;
212 char name[50];
213 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
214 itr.second.yearLSB, itr.second.month, itr.second.day,
215 itr.second.hour, itr.second.minutes, itr.second.seconds,
216 itr.second.hundredths, itr.first);
217 std::string fileName(name);
218 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
219 try
220 {
Aatir37822f62019-12-10 14:40:27 -0600221 std::vector<uint8_t> data = getFileData(fileName);
Aatir37822f62019-12-10 14:40:27 -0600222 if (!data.empty())
223 {
Aatir37822f62019-12-10 14:40:27 -0600224 PEL pel{data};
225 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
226 if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
Aatir7b291ec2019-11-19 10:37:37 -0600227 {
Aatir37822f62019-12-10 14:40:27 -0600228 // id
229 sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
230 val = std::string(tmpValStr);
231 listStr += "\t\"" + val + "\": {\n";
232 // ASCII
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800233 if (pel.primarySRC())
234 {
235 val = pel.primarySRC().value()->asciiString();
236 listStr += "\t\t\"SRC\": \"" +
237 val.substr(0, val.find(0x20)) + "\",\n";
238 // Registry message
239 auto regVal = pel.primarySRC().value()->getErrorDetails(
240 registry, DetailLevel::message, true);
241 if (regVal)
242 {
243 val = regVal.value();
244 listStr += "\t\t\"Message\": \"" + val + "\",\n";
245 }
246 }
247 else
248 {
249 listStr += "\t\t\"SRC\": \"No SRC\",\n";
250 }
Aatir37822f62019-12-10 14:40:27 -0600251 // platformid
252 sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
253 val = std::string(tmpValStr);
254 listStr += "\t\t\"PLID\": \"" + val + "\",\n";
255 // creatorid
256 sprintf(tmpValStr, "%c", pel.privateHeader().creatorID());
257 std::string creatorID(tmpValStr);
258 val = pv::creatorIDs.count(creatorID)
259 ? pv::creatorIDs.at(creatorID)
260 : "Unknown Creator ID";
261 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
262 // subsytem
263 std::string subsystem = pv::getValue(
264 pel.userHeader().subsystem(), pel_values::subsystemValues);
265 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
266 // commit time
Harisuddin Mohamed Isa160c51c2020-02-13 23:42:20 +0800267 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
Aatir37822f62019-12-10 14:40:27 -0600268 pel.privateHeader().commitTimestamp().month,
269 pel.privateHeader().commitTimestamp().day,
270 pel.privateHeader().commitTimestamp().yearMSB,
271 pel.privateHeader().commitTimestamp().yearLSB,
272 pel.privateHeader().commitTimestamp().hour,
273 pel.privateHeader().commitTimestamp().minutes,
274 pel.privateHeader().commitTimestamp().seconds);
275 val = std::string(tmpValStr);
276 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
277 // severity
278 std::string severity = pv::getValue(pel.userHeader().severity(),
279 pel_values::severityValues);
280 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
281 // compID
282 sprintf(tmpValStr, "0x%X",
283 pel.privateHeader().header().componentID);
284 val = std::string(tmpValStr);
285 listStr += "\t\t\"CompID\": \"" + val + "\",\n ";
286
287 found = listStr.rfind(",");
288 if (found != std::string::npos)
289 {
290 listStr.replace(found, 1, "");
291 listStr += "\t}, \n";
292 }
Aatir7b291ec2019-11-19 10:37:37 -0600293 }
294 }
Aatir37822f62019-12-10 14:40:27 -0600295 else
296 {
297 log<level::ERR>("Empty PEL file",
298 entry("FILENAME=%s", fileName.c_str()),
299 entry("ERROR=%s", "Empty PEL file"));
300 }
Aatir7b291ec2019-11-19 10:37:37 -0600301 }
302 catch (std::exception& e)
303 {
304 log<level::ERR>("Hit exception while reading PEL File",
305 entry("FILENAME=%s", fileName.c_str()),
306 entry("ERROR=%s", e.what()));
307 }
308 return listStr;
309}
310/**
311 * @brief Print a list of PELs
312 */
313void printList(bool order, bool hidden)
314{
315 std::string listStr;
316 std::map<uint32_t, BCDTime> PELs;
317 std::size_t found;
318 listStr = "{\n";
319 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
320 it != fs::directory_iterator(); ++it)
321 {
322 if (!fs::is_regular_file((*it).path()))
323 {
324 continue;
325 }
Aatir37822f62019-12-10 14:40:27 -0600326 else
Aatir7b291ec2019-11-19 10:37:37 -0600327 {
Aatir37822f62019-12-10 14:40:27 -0600328 PELs.emplace(fileNameToPELId((*it).path().filename()),
329 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600330 }
331 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800332 message::Registry registry(getMessageRegistryPath() /
333 message::registryFileName);
334 auto buildJSON = [&listStr, &hidden, &registry](const auto& i) {
335 listStr += genPELJSON(i, hidden, registry);
Aatir37822f62019-12-10 14:40:27 -0600336 };
Aatir7b291ec2019-11-19 10:37:37 -0600337 if (order)
338 {
339 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
340 }
341 else
342 {
343 std::for_each(PELs.begin(), PELs.end(), buildJSON);
344 }
345
346 found = listStr.rfind(",");
347 if (found != std::string::npos)
348 {
349 listStr.replace(found, 1, "");
350 listStr += "\n}\n";
351 printf("%s", listStr.c_str());
352 }
353}
Aatir186ce8c2019-10-20 15:13:39 -0500354
Matt Spinler27de6f32020-02-21 08:35:57 -0600355/**
356 * @brief Calls the function passed in on the PEL with the ID
357 * passed in.
358 *
359 * @param[in] id - The string version of the PEL ID, either with or
360 * without the 0x prefix.
361 * @param[in] func - The std::function<void(const PEL&)> function to run.
362 */
363void callFunctionOnPEL(const std::string& id, const PELFunc& func)
364{
365 std::string pelID{id};
366 std::transform(pelID.begin(), pelID.end(), pelID.begin(), toupper);
367
368 if (pelID.find("0X") == 0)
369 {
370 pelID.erase(0, 2);
371 }
372
373 bool found = false;
374
375 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
376 it != fs::directory_iterator(); ++it)
377 {
378 // The PEL ID is part of the filename, so use that to find the PEL.
379
380 if (!fs::is_regular_file((*it).path()))
381 {
382 continue;
383 }
384
385 if (ends_with((*it).path(), pelID))
386 {
387 found = true;
388
389 auto data = getFileData((*it).path());
390 if (!data.empty())
391 {
392 PEL pel{data};
393
394 try
395 {
396 func(pel);
397 }
398 catch (std::exception& e)
399 {
400 std::cerr
401 << " Internal function threw an exception: " << e.what()
402 << "\n";
403 exit(1);
404 }
405 }
406 else
407 {
408 std::cerr << "Could not read PEL file\n";
409 exit(1);
410 }
411 break;
412 }
413 }
414
415 if (!found)
416 {
417 std::cerr << "PEL not found\n";
418 exit(1);
419 }
420}
421
422/**
423 * @brief Delete a PEL by deleting its corresponding event log.
424 *
425 * @param[in] pel - The PEL to delete
426 */
427void deletePEL(const PEL& pel)
428{
429 std::string path{object_path::logEntry};
430 path += std::to_string(pel.obmcLogID());
431
432 try
433 {
434 auto bus = sdbusplus::bus::new_default();
435 auto method = bus.new_method_call(service::logging, path.c_str(),
436 interface::deleteObj, "Delete");
437 auto reply = bus.call(method);
438 }
439 catch (const SdBusError& e)
440 {
441 std::cerr << "D-Bus call to delete event log " << pel.obmcLogID()
442 << " failed: " << e.what() << "\n";
443 exit(1);
444 }
445}
446
447/**
448 * @brief Delete all PELs by deleting all event logs.
449 */
450void deleteAllPELs()
451{
452 try
453 {
454 // This may move to an audit log some day
455 log<level::INFO>("peltool deleting all event logs");
456
457 auto bus = sdbusplus::bus::new_default();
458 auto method =
459 bus.new_method_call(service::logging, object_path::logging,
460 interface::deleteAll, "DeleteAll");
461 auto reply = bus.call(method);
462 }
463 catch (const SdBusError& e)
464 {
465 std::cerr << "D-Bus call to delete all event logs failed: " << e.what()
466 << "\n";
467 exit(1);
468 }
469}
470
Matt Spinler1b420bc2020-02-21 08:54:25 -0600471/**
472 * @brief Display a single PEL
473 *
474 * @param[in] pel - the PEL to display
475 */
476void displayPEL(const PEL& pel)
477{
478 if (pel.valid())
479 {
480 pel.toJSON();
481 }
482 else
483 {
484 std::cerr << "PEL was malformed\n";
485 exit(1);
486 }
487}
488
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800489/**
490 * @brief Print number of PELs
491 * @param[in] hidden - Bool to include hidden logs
492 */
493void printPELCount(bool hidden)
494{
495 std::size_t count = 0;
496 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
497 it != fs::directory_iterator(); ++it)
498 {
499 if (!fs::is_regular_file((*it).path()))
500 {
501 continue;
502 }
503 else
504 {
505 std::vector<uint8_t> data = getFileData((*it).path());
506 if (!data.empty())
507 {
508 PEL pel{data};
509 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
510 if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
511 {
512 count++;
513 }
514 }
515 }
516 }
517 std::cout << "{\n"
518 << " \"Number of PELs found\": "
519 << getNumberString("%d", count) << "\n}" << std::endl;
520}
521
Aatir186ce8c2019-10-20 15:13:39 -0500522static void exitWithError(const std::string& help, const char* err)
523{
524 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
525 exit(-1);
526}
527
528int main(int argc, char** argv)
529{
530 CLI::App app{"OpenBMC PEL Tool"};
531 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600532 std::string idPEL;
Matt Spinler27de6f32020-02-21 08:35:57 -0600533 std::string idToDelete;
Aatire340c132019-12-09 14:19:29 -0600534 bool listPEL = false;
535 bool listPELDescOrd = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800536 bool hidden = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600537 bool deleteAll = false;
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800538 bool showPELCount = false;
Matt Spinler27de6f32020-02-21 08:35:57 -0600539
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800540 app.set_help_flag("--help", "Print this help message and exit");
Aatire340c132019-12-09 14:19:29 -0600541 app.add_option("-f,--file", fileName,
542 "Display a PEL using its Raw PEL file");
543 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Aatirbad5f8a2019-12-10 15:27:16 -0600544 app.add_flag("-l", listPEL, "List PELs");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800545 app.add_flag("-n", showPELCount, "Show number of PELs");
Aatir7b291ec2019-11-19 10:37:37 -0600546 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800547 app.add_flag("-h", hidden, "Include hidden PELs");
Matt Spinler27de6f32020-02-21 08:35:57 -0600548 app.add_option("-d, --delete", idToDelete, "Delete a PEL based on its ID");
549 app.add_flag("-D, --delete-all", deleteAll, "Delete all PELs");
550
Aatir186ce8c2019-10-20 15:13:39 -0500551 CLI11_PARSE(app, argc, argv);
552
553 if (!fileName.empty())
554 {
555 std::vector<uint8_t> data = getFileData(fileName);
556 if (!data.empty())
557 {
558 PEL pel{data};
559 pel.toJSON();
560 }
561 else
562 {
563 exitWithError(app.help("", CLI::AppFormatMode::All),
564 "Raw PEL file can't be read.");
565 }
566 }
Aatir7b291ec2019-11-19 10:37:37 -0600567
Aatire340c132019-12-09 14:19:29 -0600568 else if (!idPEL.empty())
569 {
Matt Spinler1b420bc2020-02-21 08:54:25 -0600570 callFunctionOnPEL(idPEL, displayPEL);
Aatire340c132019-12-09 14:19:29 -0600571 }
Aatir7b291ec2019-11-19 10:37:37 -0600572 else if (listPEL)
573 {
Harisuddin Mohamed Isad4002f32020-02-26 16:19:58 +0800574 printList(listPELDescOrd, hidden);
575 }
576 else if (showPELCount)
577 {
578 printPELCount(hidden);
Aatir7b291ec2019-11-19 10:37:37 -0600579 }
Matt Spinler27de6f32020-02-21 08:35:57 -0600580 else if (!idToDelete.empty())
581 {
582 callFunctionOnPEL(idToDelete, deletePEL);
583 }
584 else if (deleteAll)
585 {
586 deleteAllPELs();
587 }
Aatir186ce8c2019-10-20 15:13:39 -0500588 else
589 {
590 exitWithError(app.help("", CLI::AppFormatMode::All),
591 "Raw PEL file path not specified.");
592 }
593 return 0;
594}