PEL: Close PEL FD after getPEL call

To close the file descriptor returned from the getPEL D-Bus method call,
use the 'Defer' sdeventplus object to schedule it to happen from the
event loop.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I2b850a1c92215e7f66166cc5440d7071663065c0
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index aec5053..a705e67 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -18,6 +18,8 @@
 #include "additional_data.hpp"
 #include "pel.hpp"
 
+#include <unistd.h>
+
 #include <filesystem>
 #include <fstream>
 #include <xyz/openbmc_project/Common/error.hpp>
@@ -182,9 +184,25 @@
         throw common_error::InvalidArgument();
     }
 
+    scheduleFDClose(*fd);
+
     return *fd;
 }
 
+void Manager::scheduleFDClose(int fd)
+{
+    _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>(
+        _logManager.getBus().get_event(),
+        std::bind(std::mem_fn(&Manager::closeFD), this, fd,
+                  std::placeholders::_1));
+}
+
+void Manager::closeFD(int fd, sdeventplus::source::EventBase& source)
+{
+    close(fd);
+    _fdCloserEventSource.reset();
+}
+
 std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID)
 {
     Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
diff --git a/extensions/openpower-pels/manager.hpp b/extensions/openpower-pels/manager.hpp
index 7f1fcda..14904a9 100644
--- a/extensions/openpower-pels/manager.hpp
+++ b/extensions/openpower-pels/manager.hpp
@@ -11,6 +11,8 @@
 
 #include <org/open_power/Logging/PEL/server.hpp>
 #include <sdbusplus/server.hpp>
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/source/event.hpp>
 
 namespace openpower
 {
@@ -176,6 +178,27 @@
                    const std::vector<std::string>& associations);
 
     /**
+     * @brief Schedules a close of the file descriptor to occur from
+     *        the event loop.
+     *
+     * Uses sd_event_add_defer
+     *
+     * @param[in] fd - The file descriptor to close
+     */
+    void scheduleFDClose(int fd);
+
+    /**
+     * @brief Closes the file descriptor passed in.
+     *
+     * This is called from the event loop to close FDs returned
+     * from getPEL().
+     *
+     * @param[in] fd - The file descriptor to close
+     * @param[in] source - The event source object used
+     */
+    void closeFD(int fd, sdeventplus::source::EventBase& source);
+
+    /**
      * @brief Reference to phosphor-logging's Manager class
      */
     phosphor::logging::internal::Manager& _logManager;
@@ -200,6 +223,12 @@
      *        host about new PELs
      */
     std::unique_ptr<HostNotifier> _hostNotifier;
+
+    /**
+     * @brief The event source for closing a PEL file descriptor after
+     *        it has been returned from the getPEL D-Bus method.
+     */
+    std::unique_ptr<sdeventplus::source::Defer> _fdCloserEventSource;
 };
 
 } // namespace pels
diff --git a/test/openpower-pels/pel_manager_test.cpp b/test/openpower-pels/pel_manager_test.cpp
index 3036f30..8f3df22 100644
--- a/test/openpower-pels/pel_manager_test.cpp
+++ b/test/openpower-pels/pel_manager_test.cpp
@@ -28,6 +28,21 @@
 
 class ManagerTest : public CleanPELFiles
 {
+  public:
+    ManagerTest() : logManager(bus, "logging_path")
+    {
+        sd_event_default(&sdEvent);
+        bus.attach_event(sdEvent, SD_EVENT_PRIORITY_NORMAL);
+    }
+
+    ~ManagerTest()
+    {
+        sd_event_unref(sdEvent);
+    }
+
+    sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+    phosphor::logging::internal::Manager logManager;
+    sd_event* sdEvent;
 };
 
 fs::path makeTempDir()
@@ -56,8 +71,6 @@
 // a PEL saved in the repository.
 TEST_F(ManagerTest, TestCreateWithPEL)
 {
-    auto bus = sdbusplus::bus::new_default();
-    phosphor::logging::internal::Manager logManager(bus, "logging_path");
     std::unique_ptr<DataInterfaceBase> dataIface =
         std::make_unique<DataInterface>(bus);
 
@@ -170,8 +183,6 @@
 
 TEST_F(ManagerTest, TestDBusMethods)
 {
-    auto bus = sdbusplus::bus::new_default();
-    phosphor::logging::internal::Manager logManager(bus, "logging_path");
     std::unique_ptr<DataInterfaceBase> dataIface =
         std::make_unique<DataInterface>(bus);