control: Dump flight recorder on signal SIGUSR1

Add a SIGUSR1 handler to dump the flight recorder.

It uses the sdventplus sd_event_add_defer wrapper to do the dump when
the code gets back to the event loop so it doesn't spend the time doing
it inside the signal handler itself.

The flight recorder can now be dumped with:
    kill -USR1 $(pidof phosphor-fan-control)
    or
    systemctl kill -s USR1 phosphor-fan-control@0

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I46e45cb7a5e42ea4d217f63a8e14b6ef02ed8c22
diff --git a/control/json/manager.cpp b/control/json/manager.cpp
index 691c11f..fb997c5 100644
--- a/control/json/manager.cpp
+++ b/control/json/manager.cpp
@@ -17,6 +17,7 @@
 
 #include "manager.hpp"
 
+#include "../utils/flight_recorder.hpp"
 #include "action.hpp"
 #include "event.hpp"
 #include "fan.hpp"
@@ -91,6 +92,20 @@
     }
 }
 
+void Manager::sigUsr1Handler(sdeventplus::source::Signal&,
+                             const struct signalfd_siginfo*)
+{
+    _flightRecEventSource = std::make_unique<sdeventplus::source::Defer>(
+        _event, std::bind(std::mem_fn(&Manager::dumpFlightRecorder), this,
+                          std::placeholders::_1));
+}
+
+void Manager::dumpFlightRecorder(sdeventplus::source::EventBase& /*source*/)
+{
+    FlightRecorder::instance().dump();
+    _flightRecEventSource.reset();
+}
+
 void Manager::load()
 {
     if (_loadAllowed)
diff --git a/control/json/manager.hpp b/control/json/manager.hpp
index 30bc310..eb5a6ca 100644
--- a/control/json/manager.hpp
+++ b/control/json/manager.hpp
@@ -32,6 +32,7 @@
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server/manager.hpp>
 #include <sdeventplus/event.hpp>
+#include <sdeventplus/source/event.hpp>
 #include <sdeventplus/utility/timer.hpp>
 
 #include <chrono>
@@ -146,6 +147,13 @@
                        const struct signalfd_siginfo*);
 
     /**
+     * @brief Callback function to handle receiving a USR1 signal to dump
+     * the flight recorder.
+     */
+    void sigUsr1Handler(sdeventplus::source::Signal&,
+                        const struct signalfd_siginfo*);
+
+    /**
      * @brief Get the active profiles of the system where an empty list
      * represents that only configuration entries without a profile defined will
      * be loaded.
@@ -518,6 +526,10 @@
     /* List of events configured */
     std::map<configKey, std::unique_ptr<Event>> _events;
 
+    /* The sdeventplus wrapper around sd_event_add_defer to dump the
+     * flight recorder from the event loop after the USR1 signal.  */
+    std::unique_ptr<sdeventplus::source::Defer> _flightRecEventSource;
+
     /**
      * @brief A map of parameter names and values that are something
      *        other than just D-Bus property values that other actions
@@ -568,6 +580,11 @@
      * entries within the other configuration files.
      */
     void setProfiles();
+
+    /**
+     * @brief Callback from _flightRecEventSource to dump the flight recorder
+     */
+    void dumpFlightRecorder(sdeventplus::source::EventBase&);
 };
 
 } // namespace phosphor::fan::control::json
diff --git a/control/main.cpp b/control/main.cpp
index 81c368b..dcd4679 100644
--- a/control/main.cpp
+++ b/control/main.cpp
@@ -83,6 +83,13 @@
             std::bind(&json::Manager::sighupHandler, &manager,
                       std::placeholders::_1, std::placeholders::_2));
 
+        // Enable SIGUSR1 handling to dump the flight recorder
+        stdplus::signal::block(SIGUSR1);
+        sdeventplus::source::Signal sigUsr1(
+            event, SIGUSR1,
+            std::bind(&json::Manager::sigUsr1Handler, &manager,
+                      std::placeholders::_1, std::placeholders::_2));
+
         phosphor::fan::util::SDBusPlus::getBus().request_name(CONTROL_BUSNAME);
 #else
         Manager manager(phosphor::fan::util::SDBusPlus::getBus(), event, mode);