Get the initial IPMI SEL Record ID from syslog

Since the journal will not be persisted, IPMI SEL entries will be
stored in syslog instead.  This changes the SEL Logger to get the
initial IPMI SEL Record ID from the persisten syslog instead of
the journal.

Tested:
   1. Added an IPMI SEL entry by overriding a sensor.
   2. Rebooted
   3. Added an IPMI SEL entry by overriding a sensor.
   4. Confirmed that the new SEL entry got the next Record ID and
      did not restart at 1.

Change-Id: I85c2ebee66bb6b8d29c734d6bd05458a311845df
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e843eca..5fc1ae2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,7 @@
 
 target_include_directories (sel-logger PRIVATE ${CMAKE_SOURCE_DIR})
 
-target_link_libraries (sel-logger systemd sdbusplus pthread phosphor_logging)
+target_link_libraries (sel-logger systemd sdbusplus pthread phosphor_logging -lstdc++fs)
 
 include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
 
diff --git a/include/sel_logger.hpp b/include/sel_logger.hpp
index 28214e3..bd40dff 100644
--- a/include/sel_logger.hpp
+++ b/include/sel_logger.hpp
@@ -15,6 +15,7 @@
 */
 
 #pragma once
+#include <filesystem>
 
 static constexpr char const *ipmiSelObject = "xyz.openbmc_project.Logging.IPMI";
 static constexpr char const *ipmiSelPath = "/xyz/openbmc_project/Logging/IPMI";
@@ -32,6 +33,9 @@
 static constexpr size_t selEvtDataMaxSize = 3;
 static constexpr size_t selOemDataMaxSize = 13;
 
+static const std::filesystem::path selLogDir = "/var/log";
+static const std::string selLogFilename = "ipmi_sel";
+
 template <typename... T>
 static uint16_t
     selAddSystemRecord(const std::string &message, const std::string &path,
diff --git a/src/sel_logger.cpp b/src/sel_logger.cpp
index 6068e20..89eee7d 100644
--- a/src/sel_logger.cpp
+++ b/src/sel_logger.cpp
@@ -15,9 +15,11 @@
 */
 #include <systemd/sd-journal.h>
 
+#include <boost/algorithm/string.hpp>
 #include <boost/container/flat_map.hpp>
 #include <boost/container/flat_set.hpp>
-#include <experimental/string_view>
+#include <filesystem>
+#include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <pulse_event_monitor.hpp>
@@ -43,44 +45,55 @@
     };
 };
 
+static bool getSELLogFiles(std::vector<std::filesystem::path> &selLogFiles)
+{
+    // Loop through the directory looking for ipmi_sel log files
+    for (const std::filesystem::directory_entry &dirEnt :
+         std::filesystem::directory_iterator(selLogDir))
+    {
+        std::string filename = dirEnt.path().filename();
+        if (boost::starts_with(filename, selLogFilename))
+        {
+            // If we find an ipmi_sel log file, save the path
+            selLogFiles.emplace_back(selLogDir / filename);
+        }
+    }
+    // As the log files rotate, they are appended with a ".#" that is higher for
+    // the older logs. Since we don't expect more than 10 log files, we
+    // can just sort the list to get them in order from newest to oldest
+    std::sort(selLogFiles.begin(), selLogFiles.end());
+
+    return !selLogFiles.empty();
+}
+
 static unsigned int initializeRecordId(void)
 {
-    int journalError = -1;
-    sd_journal *journal;
-    journalError = sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY);
-    if (journalError < 0)
+    std::vector<std::filesystem::path> selLogFiles;
+    if (!getSELLogFiles(selLogFiles))
     {
-        std::cerr << "Failed to open journal: " << strerror(-journalError)
-                  << "\n";
-        throw DBusInternalError();
+        return selInvalidRecID;
     }
-    unsigned int recordId = selInvalidRecID;
-    char match[256] = {};
-    snprintf(match, sizeof(match), "MESSAGE_ID=%s", selMessageId);
-    sd_journal_add_match(journal, match, 0);
-    SD_JOURNAL_FOREACH_BACKWARDS(journal)
+    std::ifstream logStream(selLogFiles.front());
+    if (!logStream.is_open())
     {
-        const char *data = nullptr;
-        size_t length;
+        return selInvalidRecID;
+    }
+    std::string line;
+    std::string newestEntry;
+    while (std::getline(logStream, line))
+    {
+        newestEntry = line;
+    }
 
-        journalError = sd_journal_get_data(journal, "IPMI_SEL_RECORD_ID",
-                                           (const void **)&data, &length);
-        if (journalError < 0)
-        {
-            std::cerr << "Failed to read IPMI_SEL_RECORD_ID field: "
-                      << strerror(-journalError) << "\n";
-            continue;
-        }
-        if (journalError =
-                sscanf(data, "IPMI_SEL_RECORD_ID=%u", &recordId) != 1)
-        {
-            std::cerr << "Failed to parse record ID: " << journalError << "\n";
-            throw DBusInternalError();
-        }
-        break;
+    std::vector<std::string> newestEntryFields;
+    boost::split(newestEntryFields, newestEntry, boost::is_any_of(" ,"),
+                 boost::token_compress_on);
+    if (newestEntryFields.size() < 4)
+    {
+        return selInvalidRecID;
     }
-    sd_journal_close(journal);
-    return recordId;
+
+    return std::stoul(newestEntryFields[1]);
 }
 
 static unsigned int getNewRecordId(void)