Add journal traces to occ-control PELs

When creating a PEL, the last lines of the journal for the app will get
saved into the PEL for additional debug.

Change-Id: Ifa05a00ffdc57833859d719d0e7d8b81ccadb5c8
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/occ_ffdc.hpp b/occ_ffdc.hpp
index a2b1200..61bd714 100644
--- a/occ_ffdc.hpp
+++ b/occ_ffdc.hpp
@@ -2,13 +2,76 @@
 
 #include "config.h"
 
+#include "file.hpp"
 #include "occ_errors.hpp"
 
+#include <systemd/sd-journal.h>
+
+#include <nlohmann/json.hpp>
+#include <xyz/openbmc_project/Logging/Create/server.hpp>
+
+using FFDCFormat =
+    sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat;
+using FFDCFiles = std::vector<
+    std::tuple<FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>>;
+
 namespace open_power
 {
 namespace occ
 {
 
+/** @class FFDCFile
+ *  @brief Represents a single file that will get opened when created and
+ *         deleted when the object is destructed
+ */
+class FFDCFile
+{
+  public:
+    FFDCFile() = delete;
+    FFDCFile(const FFDCFile&) = delete;
+    FFDCFile& operator=(const FFDCFile&) = delete;
+    FFDCFile(FFDCFile&&) = delete;
+    FFDCFile& operator=(FFDCFile&&) = delete;
+
+    /**
+     * @brief Constructor
+     *
+     * Opens the file and saves the descriptor
+     *
+     * @param[in] name - The filename
+     */
+    explicit FFDCFile(const std::filesystem::path& name);
+
+    /**
+     * @brief Destructor - Deletes the file
+     */
+    ~FFDCFile()
+    {
+        std::filesystem::remove(_name);
+    }
+
+    /**
+     * @brief Returns the file descriptor
+     *
+     * @return int - The descriptor
+     */
+    int fd()
+    {
+        return _fd();
+    }
+
+  private:
+    /**
+     * @brief The file descriptor holder
+     */
+    FileDescriptor _fd;
+
+    /**
+     * @brief The filename
+     */
+    const std::filesystem::path _name;
+};
+
 /** @class FFDC
  *  @brief Monitors for SBE FFDC availability
  */
@@ -64,6 +127,20 @@
     static void createOCCResetPEL(unsigned int instance, const char* path,
                                   int err, const char* callout);
 
+    /**
+     * @brief Create a file containing the latest journal traces for the
+     *        specified executable and add it to the file list.
+     *
+     * @param[in] fileList     - where to add the new file
+     * @param[in] executable   - name of app to collect
+     * @param[in] lines        - number of journal lines to save
+     *
+     * @return std::unique_ptr<FFDCFile> - The file object
+     */
+    static std::unique_ptr<FFDCFile>
+        addJournalEntries(FFDCFiles& fileList, const std::string& executable,
+                          unsigned int lines);
+
   private:
     /** @brief OCC instance number. Ex, 0,1, etc */
     unsigned int instance;
@@ -79,6 +156,77 @@
      *         content denotes an error condition
      */
     void analyzeEvent() override;
+
+    /**
+     * @brief Returns an FFDCFile containing the JSON data
+     *
+     * @param[in] ffdcData - The JSON data to write to a file
+     *
+     * @return std::unique_ptr<FFDCFile> - The file object
+     */
+    static std::unique_ptr<FFDCFile>
+        makeJsonFFDCFile(const nlohmann::json& ffdcData);
+
+    /**
+     * @brief Returns a JSON structure containing the previous N journal
+     * entries.
+     *
+     * @param[in] numLines   - Number of lines of journal to retrieve
+     * @param[in] executable - name of app to collect for
+     *
+     * @return JSON object that was created
+     */
+    static nlohmann::json getJournalEntries(int numLines,
+                                            std::string executable);
+
+    /**
+     * @brief Gets the realtime (wallclock) timestamp for the current journal
+     * entry.
+     *
+     * @param journal current journal entry
+     * @return timestamp as a date/time string
+     */
+    static std::string getTimeStamp(sd_journal* journal);
+
+    /**
+     * @brief Gets the value of the specified field for the current journal
+     * entry.
+     *
+     * Returns an empty string if the current journal entry does not have the
+     * specified field.
+     *
+     * @param journal current journal entry
+     * @param field journal field name
+     * @return field value
+     */
+    static std::string getFieldValue(sd_journal* journal,
+                                     const std::string& field);
+};
+
+/**
+ * @class JournalCloser
+ *  @brief Automatically closes the journal when the object goes out of scope.
+ */
+class JournalCloser
+{
+  public:
+    // Specify which compiler-generated methods we want
+    JournalCloser() = delete;
+    JournalCloser(const JournalCloser&) = delete;
+    JournalCloser(JournalCloser&&) = delete;
+    JournalCloser& operator=(const JournalCloser&) = delete;
+    JournalCloser& operator=(JournalCloser&&) = delete;
+
+    JournalCloser(sd_journal* journal) : journal{journal}
+    {}
+
+    ~JournalCloser()
+    {
+        sd_journal_close(journal);
+    }
+
+  private:
+    sd_journal* journal{nullptr};
 };
 
 } // namespace occ