blob: ed6daa255128e7425d209e739010288c26d2fdf4 [file] [log] [blame]
/**
* Copyright © 2019 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
#include "../bcd_time.hpp"
#include "../paths.hpp"
#include "../pel.hpp"
#include "../pel_types.hpp"
#include "../pel_values.hpp"
#include <CLI/CLI.hpp>
#include <bitset>
#include <iostream>
#include <phosphor-logging/log.hpp>
#include <regex>
#include <string>
#include <xyz/openbmc_project/Common/File/error.hpp>
namespace fs = std::filesystem;
using namespace phosphor::logging;
using namespace openpower::pels;
namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
namespace message = openpower::pels::message;
namespace pv = openpower::pels::pel_values;
/**
* @brief helper function to get PEL commit timestamp from file name
* @retrun BCDTime - PEL commit timestamp
* @param[in] std::string - file name
*/
BCDTime fileNameToTimestamp(const std::string& fileName)
{
std::string token = fileName.substr(0, fileName.find("_"));
int i = 0;
BCDTime tmp;
if (token.length() >= 14)
{
try
{
tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
i += 2;
try
{
tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
i += 2;
try
{
tmp.month = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
i += 2;
try
{
tmp.day = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
i += 2;
try
{
tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
i += 2;
try
{
tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
i += 2;
try
{
tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
i += 2;
try
{
tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
}
return tmp;
}
/**
* @brief helper function to get PEL id from file name
* @retrun uint32_t - PEL id
* @param[in] std::string - file name
*/
uint32_t fileNameToPELId(const std::string& fileName)
{
uint32_t num = 0;
try
{
num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
}
catch (std::exception& err)
{
std::cout << "Conversion failure: " << err.what() << std::endl;
}
return num;
}
/**
* @brief helper function to check string suffix
* @retrun bool - true with suffix matches
* @param[in] std::string - string to check for suffix
* @param[in] std::string - suffix string
*/
bool ends_with(const std::string& str, const std::string& end)
{
size_t slen = str.size(), elen = end.size();
if (slen < elen)
return false;
while (elen)
{
if (str[--slen] != end[--elen])
return false;
}
return true;
}
/**
* @brief get data form raw PEL file.
* @param[in] std::string Name of file with raw PEL
* @return std::vector<uint8_t> char vector read from raw PEL file.
*/
std::vector<uint8_t> getFileData(const std::string& name)
{
std::ifstream file(name, std::ifstream::in);
if (file.good())
{
std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>()};
return data;
}
else
{
printf("Can't open raw PEL file");
return {};
}
}
template <typename T>
std::string genPELJSON(T itr, bool hidden, message::Registry& registry)
{
std::size_t found;
std::string val;
char tmpValStr[50];
std::string listStr;
char name[50];
sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
itr.second.yearLSB, itr.second.month, itr.second.day,
itr.second.hour, itr.second.minutes, itr.second.seconds,
itr.second.hundredths, itr.first);
std::string fileName(name);
fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
try
{
std::vector<uint8_t> data = getFileData(fileName);
if (!data.empty())
{
PEL pel{data};
std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
{
// id
sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
val = std::string(tmpValStr);
listStr += "\t\"" + val + "\": {\n";
// ASCII
if (pel.primarySRC())
{
val = pel.primarySRC().value()->asciiString();
listStr += "\t\t\"SRC\": \"" +
val.substr(0, val.find(0x20)) + "\",\n";
// Registry message
auto regVal = pel.primarySRC().value()->getErrorDetails(
registry, DetailLevel::message, true);
if (regVal)
{
val = regVal.value();
listStr += "\t\t\"Message\": \"" + val + "\",\n";
}
}
else
{
listStr += "\t\t\"SRC\": \"No SRC\",\n";
}
// platformid
sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
val = std::string(tmpValStr);
listStr += "\t\t\"PLID\": \"" + val + "\",\n";
// creatorid
sprintf(tmpValStr, "%c", pel.privateHeader().creatorID());
std::string creatorID(tmpValStr);
val = pv::creatorIDs.count(creatorID)
? pv::creatorIDs.at(creatorID)
: "Unknown Creator ID";
listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
// subsytem
std::string subsystem = pv::getValue(
pel.userHeader().subsystem(), pel_values::subsystemValues);
listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
// commit time
sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
pel.privateHeader().commitTimestamp().month,
pel.privateHeader().commitTimestamp().day,
pel.privateHeader().commitTimestamp().yearMSB,
pel.privateHeader().commitTimestamp().yearLSB,
pel.privateHeader().commitTimestamp().hour,
pel.privateHeader().commitTimestamp().minutes,
pel.privateHeader().commitTimestamp().seconds);
val = std::string(tmpValStr);
listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
// severity
std::string severity = pv::getValue(pel.userHeader().severity(),
pel_values::severityValues);
listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
// compID
sprintf(tmpValStr, "0x%X",
pel.privateHeader().header().componentID);
val = std::string(tmpValStr);
listStr += "\t\t\"CompID\": \"" + val + "\",\n ";
found = listStr.rfind(",");
if (found != std::string::npos)
{
listStr.replace(found, 1, "");
listStr += "\t}, \n";
}
}
}
else
{
log<level::ERR>("Empty PEL file",
entry("FILENAME=%s", fileName.c_str()),
entry("ERROR=%s", "Empty PEL file"));
}
}
catch (std::exception& e)
{
log<level::ERR>("Hit exception while reading PEL File",
entry("FILENAME=%s", fileName.c_str()),
entry("ERROR=%s", e.what()));
}
return listStr;
}
/**
* @brief Print a list of PELs
*/
void printList(bool order, bool hidden)
{
std::string listStr;
std::map<uint32_t, BCDTime> PELs;
std::size_t found;
listStr = "{\n";
for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
it != fs::directory_iterator(); ++it)
{
if (!fs::is_regular_file((*it).path()))
{
continue;
}
else
{
PELs.emplace(fileNameToPELId((*it).path().filename()),
fileNameToTimestamp((*it).path().filename()));
}
}
message::Registry registry(getMessageRegistryPath() /
message::registryFileName);
auto buildJSON = [&listStr, &hidden, &registry](const auto& i) {
listStr += genPELJSON(i, hidden, registry);
};
if (order)
{
std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
}
else
{
std::for_each(PELs.begin(), PELs.end(), buildJSON);
}
found = listStr.rfind(",");
if (found != std::string::npos)
{
listStr.replace(found, 1, "");
listStr += "\n}\n";
printf("%s", listStr.c_str());
}
}
static void exitWithError(const std::string& help, const char* err)
{
std::cerr << "ERROR: " << err << std::endl << help << std::endl;
exit(-1);
}
int main(int argc, char** argv)
{
CLI::App app{"OpenBMC PEL Tool"};
std::string fileName;
std::string idPEL;
bool listPEL = false;
bool listPELDescOrd = false;
bool listPELShowHidden = false;
app.add_option("-f,--file", fileName,
"Display a PEL using its Raw PEL file");
app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
app.add_flag("-l", listPEL, "List PELs");
app.add_flag("-r", listPELDescOrd, "Reverse order of output");
app.add_flag("-s", listPELShowHidden, "Show hidden PELs");
CLI11_PARSE(app, argc, argv);
if (!fileName.empty())
{
std::vector<uint8_t> data = getFileData(fileName);
if (!data.empty())
{
PEL pel{data};
pel.toJSON();
}
else
{
exitWithError(app.help("", CLI::AppFormatMode::All),
"Raw PEL file can't be read.");
}
}
else if (!idPEL.empty())
{
for (auto it =
fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
it != fs::directory_iterator(); ++it)
{
if (!fs::is_regular_file((*it).path()))
{
continue;
}
try
{
for (auto& c : idPEL)
c = toupper(c);
size_t found = idPEL.find("0X");
if (found == 0)
{
idPEL.erase(0, 2);
}
if (ends_with((*it).path(), idPEL))
{
std::vector<uint8_t> data = getFileData((*it).path());
if (!data.empty())
{
PEL pel{data};
if (pel.valid())
{
pel.toJSON();
}
else
{
log<level::ERR>(
"PEL File contains invalid PEL",
entry("FILENAME=%s", (*it).path().c_str()),
entry("ERROR=%s", "file contains invalid PEL"));
}
}
break;
}
}
catch (std::exception& e)
{
log<level::ERR>("Hit exception while reading PEL File",
entry("FILENAME=%s", (*it).path().c_str()),
entry("ERROR=%s", e.what()));
}
}
}
else if (listPEL)
{
printList(listPELDescOrd, listPELShowHidden);
}
else
{
exitWithError(app.help("", CLI::AppFormatMode::All),
"Raw PEL file path not specified.");
}
return 0;
}