Implement createWithFFDCFiles

This commit adds support for the createWithFFDCFiles D-Bus method.  This
method creates a new OpenBMC event log, just like create() does, but it
also adds a new parameter to pass through information about files
containing FFDC (First Failure Data Capture) to any extensions.  This
FFDC may be stored by the extensions code to provide additional debug
information about an error.

The FFDC parameter is a vector of tuples.  Each tuple contains:
* Format Type - An enumeration describing the format of the data
* Subtype - If the format type is custom, then this can be used
            to provide a format type that is specific to the
            creator.
* Version - If the format type is custom, then this can be used
            to provide the version of the custom data.
* FD      - A file descriptor to the file containing the FFDC.
            This does not need to be closed (an attempt will fail)
            by phosphor-log-manager.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I3e8e03d6393dfc145e9fd11bd078595868bb3767
diff --git a/extensions.hpp b/extensions.hpp
index 3897894..4e38f1d 100644
--- a/extensions.hpp
+++ b/extensions.hpp
@@ -19,6 +19,8 @@
 
 using AdditionalDataArg = std::vector<std::string>;
 using AssociationEndpointsArg = std::vector<std::string>;
+using FFDCArg = FFDCEntries;
+
 /**
  *  @brief The function type that will be called after an event log
  *         is created.
@@ -28,10 +30,11 @@
  * @param[in] Level - The event level
  * @param[in] const AdditionalDataArg&) - the additional data
  * @param[in] const AssociationEndpoints& - Association endpoints (callouts)
+ * @param[in] const FFDCArg& - A vector of FFDC file info.
  */
 using CreateFunction = std::function<void(
     const std::string&, uint32_t, uint64_t, Entry::Level,
-    const AdditionalDataArg&, const AssociationEndpointsArg&)>;
+    const AdditionalDataArg&, const AssociationEndpointsArg&, const FFDCArg&)>;
 
 /**
  * @brief The function type that will be called after an event log is deleted.
diff --git a/extensions/openpower-pels/entry_points.cpp b/extensions/openpower-pels/entry_points.cpp
index f5ef40c..6b2c470 100644
--- a/extensions/openpower-pels/entry_points.cpp
+++ b/extensions/openpower-pels/entry_points.cpp
@@ -55,8 +55,9 @@
 
 void pelCreate(const std::string& message, uint32_t id, uint64_t timestamp,
                Entry::Level severity, const AdditionalDataArg& additionalData,
-               const AssociationEndpointsArg& assocs)
+               const AssociationEndpointsArg& assocs, const FFDCArg& ffdc)
 {
+    // Next: pass through ffdc arg
     manager->create(message, id, timestamp, severity, additionalData, assocs);
 }
 
diff --git a/log_manager.cpp b/log_manager.cpp
index 569413a..bdb2e2e 100644
--- a/log_manager.cpp
+++ b/log_manager.cpp
@@ -185,7 +185,8 @@
 }
 
 void Manager::createEntry(std::string errMsg, Entry::Level errLvl,
-                          std::vector<std::string> additionalData)
+                          std::vector<std::string> additionalData,
+                          const FFDCEntries& ffdc)
 {
     if (!Extensions::disableDefaultLogCaps())
     {
@@ -229,12 +230,14 @@
                                      std::move(objects), fwVersion, *this);
     serialize(*e);
 
-    doExtensionLogCreate(*e);
+    doExtensionLogCreate(*e, ffdc);
+
+    // Note: No need to close the file descriptors in the FFDC.
 
     entries.insert(std::make_pair(entryId, std::move(e)));
 }
 
-void Manager::doExtensionLogCreate(const Entry& entry)
+void Manager::doExtensionLogCreate(const Entry& entry, const FFDCEntries& ffdc)
 {
     // Make the association <endpointpath>/<endpointtype> paths
     std::vector<std::string> assocs;
@@ -251,7 +254,7 @@
         try
         {
             create(entry.message(), entry.id(), entry.timestamp(),
-                   entry.severity(), entry.additionalData(), assocs);
+                   entry.severity(), entry.additionalData(), assocs, ffdc);
         }
         catch (std::exception& e)
         {
@@ -583,6 +586,18 @@
     createEntry(message, severity, ad);
 }
 
+void Manager::createWithFFDC(
+    const std::string& message, Entry::Level severity,
+    const std::map<std::string, std::string>& additionalData,
+    const FFDCEntries& ffdc)
+{
+    // Convert the map into a vector of "key=value" strings
+    std::vector<std::string> ad;
+    metadata::associations::combine(additionalData, ad);
+
+    createEntry(message, severity, ad, ffdc);
+}
+
 } // namespace internal
 } // namespace logging
 } // namespace phosphor
diff --git a/log_manager.hpp b/log_manager.hpp
index 9ab100d..74450cb 100644
--- a/log_manager.hpp
+++ b/log_manager.hpp
@@ -32,6 +32,16 @@
 
 } // namespace details
 
+constexpr size_t ffdcFormatPos = 0;
+constexpr size_t ffdcSubtypePos = 1;
+constexpr size_t ffdcVersionPos = 2;
+constexpr size_t ffdcFDPos = 3;
+
+using FFDCEntry = std::tuple<CreateIface::FFDCFormat, uint8_t, uint8_t,
+                             sdbusplus::message::unix_fd>;
+
+using FFDCEntries = std::vector<FFDCEntry>;
+
 namespace internal
 {
 
@@ -141,6 +151,26 @@
         sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level severity,
         const std::map<std::string, std::string>& additionalData);
 
+    /** @brief Creates an event log, and accepts FFDC files
+     *
+     * This is the same as create(), but also takes an FFDC argument.
+     *
+     * The FFDC argument is a vector of tuples that allows one to pass in file
+     * descriptors for files that contain FFDC (First Failure Data Capture).
+     * These will be passed to any event logging extensions.
+     *
+     * @param[in] errMsg - The error exception message associated with the
+     *                     error log to be committed.
+     * @param[in] severity - level of the error
+     * @param[in] additionalData - The AdditionalData property for the error
+     * @param[in] ffdc - A vector of FFDC file info
+     */
+    void createWithFFDC(
+        const std::string& message,
+        sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level severity,
+        const std::map<std::string, std::string>& additionalData,
+        const FFDCEntries& ffdc);
+
   private:
     /*
      * @fn _commit()
@@ -181,8 +211,9 @@
      *  extensions to create their own log based on this one.
      *
      *  @param[in] entry - the new event log entry
+     *  @param[in] ffdc - A vector of FFDC file info
      */
-    void doExtensionLogCreate(const Entry& entry);
+    void doExtensionLogCreate(const Entry& entry, const FFDCEntries& ffdc);
 
     /** @brief Common wrapper for creating an Entry object
      *
@@ -190,9 +221,12 @@
      *                     error log to be committed.
      * @param[in] errLvl - level of the error
      * @param[in] additionalData - The AdditionalData property for the error
+     * @param[in] ffdc - A vector of FFDC file info. Defaults to an empty
+     * vector.
      */
     void createEntry(std::string errMsg, Entry::Level errLvl,
-                     std::vector<std::string> additionalData);
+                     std::vector<std::string> additionalData,
+                     const FFDCEntries& ffdc = FFDCEntries{});
 
     /** @brief Persistent sdbusplus DBus bus connection. */
     sdbusplus::bus::bus& busLog;
@@ -267,6 +301,16 @@
         manager.create(message, severity, additionalData);
     }
 
+    /** @brief D-Bus method call implementation to create an event log with FFDC
+     *
+     * The same as create(), but takes an extra FFDC argument.
+     *
+     * @param[in] errMsg - The error exception message associated with the
+     *                     error log to be committed.
+     * @param[in] severity - Level of the error
+     * @param[in] additionalData - The AdditionalData property for the error
+     * @param[in] ffdc - A vector of FFDC file info
+     */
     void createWithFFDCFiles(
         std::string message,
         sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level severity,
@@ -275,6 +319,7 @@
                                sdbusplus::message::unix_fd>>
             ffdc) override
     {
+        manager.createWithFFDC(message, severity, additionalData, ffdc);
     }
 
   private:
diff --git a/test/extensions_test.cpp b/test/extensions_test.cpp
index 17c3395..d210115 100644
--- a/test/extensions_test.cpp
+++ b/test/extensions_test.cpp
@@ -23,13 +23,13 @@
 
 void create1(const std::string& message, uint32_t id, uint64_t timestamp,
              Entry::Level severity, const AdditionalDataArg& additionalData,
-             const AssociationEndpointsArg& assocs)
+             const AssociationEndpointsArg& assocs, const FFDCArg& ffdc)
 {
 }
 
 void create2(const std::string& message, uint32_t id, uint64_t timestamp,
              Entry::Level severity, const AdditionalDataArg& additionalData,
-             const AssociationEndpointsArg& assocs)
+             const AssociationEndpointsArg& assocs, const FFDCArg& ffdc)
 {
 }
 
@@ -74,10 +74,11 @@
 
     AdditionalDataArg ad;
     AssociationEndpointsArg assocs;
+    FFDCArg ffdc;
     EXPECT_EQ(Extensions::getCreateFunctions().size(), 2);
     for (auto& c : Extensions::getCreateFunctions())
     {
-        c("test", 5, 6, Entry::Level::Informational, ad, assocs);
+        c("test", 5, 6, Entry::Level::Informational, ad, assocs, ffdc);
     }
 
     EXPECT_EQ(Extensions::getDeleteFunctions().size(), 2);