blob: 5c040caaa0d20af214dcf8b547e6c8547a20bcb0 [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"
Aatir186ce8c2019-10-20 15:13:39 -050019#include "../pel.hpp"
Aatir7b291ec2019-11-19 10:37:37 -060020#include "../pel_types.hpp"
21#include "../pel_values.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050022
23#include <CLI/CLI.hpp>
Aatir7b291ec2019-11-19 10:37:37 -060024#include <bitset>
Aatir186ce8c2019-10-20 15:13:39 -050025#include <iostream>
Aatir7b291ec2019-11-19 10:37:37 -060026#include <phosphor-logging/log.hpp>
27#include <regex>
Aatir186ce8c2019-10-20 15:13:39 -050028#include <string>
Aatir7b291ec2019-11-19 10:37:37 -060029#include <xyz/openbmc_project/Common/File/error.hpp>
Aatir186ce8c2019-10-20 15:13:39 -050030
Aatir7b291ec2019-11-19 10:37:37 -060031namespace fs = std::filesystem;
Aatir186ce8c2019-10-20 15:13:39 -050032using namespace phosphor::logging;
33using namespace openpower::pels;
Aatir7b291ec2019-11-19 10:37:37 -060034namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
35namespace message = openpower::pels::message;
36namespace pv = openpower::pels::pel_values;
Aatir186ce8c2019-10-20 15:13:39 -050037
Aatire340c132019-12-09 14:19:29 -060038/**
Aatir37822f62019-12-10 14:40:27 -060039 * @brief helper function to get PEL commit timestamp from file name
40 * @retrun BCDTime - PEL commit timestamp
41 * @param[in] std::string - file name
42 */
43BCDTime fileNameToTimestamp(const std::string& fileName)
44{
45 std::string token = fileName.substr(0, fileName.find("_"));
46 int i = 0;
47 BCDTime tmp;
48 if (token.length() >= 14)
49 {
50 try
51 {
52 tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
53 }
54 catch (std::exception& err)
55 {
56 std::cout << "Conversion failure: " << err.what() << std::endl;
57 }
58 i += 2;
59 try
60 {
61 tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
62 }
63 catch (std::exception& err)
64 {
65 std::cout << "Conversion failure: " << err.what() << std::endl;
66 }
67 i += 2;
68 try
69 {
70 tmp.month = std::stoi(token.substr(i, 2), 0, 16);
71 }
72 catch (std::exception& err)
73 {
74 std::cout << "Conversion failure: " << err.what() << std::endl;
75 }
76 i += 2;
77 try
78 {
79 tmp.day = std::stoi(token.substr(i, 2), 0, 16);
80 }
81 catch (std::exception& err)
82 {
83 std::cout << "Conversion failure: " << err.what() << std::endl;
84 }
85 i += 2;
86 try
87 {
88 tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
89 }
90 catch (std::exception& err)
91 {
92 std::cout << "Conversion failure: " << err.what() << std::endl;
93 }
94 i += 2;
95 try
96 {
97 tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
98 }
99 catch (std::exception& err)
100 {
101 std::cout << "Conversion failure: " << err.what() << std::endl;
102 }
103 i += 2;
104 try
105 {
106 tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
107 }
108 catch (std::exception& err)
109 {
110 std::cout << "Conversion failure: " << err.what() << std::endl;
111 }
112 i += 2;
113 try
114 {
115 tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
116 }
117 catch (std::exception& err)
118 {
119 std::cout << "Conversion failure: " << err.what() << std::endl;
120 }
121 }
122 return tmp;
123}
124
125/**
126 * @brief helper function to get PEL id from file name
127 * @retrun uint32_t - PEL id
128 * @param[in] std::string - file name
129 */
130uint32_t fileNameToPELId(const std::string& fileName)
131{
132 uint32_t num = 0;
133 try
134 {
135 num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
136 }
137 catch (std::exception& err)
138 {
139 std::cout << "Conversion failure: " << err.what() << std::endl;
140 }
141 return num;
142}
143
144/**
Aatire340c132019-12-09 14:19:29 -0600145 * @brief helper function to check string suffix
146 * @retrun bool - true with suffix matches
147 * @param[in] std::string - string to check for suffix
148 * @param[in] std::string - suffix string
149 */
150bool ends_with(const std::string& str, const std::string& end)
151{
152 size_t slen = str.size(), elen = end.size();
153 if (slen < elen)
154 return false;
155 while (elen)
156 {
157 if (str[--slen] != end[--elen])
158 return false;
159 }
160 return true;
161}
162
Aatir37822f62019-12-10 14:40:27 -0600163/**
164 * @brief get data form raw PEL file.
165 * @param[in] std::string Name of file with raw PEL
166 * @return std::vector<uint8_t> char vector read from raw PEL file.
167 */
Aatirbad5f8a2019-12-10 15:27:16 -0600168std::vector<uint8_t> getFileData(const std::string& name)
Aatir37822f62019-12-10 14:40:27 -0600169{
170 std::ifstream file(name, std::ifstream::in);
171 if (file.good())
172 {
173 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
174 std::istreambuf_iterator<char>()};
175 return data;
176 }
177 else
178 {
179 printf("Can't open raw PEL file");
180 return {};
181 }
182}
183
Aatirbad5f8a2019-12-10 15:27:16 -0600184/**
185 * @brief helper function to trim trailing whitespaces
186 * @return std::string - trimmed string
187 * @param[in] std::string - string to trim
188 */
189const char* ws = " \t\n\r\f\v";
190std::string trim(std::string s, const char* t = ws)
191{
192 if (s.find_last_not_of(t) != std::string::npos)
193 {
194 s.erase(s.find_last_not_of(t) + 1);
195 }
196 return s;
197}
198
Aatir7b291ec2019-11-19 10:37:37 -0600199template <typename T>
Aatir37822f62019-12-10 14:40:27 -0600200std::string genPELJSON(T itr, bool hidden)
Aatir7b291ec2019-11-19 10:37:37 -0600201{
202 std::size_t found;
203 std::string val;
204 char tmpValStr[50];
205 std::string listStr;
206 char name[50];
207 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
208 itr.second.yearLSB, itr.second.month, itr.second.day,
209 itr.second.hour, itr.second.minutes, itr.second.seconds,
210 itr.second.hundredths, itr.first);
211 std::string fileName(name);
212 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
213 try
214 {
Aatir37822f62019-12-10 14:40:27 -0600215 std::vector<uint8_t> data = getFileData(fileName);
Aatir37822f62019-12-10 14:40:27 -0600216 if (!data.empty())
217 {
Aatir37822f62019-12-10 14:40:27 -0600218 PEL pel{data};
219 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
220 if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
Aatir7b291ec2019-11-19 10:37:37 -0600221 {
Aatir37822f62019-12-10 14:40:27 -0600222 // id
223 sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
224 val = std::string(tmpValStr);
225 listStr += "\t\"" + val + "\": {\n";
226 // ASCII
227 val = pel.primarySRC() ? pel.primarySRC().value()->asciiString()
228 : "No SRC";
229 listStr += "\t\t\"SRC\": \"" + trim(val) + "\",\n";
230 // platformid
231 sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
232 val = std::string(tmpValStr);
233 listStr += "\t\t\"PLID\": \"" + val + "\",\n";
234 // creatorid
235 sprintf(tmpValStr, "%c", pel.privateHeader().creatorID());
236 std::string creatorID(tmpValStr);
237 val = pv::creatorIDs.count(creatorID)
238 ? pv::creatorIDs.at(creatorID)
239 : "Unknown Creator ID";
240 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
241 // subsytem
242 std::string subsystem = pv::getValue(
243 pel.userHeader().subsystem(), pel_values::subsystemValues);
244 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
245 // commit time
246 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
247 pel.privateHeader().commitTimestamp().month,
248 pel.privateHeader().commitTimestamp().day,
249 pel.privateHeader().commitTimestamp().yearMSB,
250 pel.privateHeader().commitTimestamp().yearLSB,
251 pel.privateHeader().commitTimestamp().hour,
252 pel.privateHeader().commitTimestamp().minutes,
253 pel.privateHeader().commitTimestamp().seconds);
254 val = std::string(tmpValStr);
255 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
256 // severity
257 std::string severity = pv::getValue(pel.userHeader().severity(),
258 pel_values::severityValues);
259 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
260 // compID
261 sprintf(tmpValStr, "0x%X",
262 pel.privateHeader().header().componentID);
263 val = std::string(tmpValStr);
264 listStr += "\t\t\"CompID\": \"" + val + "\",\n ";
265
266 found = listStr.rfind(",");
267 if (found != std::string::npos)
268 {
269 listStr.replace(found, 1, "");
270 listStr += "\t}, \n";
271 }
Aatir7b291ec2019-11-19 10:37:37 -0600272 }
273 }
Aatir37822f62019-12-10 14:40:27 -0600274 else
275 {
276 log<level::ERR>("Empty PEL file",
277 entry("FILENAME=%s", fileName.c_str()),
278 entry("ERROR=%s", "Empty PEL file"));
279 }
Aatir7b291ec2019-11-19 10:37:37 -0600280 }
281 catch (std::exception& e)
282 {
283 log<level::ERR>("Hit exception while reading PEL File",
284 entry("FILENAME=%s", fileName.c_str()),
285 entry("ERROR=%s", e.what()));
286 }
287 return listStr;
288}
289/**
290 * @brief Print a list of PELs
291 */
292void printList(bool order, bool hidden)
293{
294 std::string listStr;
295 std::map<uint32_t, BCDTime> PELs;
296 std::size_t found;
297 listStr = "{\n";
298 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
299 it != fs::directory_iterator(); ++it)
300 {
301 if (!fs::is_regular_file((*it).path()))
302 {
303 continue;
304 }
Aatir37822f62019-12-10 14:40:27 -0600305 else
Aatir7b291ec2019-11-19 10:37:37 -0600306 {
Aatir37822f62019-12-10 14:40:27 -0600307 PELs.emplace(fileNameToPELId((*it).path().filename()),
308 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600309 }
310 }
Aatir37822f62019-12-10 14:40:27 -0600311 auto buildJSON = [&listStr, &hidden](const auto& i) {
312 listStr += genPELJSON(i, hidden);
313 };
Aatir7b291ec2019-11-19 10:37:37 -0600314 if (order)
315 {
316 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
317 }
318 else
319 {
320 std::for_each(PELs.begin(), PELs.end(), buildJSON);
321 }
322
323 found = listStr.rfind(",");
324 if (found != std::string::npos)
325 {
326 listStr.replace(found, 1, "");
327 listStr += "\n}\n";
328 printf("%s", listStr.c_str());
329 }
330}
Aatir186ce8c2019-10-20 15:13:39 -0500331
332static void exitWithError(const std::string& help, const char* err)
333{
334 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
335 exit(-1);
336}
337
338int main(int argc, char** argv)
339{
340 CLI::App app{"OpenBMC PEL Tool"};
341 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600342 std::string idPEL;
343 bool listPEL = false;
344 bool listPELDescOrd = false;
345 bool listPELShowHidden = false;
346 app.add_option("-f,--file", fileName,
347 "Display a PEL using its Raw PEL file");
348 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Aatirbad5f8a2019-12-10 15:27:16 -0600349 app.add_flag("-l", listPEL, "List PELs");
Aatir7b291ec2019-11-19 10:37:37 -0600350 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
351 app.add_flag("-s", listPELShowHidden, "Show hidden PELs");
Aatir186ce8c2019-10-20 15:13:39 -0500352 CLI11_PARSE(app, argc, argv);
353
354 if (!fileName.empty())
355 {
356 std::vector<uint8_t> data = getFileData(fileName);
357 if (!data.empty())
358 {
359 PEL pel{data};
360 pel.toJSON();
361 }
362 else
363 {
364 exitWithError(app.help("", CLI::AppFormatMode::All),
365 "Raw PEL file can't be read.");
366 }
367 }
Aatir7b291ec2019-11-19 10:37:37 -0600368
Aatire340c132019-12-09 14:19:29 -0600369 else if (!idPEL.empty())
370 {
371 for (auto it =
372 fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
373 it != fs::directory_iterator(); ++it)
374 {
375 if (!fs::is_regular_file((*it).path()))
376 {
377 continue;
378 }
379 try
380 {
381 for (auto& c : idPEL)
382 c = toupper(c);
383 size_t found = idPEL.find("0X");
384 if (found == 0)
385 {
386 idPEL.erase(0, 2);
387 }
388 if (ends_with((*it).path(), idPEL))
389 {
390 std::vector<uint8_t> data = getFileData((*it).path());
391 if (!data.empty())
392 {
393 PEL pel{data};
394 if (pel.valid())
395 {
396 pel.toJSON();
397 }
398 else
399 {
400 log<level::ERR>(
401 "PEL File contains invalid PEL",
402 entry("FILENAME=%s", (*it).path().c_str()),
403 entry("ERROR=%s", "file contains invalid PEL"));
404 }
405 }
406 break;
407 }
408 }
409 catch (std::exception& e)
410 {
411 log<level::ERR>("Hit exception while reading PEL File",
412 entry("FILENAME=%s", (*it).path().c_str()),
413 entry("ERROR=%s", e.what()));
414 }
415 }
416 }
Aatir7b291ec2019-11-19 10:37:37 -0600417 else if (listPEL)
418 {
419
420 printList(listPELDescOrd, listPELShowHidden);
421 }
Aatir186ce8c2019-10-20 15:13:39 -0500422 else
423 {
424 exitWithError(app.help("", CLI::AppFormatMode::All),
425 "Raw PEL file path not specified.");
426 }
427 return 0;
428}