blob: 959603a5a3cc7c35ff6bdfca2f0d3ff7c86ba93b [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/**
54 * @brief helper function to check string suffix
55 * @retrun bool - true with suffix matches
56 * @param[in] std::string - string to check for suffix
57 * @param[in] std::string - suffix string
58 */
59bool ends_with(const std::string& str, const std::string& end)
60{
61 size_t slen = str.size(), elen = end.size();
62 if (slen < elen)
63 return false;
64 while (elen)
65 {
66 if (str[--slen] != end[--elen])
67 return false;
68 }
69 return true;
70}
71
Aatir7b291ec2019-11-19 10:37:37 -060072template <typename T>
73std::string genPELJSON(T itr)
74{
75 std::size_t found;
76 std::string val;
77 char tmpValStr[50];
78 std::string listStr;
79 char name[50];
80 sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
81 itr.second.yearLSB, itr.second.month, itr.second.day,
82 itr.second.hour, itr.second.minutes, itr.second.seconds,
83 itr.second.hundredths, itr.first);
84 std::string fileName(name);
85 fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
86 try
87 {
88 std::ifstream stream(fileName, std::ios::in | std::ios::binary);
89 std::vector<uint8_t> data((std::istreambuf_iterator<char>(stream)),
90 std::istreambuf_iterator<char>());
91 stream.close();
92 PEL pel{data};
93 if (pel.valid())
94 {
95 // id
96 sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
97 val = std::string(tmpValStr);
98 listStr += "\t\"" + val + "\": {\n";
99 // ASCII
100 val = pel.primarySRC() ? pel.primarySRC().value()->asciiString()
101 : "No SRC";
102 listStr += "\t\t\"SRC\": \"" + trim(val) + "\",\n";
103 // platformid
104 sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
105 val = std::string(tmpValStr);
106 listStr += "\t\t\"PLID\": \"" + val + "\",\n";
107 // creatorid
108 sprintf(tmpValStr, "%c", pel.privateHeader().creatorID());
109 std::string creatorID(tmpValStr);
110 val = pv::creatorIDs.count(creatorID) ? pv::creatorIDs.at(creatorID)
111 : "Unknown Creator ID";
112 listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
113 // subsytem
114 std::string subsystem = pv::getValue(pel.userHeader().subsystem(),
115 pel_values::subsystemValues);
116 listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
117 // commit time
118 sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
119 pel.privateHeader().commitTimestamp().month,
120 pel.privateHeader().commitTimestamp().day,
121 pel.privateHeader().commitTimestamp().yearMSB,
122 pel.privateHeader().commitTimestamp().yearLSB,
123 pel.privateHeader().commitTimestamp().hour,
124 pel.privateHeader().commitTimestamp().minutes,
125 pel.privateHeader().commitTimestamp().seconds);
126 val = std::string(tmpValStr);
127 listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
128 // severity
129 std::string severity = pv::getValue(pel.userHeader().severity(),
130 pel_values::severityValues);
131 listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
132 // compID
133 sprintf(tmpValStr, "0x%X",
134 pel.privateHeader().header().componentID);
135 val = std::string(tmpValStr);
136 listStr += "\t\t\"CompID\": \"" + val + "\",\n ";
137
138 found = listStr.rfind(",");
139 if (found != std::string::npos)
140 {
141 listStr.replace(found, 1, "");
142 listStr += "\t}, \n";
143 }
144 }
145 }
146 catch (std::exception& e)
147 {
148 log<level::ERR>("Hit exception while reading PEL File",
149 entry("FILENAME=%s", fileName.c_str()),
150 entry("ERROR=%s", e.what()));
151 }
152 return listStr;
153}
154/**
155 * @brief Print a list of PELs
156 */
157void printList(bool order, bool hidden)
158{
159 std::string listStr;
160 std::map<uint32_t, BCDTime> PELs;
161 std::size_t found;
162 listStr = "{\n";
163 for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
164 it != fs::directory_iterator(); ++it)
165 {
166 if (!fs::is_regular_file((*it).path()))
167 {
168 continue;
169 }
170 try
171 {
172 std::ifstream stream((*it).path(), std::ios::in | std::ios::binary);
173 std::vector<uint8_t> data((std::istreambuf_iterator<char>(stream)),
174 std::istreambuf_iterator<char>());
175 stream.close();
176 PEL pel{data};
177 if (pel.valid())
178 {
179
180 std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
181 if (hidden || !actionFlags.test(hiddenFlagBit))
182 {
183 PELs.emplace(pel.id(),
184 pel.privateHeader().commitTimestamp());
185 }
186 }
187 }
188 catch (std::exception& e)
189 {
190 log<level::ERR>("Hit exception while reading PEL File",
191 entry("FILENAME=%s", (*it).path().c_str()),
192 entry("ERROR=%s", e.what()));
193 }
194 }
195 std::string val;
196 auto buildJSON = [&listStr](const auto& i) { listStr += genPELJSON(i); };
197 if (order)
198 {
199 std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
200 }
201 else
202 {
203 std::for_each(PELs.begin(), PELs.end(), buildJSON);
204 }
205
206 found = listStr.rfind(",");
207 if (found != std::string::npos)
208 {
209 listStr.replace(found, 1, "");
210 listStr += "\n}\n";
211 printf("%s", listStr.c_str());
212 }
213}
Aatir186ce8c2019-10-20 15:13:39 -0500214/**
215 * @brief get data form raw PEL file.
216 * @param[in] std::string Name of file with raw PEL
217 * @return std::vector<uint8_t> char vector read from raw PEL file.
218 */
219std::vector<uint8_t> getFileData(std::string name)
220{
221 std::ifstream file(name, std::ifstream::in);
222 if (file.good())
223 {
224 std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
225 std::istreambuf_iterator<char>()};
226 return data;
227 }
228 else
229 {
230 printf("Can't open raw PEL file");
231 return {};
232 }
233}
234
235static void exitWithError(const std::string& help, const char* err)
236{
237 std::cerr << "ERROR: " << err << std::endl << help << std::endl;
238 exit(-1);
239}
240
241int main(int argc, char** argv)
242{
243 CLI::App app{"OpenBMC PEL Tool"};
244 std::string fileName;
Aatire340c132019-12-09 14:19:29 -0600245 std::string idPEL;
246 bool listPEL = false;
247 bool listPELDescOrd = false;
248 bool listPELShowHidden = false;
249 app.add_option("-f,--file", fileName,
250 "Display a PEL using its Raw PEL file");
251 app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
Aatir7b291ec2019-11-19 10:37:37 -0600252 app.add_flag("-l", listPEL, "List PELS");
253 app.add_flag("-r", listPELDescOrd, "Reverse order of output");
254 app.add_flag("-s", listPELShowHidden, "Show hidden PELs");
Aatir186ce8c2019-10-20 15:13:39 -0500255 CLI11_PARSE(app, argc, argv);
256
257 if (!fileName.empty())
258 {
259 std::vector<uint8_t> data = getFileData(fileName);
260 if (!data.empty())
261 {
262 PEL pel{data};
263 pel.toJSON();
264 }
265 else
266 {
267 exitWithError(app.help("", CLI::AppFormatMode::All),
268 "Raw PEL file can't be read.");
269 }
270 }
Aatir7b291ec2019-11-19 10:37:37 -0600271
Aatire340c132019-12-09 14:19:29 -0600272 else if (!idPEL.empty())
273 {
274 for (auto it =
275 fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
276 it != fs::directory_iterator(); ++it)
277 {
278 if (!fs::is_regular_file((*it).path()))
279 {
280 continue;
281 }
282 try
283 {
284 for (auto& c : idPEL)
285 c = toupper(c);
286 size_t found = idPEL.find("0X");
287 if (found == 0)
288 {
289 idPEL.erase(0, 2);
290 }
291 if (ends_with((*it).path(), idPEL))
292 {
293 std::vector<uint8_t> data = getFileData((*it).path());
294 if (!data.empty())
295 {
296 PEL pel{data};
297 if (pel.valid())
298 {
299 pel.toJSON();
300 }
301 else
302 {
303 log<level::ERR>(
304 "PEL File contains invalid PEL",
305 entry("FILENAME=%s", (*it).path().c_str()),
306 entry("ERROR=%s", "file contains invalid PEL"));
307 }
308 }
309 break;
310 }
311 }
312 catch (std::exception& e)
313 {
314 log<level::ERR>("Hit exception while reading PEL File",
315 entry("FILENAME=%s", (*it).path().c_str()),
316 entry("ERROR=%s", e.what()));
317 }
318 }
319 }
Aatir7b291ec2019-11-19 10:37:37 -0600320 else if (listPEL)
321 {
322
323 printList(listPELDescOrd, listPELShowHidden);
324 }
Aatir186ce8c2019-10-20 15:13:39 -0500325 else
326 {
327 exitWithError(app.help("", CLI::AppFormatMode::All),
328 "Raw PEL file path not specified.");
329 }
330 return 0;
331}