blob: f1d5f5964859d0d12e44abb28b0dce7b55004789 [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 Isa0f717e12020-01-15 20:05:33 +080019#include "../paths.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050020#include "../pel.hpp"
Aatir7b291ec2019-11-19 10:37:37 -060021#include "../pel_types.hpp"
22#include "../pel_values.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050023
24#include <CLI/CLI.hpp>
Aatir7b291ec2019-11-19 10:37:37 -060025#include <bitset>
Aatir186ce8c2019-10-20 15:13:39 -050026#include <iostream>
Aatir7b291ec2019-11-19 10:37:37 -060027#include <phosphor-logging/log.hpp>
28#include <regex>
Aatir186ce8c2019-10-20 15:13:39 -050029#include <string>
Aatir7b291ec2019-11-19 10:37:37 -060030#include <xyz/openbmc_project/Common/File/error.hpp>
Aatir186ce8c2019-10-20 15:13:39 -050031
Aatir7b291ec2019-11-19 10:37:37 -060032namespace fs = std::filesystem;
Aatir186ce8c2019-10-20 15:13:39 -050033using namespace phosphor::logging;
34using namespace openpower::pels;
Aatir7b291ec2019-11-19 10:37:37 -060035namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
36namespace message = openpower::pels::message;
37namespace pv = openpower::pels::pel_values;
Aatir186ce8c2019-10-20 15:13:39 -050038
Aatire340c132019-12-09 14:19:29 -060039/**
Aatir37822f62019-12-10 14:40:27 -060040 * @brief helper function to get PEL commit timestamp from file name
41 * @retrun BCDTime - PEL commit timestamp
42 * @param[in] std::string - file name
43 */
44BCDTime fileNameToTimestamp(const std::string& fileName)
45{
46 std::string token = fileName.substr(0, fileName.find("_"));
47 int i = 0;
48 BCDTime tmp;
49 if (token.length() >= 14)
50 {
51 try
52 {
53 tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
54 }
55 catch (std::exception& err)
56 {
57 std::cout << "Conversion failure: " << err.what() << std::endl;
58 }
59 i += 2;
60 try
61 {
62 tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
63 }
64 catch (std::exception& err)
65 {
66 std::cout << "Conversion failure: " << err.what() << std::endl;
67 }
68 i += 2;
69 try
70 {
71 tmp.month = std::stoi(token.substr(i, 2), 0, 16);
72 }
73 catch (std::exception& err)
74 {
75 std::cout << "Conversion failure: " << err.what() << std::endl;
76 }
77 i += 2;
78 try
79 {
80 tmp.day = std::stoi(token.substr(i, 2), 0, 16);
81 }
82 catch (std::exception& err)
83 {
84 std::cout << "Conversion failure: " << err.what() << std::endl;
85 }
86 i += 2;
87 try
88 {
89 tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
90 }
91 catch (std::exception& err)
92 {
93 std::cout << "Conversion failure: " << err.what() << std::endl;
94 }
95 i += 2;
96 try
97 {
98 tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
99 }
100 catch (std::exception& err)
101 {
102 std::cout << "Conversion failure: " << err.what() << std::endl;
103 }
104 i += 2;
105 try
106 {
107 tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
108 }
109 catch (std::exception& err)
110 {
111 std::cout << "Conversion failure: " << err.what() << std::endl;
112 }
113 i += 2;
114 try
115 {
116 tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
117 }
118 catch (std::exception& err)
119 {
120 std::cout << "Conversion failure: " << err.what() << std::endl;
121 }
122 }
123 return tmp;
124}
125
126/**
127 * @brief helper function to get PEL id from file name
128 * @retrun uint32_t - PEL id
129 * @param[in] std::string - file name
130 */
131uint32_t fileNameToPELId(const std::string& fileName)
132{
133 uint32_t num = 0;
134 try
135 {
136 num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
137 }
138 catch (std::exception& err)
139 {
140 std::cout << "Conversion failure: " << err.what() << std::endl;
141 }
142 return num;
143}
144
145/**
Aatire340c132019-12-09 14:19:29 -0600146 * @brief helper function to check string suffix
147 * @retrun bool - true with suffix matches
148 * @param[in] std::string - string to check for suffix
149 * @param[in] std::string - suffix string
150 */
151bool ends_with(const std::string& str, const std::string& end)
152{
153 size_t slen = str.size(), elen = end.size();
154 if (slen < elen)
155 return false;
156 while (elen)
157 {
158 if (str[--slen] != end[--elen])
159 return false;
160 }
161 return true;
162}
163
Aatir37822f62019-12-10 14:40:27 -0600164/**
165 * @brief get data form raw PEL file.
166 * @param[in] std::string Name of file with raw PEL
167 * @return std::vector<uint8_t> char vector read from raw PEL file.
168 */
Aatirbad5f8a2019-12-10 15:27:16 -0600169std::vector<uint8_t> getFileData(const std::string& name)
Aatir37822f62019-12-10 14:40:27 -0600170{
171 std::ifstream file(name, std::ifstream::in);
172 if (file.good())
173 {
174 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
175 std::istreambuf_iterator<char>()};
176 return data;
177 }
178 else
179 {
180 printf("Can't open raw PEL file");
181 return {};
182 }
183}
184
Aatirbad5f8a2019-12-10 15:27:16 -0600185/**
186 * @brief helper function to trim trailing whitespaces
187 * @return std::string - trimmed string
188 * @param[in] std::string - string to trim
189 */
190const char* ws = " \t\n\r\f\v";
191std::string trim(std::string s, const char* t = ws)
192{
193 if (s.find_last_not_of(t) != std::string::npos)
194 {
195 s.erase(s.find_last_not_of(t) + 1);
196 }
197 return s;
198}
199
Aatir7b291ec2019-11-19 10:37:37 -0600200template <typename T>
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800201std::string genPELJSON(T itr, bool hidden, message::Registry& registry)
Aatir7b291ec2019-11-19 10:37:37 -0600202{
203 std::size_t found;
204 std::string val;
205 char tmpValStr[50];
206 std::string listStr;
207 char name[50];
208 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
209 itr.second.yearLSB, itr.second.month, itr.second.day,
210 itr.second.hour, itr.second.minutes, itr.second.seconds,
211 itr.second.hundredths, itr.first);
212 std::string fileName(name);
213 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
214 try
215 {
Aatir37822f62019-12-10 14:40:27 -0600216 std::vector<uint8_t> data = getFileData(fileName);
Aatir37822f62019-12-10 14:40:27 -0600217 if (!data.empty())
218 {
Aatir37822f62019-12-10 14:40:27 -0600219 PEL pel{data};
220 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
221 if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
Aatir7b291ec2019-11-19 10:37:37 -0600222 {
Aatir37822f62019-12-10 14:40:27 -0600223 // id
224 sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
225 val = std::string(tmpValStr);
226 listStr += "\t\"" + val + "\": {\n";
227 // ASCII
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800228 if (pel.primarySRC())
229 {
230 val = pel.primarySRC().value()->asciiString();
231 listStr += "\t\t\"SRC\": \"" +
232 val.substr(0, val.find(0x20)) + "\",\n";
233 // Registry message
234 auto regVal = pel.primarySRC().value()->getErrorDetails(
235 registry, DetailLevel::message, true);
236 if (regVal)
237 {
238 val = regVal.value();
239 listStr += "\t\t\"Message\": \"" + val + "\",\n";
240 }
241 }
242 else
243 {
244 listStr += "\t\t\"SRC\": \"No SRC\",\n";
245 }
Aatir37822f62019-12-10 14:40:27 -0600246 // platformid
247 sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
248 val = std::string(tmpValStr);
249 listStr += "\t\t\"PLID\": \"" + val + "\",\n";
250 // creatorid
251 sprintf(tmpValStr, "%c", pel.privateHeader().creatorID());
252 std::string creatorID(tmpValStr);
253 val = pv::creatorIDs.count(creatorID)
254 ? pv::creatorIDs.at(creatorID)
255 : "Unknown Creator ID";
256 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
257 // subsytem
258 std::string subsystem = pv::getValue(
259 pel.userHeader().subsystem(), pel_values::subsystemValues);
260 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
261 // commit time
262 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
263 pel.privateHeader().commitTimestamp().month,
264 pel.privateHeader().commitTimestamp().day,
265 pel.privateHeader().commitTimestamp().yearMSB,
266 pel.privateHeader().commitTimestamp().yearLSB,
267 pel.privateHeader().commitTimestamp().hour,
268 pel.privateHeader().commitTimestamp().minutes,
269 pel.privateHeader().commitTimestamp().seconds);
270 val = std::string(tmpValStr);
271 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
272 // severity
273 std::string severity = pv::getValue(pel.userHeader().severity(),
274 pel_values::severityValues);
275 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
276 // compID
277 sprintf(tmpValStr, "0x%X",
278 pel.privateHeader().header().componentID);
279 val = std::string(tmpValStr);
280 listStr += "\t\t\"CompID\": \"" + val + "\",\n ";
281
282 found = listStr.rfind(",");
283 if (found != std::string::npos)
284 {
285 listStr.replace(found, 1, "");
286 listStr += "\t}, \n";
287 }
Aatir7b291ec2019-11-19 10:37:37 -0600288 }
289 }
Aatir37822f62019-12-10 14:40:27 -0600290 else
291 {
292 log<level::ERR>("Empty PEL file",
293 entry("FILENAME=%s", fileName.c_str()),
294 entry("ERROR=%s", "Empty PEL file"));
295 }
Aatir7b291ec2019-11-19 10:37:37 -0600296 }
297 catch (std::exception& e)
298 {
299 log<level::ERR>("Hit exception while reading PEL File",
300 entry("FILENAME=%s", fileName.c_str()),
301 entry("ERROR=%s", e.what()));
302 }
303 return listStr;
304}
305/**
306 * @brief Print a list of PELs
307 */
308void printList(bool order, bool hidden)
309{
310 std::string listStr;
311 std::map<uint32_t, BCDTime> PELs;
312 std::size_t found;
313 listStr = "{\n";
314 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
315 it != fs::directory_iterator(); ++it)
316 {
317 if (!fs::is_regular_file((*it).path()))
318 {
319 continue;
320 }
Aatir37822f62019-12-10 14:40:27 -0600321 else
Aatir7b291ec2019-11-19 10:37:37 -0600322 {
Aatir37822f62019-12-10 14:40:27 -0600323 PELs.emplace(fileNameToPELId((*it).path().filename()),
324 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600325 }
326 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800327 message::Registry registry(getMessageRegistryPath() /
328 message::registryFileName);
329 auto buildJSON = [&listStr, &hidden, &registry](const auto& i) {
330 listStr += genPELJSON(i, hidden, registry);
Aatir37822f62019-12-10 14:40:27 -0600331 };
Aatir7b291ec2019-11-19 10:37:37 -0600332 if (order)
333 {
334 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
335 }
336 else
337 {
338 std::for_each(PELs.begin(), PELs.end(), buildJSON);
339 }
340
341 found = listStr.rfind(",");
342 if (found != std::string::npos)
343 {
344 listStr.replace(found, 1, "");
345 listStr += "\n}\n";
346 printf("%s", listStr.c_str());
347 }
348}
Aatir186ce8c2019-10-20 15:13:39 -0500349
350static void exitWithError(const std::string& help, const char* err)
351{
352 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
353 exit(-1);
354}
355
356int main(int argc, char** argv)
357{
358 CLI::App app{"OpenBMC PEL Tool"};
359 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600360 std::string idPEL;
361 bool listPEL = false;
362 bool listPELDescOrd = false;
363 bool listPELShowHidden = false;
364 app.add_option("-f,--file", fileName,
365 "Display a PEL using its Raw PEL file");
366 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Aatirbad5f8a2019-12-10 15:27:16 -0600367 app.add_flag("-l", listPEL, "List PELs");
Aatir7b291ec2019-11-19 10:37:37 -0600368 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
369 app.add_flag("-s", listPELShowHidden, "Show hidden PELs");
Aatir186ce8c2019-10-20 15:13:39 -0500370 CLI11_PARSE(app, argc, argv);
371
372 if (!fileName.empty())
373 {
374 std::vector<uint8_t> data = getFileData(fileName);
375 if (!data.empty())
376 {
377 PEL pel{data};
378 pel.toJSON();
379 }
380 else
381 {
382 exitWithError(app.help("", CLI::AppFormatMode::All),
383 "Raw PEL file can't be read.");
384 }
385 }
Aatir7b291ec2019-11-19 10:37:37 -0600386
Aatire340c132019-12-09 14:19:29 -0600387 else if (!idPEL.empty())
388 {
389 for (auto it =
390 fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
391 it != fs::directory_iterator(); ++it)
392 {
393 if (!fs::is_regular_file((*it).path()))
394 {
395 continue;
396 }
397 try
398 {
399 for (auto& c : idPEL)
400 c = toupper(c);
401 size_t found = idPEL.find("0X");
402 if (found == 0)
403 {
404 idPEL.erase(0, 2);
405 }
406 if (ends_with((*it).path(), idPEL))
407 {
408 std::vector<uint8_t> data = getFileData((*it).path());
409 if (!data.empty())
410 {
411 PEL pel{data};
412 if (pel.valid())
413 {
414 pel.toJSON();
415 }
416 else
417 {
418 log<level::ERR>(
419 "PEL File contains invalid PEL",
420 entry("FILENAME=%s", (*it).path().c_str()),
421 entry("ERROR=%s", "file contains invalid PEL"));
422 }
423 }
424 break;
425 }
426 }
427 catch (std::exception& e)
428 {
429 log<level::ERR>("Hit exception while reading PEL File",
430 entry("FILENAME=%s", (*it).path().c_str()),
431 entry("ERROR=%s", e.what()));
432 }
433 }
434 }
Aatir7b291ec2019-11-19 10:37:37 -0600435 else if (listPEL)
436 {
437
438 printList(listPELDescOrd, listPELShowHidden);
439 }
Aatir186ce8c2019-10-20 15:13:39 -0500440 else
441 {
442 exitWithError(app.help("", CLI::AppFormatMode::All),
443 "Raw PEL file path not specified.");
444 }
445 return 0;
446}