PEL: Print list of PELs

PelTool commands for printing a list of PELs.

PEL list sample:

{
	"0x50000004": {
		"SRC": "BD8D1001",
		"PLID": "0x50000004",
		"CreatorID": "BMC",
		"Subsystem": "bmc_firmware",
		"Commit Time": "10/24/2019  15:50:08",
		"Sev": "unrecoverable",
 		"CompID": "0x1000"
 	}

}

Change-Id: Ifd864a6561c09de098689195edcf107b3fe550e3
Signed-off-by: Aatir <aatrapps@gmail.com>
diff --git a/extensions/openpower-pels/pel_values.cpp b/extensions/openpower-pels/pel_values.cpp
index 775c0f5..8f5c1cd 100644
--- a/extensions/openpower-pels/pel_values.cpp
+++ b/extensions/openpower-pels/pel_values.cpp
@@ -239,6 +239,19 @@
     {"S", "SLIC"},     {"B", "Hostboot"}, {"T", "OCC"},  {"M", "I/O Drawer"},
     {"K", "Sapphire"}, {"P", "PowerNV"}};
 
+std::string getValue(const uint8_t field, const pel_values::PELValues& values)
+{
+
+    auto tmp = pel_values::findByValue(field, values);
+    if (tmp != values.end())
+    {
+        return std::get<pel_values::registryNamePos>(*tmp);
+    }
+    else
+    {
+        return "invalid";
+    }
+}
 } // namespace pel_values
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/pel_values.hpp b/extensions/openpower-pels/pel_values.hpp
index 9d49dbe..8d86acf 100644
--- a/extensions/openpower-pels/pel_values.hpp
+++ b/extensions/openpower-pels/pel_values.hpp
@@ -25,6 +25,14 @@
 using PELValues = std::vector<PELFieldValue>;
 
 /**
+ * @brief Helper function to get values from lookup tables.
+ * @return std::string - the value
+ * @param[in] uint8_t - field to get value for
+ * @param[in] PELValues - lookup table
+ */
+std::string getValue(const uint8_t field, const pel_values::PELValues& values);
+
+/**
  * @brief Find the desired entry in a PELValues table based on the
  *        field value.
  *
diff --git a/extensions/openpower-pels/tools/peltool.cpp b/extensions/openpower-pels/tools/peltool.cpp
index 1208f24..041449e 100644
--- a/extensions/openpower-pels/tools/peltool.cpp
+++ b/extensions/openpower-pels/tools/peltool.cpp
@@ -13,15 +13,185 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "config.h"
+
+#include "../bcd_time.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;
 
+std::string ltrim(const std::string& s)
+{
+    return std::regex_replace(s, std::regex("^\\s+"), std::string(""));
+}
+
+std::string rtrim(const std::string& s)
+{
+    return std::regex_replace(s, std::regex("\\s+$"), std::string(""));
+}
+
+std::string trim(const std::string& s)
+{
+    return ltrim(rtrim(s));
+}
+
+template <typename T>
+std::string genPELJSON(T itr)
+{
+    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::ifstream stream(fileName, std::ios::in | std::ios::binary);
+        std::vector<uint8_t> data((std::istreambuf_iterator<char>(stream)),
+                                  std::istreambuf_iterator<char>());
+        stream.close();
+        PEL pel{data};
+        if (pel.valid())
+        {
+            // id
+            sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
+            val = std::string(tmpValStr);
+            listStr += "\t\"" + val + "\": {\n";
+            // ASCII
+            val = pel.primarySRC() ? pel.primarySRC().value()->asciiString()
+                                   : "No SRC";
+            listStr += "\t\t\"SRC\": \"" + trim(val) + "\",\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";
+            }
+        }
+    }
+    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;
+        }
+        try
+        {
+            std::ifstream stream((*it).path(), std::ios::in | std::ios::binary);
+            std::vector<uint8_t> data((std::istreambuf_iterator<char>(stream)),
+                                      std::istreambuf_iterator<char>());
+            stream.close();
+            PEL pel{data};
+            if (pel.valid())
+            {
+
+                std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
+                if (hidden || !actionFlags.test(hiddenFlagBit))
+                {
+                    PELs.emplace(pel.id(),
+                                 pel.privateHeader().commitTimestamp());
+                }
+            }
+        }
+        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()));
+        }
+    }
+    std::string val;
+    auto buildJSON = [&listStr](const auto& i) { listStr += genPELJSON(i); };
+    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());
+    }
+}
 /**
  * @brief get data form raw PEL file.
  * @param[in] std::string Name of file with raw PEL
@@ -53,7 +223,15 @@
 {
     CLI::App app{"OpenBMC PEL Tool"};
     std::string fileName;
-    app.add_option("-f,--file", fileName, "Raw PEL File");
+    bool listPEL;
+    bool listPELDescOrd;
+    bool listPELShowHidden;
+    listPELDescOrd = false;
+    listPELShowHidden = false;
+    app.add_option("-f,--file", fileName, "Raw PEL file");
+    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())
@@ -70,6 +248,12 @@
                           "Raw PEL file can't be read.");
         }
     }
+
+    else if (listPEL)
+    {
+
+        printList(listPELDescOrd, listPELShowHidden);
+    }
     else
     {
         exitWithError(app.help("", CLI::AppFormatMode::All),
diff --git a/extensions/openpower-pels/user_header.hpp b/extensions/openpower-pels/user_header.hpp
index 5689a1c..6692dd3 100644
--- a/extensions/openpower-pels/user_header.hpp
+++ b/extensions/openpower-pels/user_header.hpp
@@ -173,6 +173,15 @@
      */
     std::optional<std::string> getJSON() const override;
 
+    /**
+     * @brief Helper function to get values from lookup tables.
+     * @return std::string - the value
+     * @param[in] uint8_t - field to get value for
+     * @param[in] PELValues - lookup table
+     */
+    std::string getValue(const uint8_t field,
+                         const pel_values::PELValues& values) const;
+
   private:
     /**
      * @brief Fills in the object from the stream data
@@ -232,15 +241,6 @@
      * @brief The second reserved word placeholder.
      */
     uint32_t _reserved4Byte2;
-
-    /**
-     * @brief Helper function to get values from lookup tables.
-     * @return std::string - the value
-     * @param[in] uint8_t - field to get value for
-     * @param[in] PELValues - lookup table
-     */
-    std::string getValue(const uint8_t field,
-                         const pel_values::PELValues& values) const;
 };
 
 } // namespace pels