blob: 10646de6d12654fb0869d29d69b827b38045123e [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
Aatir7b291ec2019-11-19 10:37:37 -060038std::string ltrim(const std::string& s)
39{
40 return std::regex_replace(s, std::regex("^\\s+"), std::string(""));
41}
42
43std::string rtrim(const std::string& s)
44{
45 return std::regex_replace(s, std::regex("\\s+$"), std::string(""));
46}
47
48std::string trim(const std::string& s)
49{
50 return ltrim(rtrim(s));
51}
52
Aatire340c132019-12-09 14:19:29 -060053/**
Aatir37822f62019-12-10 14:40:27 -060054 * @brief helper function to get PEL commit timestamp from file name
55 * @retrun BCDTime - PEL commit timestamp
56 * @param[in] std::string - file name
57 */
58BCDTime fileNameToTimestamp(const std::string& fileName)
59{
60 std::string token = fileName.substr(0, fileName.find("_"));
61 int i = 0;
62 BCDTime tmp;
63 if (token.length() >= 14)
64 {
65 try
66 {
67 tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
68 }
69 catch (std::exception& err)
70 {
71 std::cout << "Conversion failure: " << err.what() << std::endl;
72 }
73 i += 2;
74 try
75 {
76 tmp.yearLSB = 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.month = 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.day = 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.hour = 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.minutes = 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.seconds = 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.hundredths = 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 }
137 return tmp;
138}
139
140/**
141 * @brief helper function to get PEL id from file name
142 * @retrun uint32_t - PEL id
143 * @param[in] std::string - file name
144 */
145uint32_t fileNameToPELId(const std::string& fileName)
146{
147 uint32_t num = 0;
148 try
149 {
150 num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
151 }
152 catch (std::exception& err)
153 {
154 std::cout << "Conversion failure: " << err.what() << std::endl;
155 }
156 return num;
157}
158
159/**
Aatire340c132019-12-09 14:19:29 -0600160 * @brief helper function to check string suffix
161 * @retrun bool - true with suffix matches
162 * @param[in] std::string - string to check for suffix
163 * @param[in] std::string - suffix string
164 */
165bool ends_with(const std::string& str, const std::string& end)
166{
167 size_t slen = str.size(), elen = end.size();
168 if (slen < elen)
169 return false;
170 while (elen)
171 {
172 if (str[--slen] != end[--elen])
173 return false;
174 }
175 return true;
176}
177
Aatir37822f62019-12-10 14:40:27 -0600178/**
179 * @brief get data form raw PEL file.
180 * @param[in] std::string Name of file with raw PEL
181 * @return std::vector<uint8_t> char vector read from raw PEL file.
182 */
183std::vector<uint8_t> getFileData(std::string name)
184{
185 std::ifstream file(name, std::ifstream::in);
186 if (file.good())
187 {
188 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
189 std::istreambuf_iterator<char>()};
190 return data;
191 }
192 else
193 {
194 printf("Can't open raw PEL file");
195 return {};
196 }
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);
Aatir7b291ec2019-11-19 10:37:37 -0600216
Aatir37822f62019-12-10 14:40:27 -0600217 if (!data.empty())
218 {
219
220 PEL pel{data};
221 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
222 if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
Aatir7b291ec2019-11-19 10:37:37 -0600223 {
Aatir37822f62019-12-10 14:40:27 -0600224 // id
225 sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
226 val = std::string(tmpValStr);
227 listStr += "\t\"" + val + "\": {\n";
228 // ASCII
229 val = pel.primarySRC() ? pel.primarySRC().value()->asciiString()
230 : "No SRC";
231 listStr += "\t\t\"SRC\": \"" + trim(val) + "\",\n";
232 // platformid
233 sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
234 val = std::string(tmpValStr);
235 listStr += "\t\t\"PLID\": \"" + val + "\",\n";
236 // creatorid
237 sprintf(tmpValStr, "%c", pel.privateHeader().creatorID());
238 std::string creatorID(tmpValStr);
239 val = pv::creatorIDs.count(creatorID)
240 ? pv::creatorIDs.at(creatorID)
241 : "Unknown Creator ID";
242 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
243 // subsytem
244 std::string subsystem = pv::getValue(
245 pel.userHeader().subsystem(), pel_values::subsystemValues);
246 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
247 // commit time
248 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
249 pel.privateHeader().commitTimestamp().month,
250 pel.privateHeader().commitTimestamp().day,
251 pel.privateHeader().commitTimestamp().yearMSB,
252 pel.privateHeader().commitTimestamp().yearLSB,
253 pel.privateHeader().commitTimestamp().hour,
254 pel.privateHeader().commitTimestamp().minutes,
255 pel.privateHeader().commitTimestamp().seconds);
256 val = std::string(tmpValStr);
257 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
258 // severity
259 std::string severity = pv::getValue(pel.userHeader().severity(),
260 pel_values::severityValues);
261 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
262 // compID
263 sprintf(tmpValStr, "0x%X",
264 pel.privateHeader().header().componentID);
265 val = std::string(tmpValStr);
266 listStr += "\t\t\"CompID\": \"" + val + "\",\n ";
267
268 found = listStr.rfind(",");
269 if (found != std::string::npos)
270 {
271 listStr.replace(found, 1, "");
272 listStr += "\t}, \n";
273 }
Aatir7b291ec2019-11-19 10:37:37 -0600274 }
275 }
Aatir37822f62019-12-10 14:40:27 -0600276 else
277 {
278 log<level::ERR>("Empty PEL file",
279 entry("FILENAME=%s", fileName.c_str()),
280 entry("ERROR=%s", "Empty PEL file"));
281 }
Aatir7b291ec2019-11-19 10:37:37 -0600282 }
283 catch (std::exception& e)
284 {
285 log<level::ERR>("Hit exception while reading PEL File",
286 entry("FILENAME=%s", fileName.c_str()),
287 entry("ERROR=%s", e.what()));
288 }
289 return listStr;
290}
291/**
292 * @brief Print a list of PELs
293 */
294void printList(bool order, bool hidden)
295{
296 std::string listStr;
297 std::map<uint32_t, BCDTime> PELs;
298 std::size_t found;
299 listStr = "{\n";
300 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
301 it != fs::directory_iterator(); ++it)
302 {
303 if (!fs::is_regular_file((*it).path()))
304 {
305 continue;
306 }
Aatir37822f62019-12-10 14:40:27 -0600307 else
Aatir7b291ec2019-11-19 10:37:37 -0600308 {
Aatir37822f62019-12-10 14:40:27 -0600309 PELs.emplace(fileNameToPELId((*it).path().filename()),
310 fileNameToTimestamp((*it).path().filename()));
Aatir7b291ec2019-11-19 10:37:37 -0600311 }
312 }
313 std::string val;
Aatir37822f62019-12-10 14:40:27 -0600314 auto buildJSON = [&listStr, &hidden](const auto& i) {
315 listStr += genPELJSON(i, hidden);
316 };
Aatir7b291ec2019-11-19 10:37:37 -0600317 if (order)
318 {
319 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
320 }
321 else
322 {
323 std::for_each(PELs.begin(), PELs.end(), buildJSON);
324 }
325
326 found = listStr.rfind(",");
327 if (found != std::string::npos)
328 {
329 listStr.replace(found, 1, "");
330 listStr += "\n}\n";
331 printf("%s", listStr.c_str());
332 }
333}
Aatir186ce8c2019-10-20 15:13:39 -0500334
335static void exitWithError(const std::string& help, const char* err)
336{
337 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
338 exit(-1);
339}
340
341int main(int argc, char** argv)
342{
343 CLI::App app{"OpenBMC PEL Tool"};
344 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600345 std::string idPEL;
346 bool listPEL = false;
347 bool listPELDescOrd = false;
348 bool listPELShowHidden = false;
349 app.add_option("-f,--file", fileName,
350 "Display a PEL using its Raw PEL file");
351 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Aatir7b291ec2019-11-19 10:37:37 -0600352 app.add_flag("-l", listPEL, "List PELS");
353 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
354 app.add_flag("-s", listPELShowHidden, "Show hidden PELs");
Aatir186ce8c2019-10-20 15:13:39 -0500355 CLI11_PARSE(app, argc, argv);
356
357 if (!fileName.empty())
358 {
359 std::vector<uint8_t> data = getFileData(fileName);
360 if (!data.empty())
361 {
362 PEL pel{data};
363 pel.toJSON();
364 }
365 else
366 {
367 exitWithError(app.help("", CLI::AppFormatMode::All),
368 "Raw PEL file can't be read.");
369 }
370 }
Aatir7b291ec2019-11-19 10:37:37 -0600371
Aatire340c132019-12-09 14:19:29 -0600372 else if (!idPEL.empty())
373 {
374 for (auto it =
375 fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
376 it != fs::directory_iterator(); ++it)
377 {
378 if (!fs::is_regular_file((*it).path()))
379 {
380 continue;
381 }
382 try
383 {
384 for (auto& c : idPEL)
385 c = toupper(c);
386 size_t found = idPEL.find("0X");
387 if (found == 0)
388 {
389 idPEL.erase(0, 2);
390 }
391 if (ends_with((*it).path(), idPEL))
392 {
393 std::vector<uint8_t> data = getFileData((*it).path());
394 if (!data.empty())
395 {
396 PEL pel{data};
397 if (pel.valid())
398 {
399 pel.toJSON();
400 }
401 else
402 {
403 log<level::ERR>(
404 "PEL File contains invalid PEL",
405 entry("FILENAME=%s", (*it).path().c_str()),
406 entry("ERROR=%s", "file contains invalid PEL"));
407 }
408 }
409 break;
410 }
411 }
412 catch (std::exception& e)
413 {
414 log<level::ERR>("Hit exception while reading PEL File",
415 entry("FILENAME=%s", (*it).path().c_str()),
416 entry("ERROR=%s", e.what()));
417 }
418 }
419 }
Aatir7b291ec2019-11-19 10:37:37 -0600420 else if (listPEL)
421 {
422
423 printList(listPELDescOrd, listPELShowHidden);
424 }
Aatir186ce8c2019-10-20 15:13:39 -0500425 else
426 {
427 exitWithError(app.help("", CLI::AppFormatMode::All),
428 "Raw PEL file path not specified.");
429 }
430 return 0;
431}