Callout support for SBE when watchdog times out

This commit provides additional callout support for SBE when the
host does not respond within watchdog timeout interval during SBE
boot window.

Testing:
Steps used:
 1. obmcutil poweroff
 2. istep -s0
 3. systemctl start org.open_power.Dump.Manager.service
 4. systemctl start openpower-debug-collector-watchdog@0.service
 5. Check the journal log, and get the PEL id to print PEL info

PEL info:
    "Callout Section": {
        "Callout Count":        "2",
        "Callouts": [{
            "FRU Type":         "Maintenance Procedure Required",
            "Priority":         "Mandatory, replace all with this type
as a unit",
            "Procedure":        "BMC0002"
        }, {
            "FRU Type":         "Normal Hardware FRU",
            "Priority":         "Medium Priority",
            "Location Code":    "xxxxxxxxxxxxx",
            "Part Number":      "xxxxx",
            "CCIN":             "xxxx",
            "Serial Number":    "xxxxxxx"
        }
...
"User Data 2": {
    "Section Version": "1",
    "Sub-section type": "1",
    "Created by": "0x2000",
    "Data": [
        {
            "Priority": "H",
            "Procedure": "BMC0002"
        },
        {
            "Deconfigured": false,
            "Guarded": false,
            "LocationCode": "Ufcs-xx-xxx",
            "Priority": "M"
        }
    ]
}

Signed-off-by: Shantappa Teekappanavar <sbteeks@yahoo.com>
Change-Id: I5e182cb415a807d97c98812a6713905d39fdbc9a
diff --git a/watchdog/ffdc_file.cpp b/watchdog/ffdc_file.cpp
new file mode 100644
index 0000000..4fe5875
--- /dev/null
+++ b/watchdog/ffdc_file.cpp
@@ -0,0 +1,91 @@
+#include "ffdc_file.hpp"
+
+#include <errno.h> // for errno
+#include <fcntl.h> // for open()
+#include <fmt/format.h>
+#include <string.h>    // for strerror()
+#include <sys/stat.h>  // for open()
+#include <sys/types.h> // for open()
+
+#include <phosphor-logging/log.hpp>
+
+#include <stdexcept>
+#include <string>
+
+namespace watchdog
+{
+namespace dump
+{
+using namespace phosphor::logging;
+
+FFDCFile::FFDCFile(const json& calloutDataObject) :
+    calloutData(calloutDataObject.dump())
+{
+    prepareFFDCFile();
+}
+
+FFDCFile::~FFDCFile()
+{
+    // Close file descriptor.  Does nothing if descriptor was already closed.
+    if (descriptor.close() == -1)
+    {
+        log<level::ERR>(fmt::format("Unable to close FFDC file: errormsg({})",
+                                    strerror(errno))
+                            .c_str());
+    }
+
+    // Delete temporary file.  Does nothing if file was already deleted.
+    tempFile.remove();
+}
+
+void FFDCFile::prepareFFDCFile()
+{
+    // Open the temporary file for both reading and writing
+    int fd = open(tempFile.getPath().c_str(), O_RDWR);
+    if (fd == -1)
+    {
+        throw std::runtime_error{std::string{"Unable to open FFDC file: "} +
+                                 strerror(errno)};
+    }
+
+    ssize_t rc = write(fd, calloutData.c_str(), calloutData.size());
+
+    if (rc == -1)
+    {
+        log<level::ERR>(fmt::format("Failed to write callout info "
+                                    "in file({}), errorno({}), errormsg({})",
+                                    tempFile.getPath().c_str(), errno,
+                                    strerror(errno))
+                            .c_str());
+        throw std::runtime_error("Failed to write phalPELCallouts info");
+    }
+    else if (rc != static_cast<ssize_t>(calloutData.size()))
+    {
+        log<level::WARNING>(fmt::format("Could not write all callout "
+                                        "info in file({}), written byte({}) "
+                                        "and total byte({})",
+                                        tempFile.getPath().c_str(), rc,
+                                        calloutData.size())
+                                .c_str());
+    }
+
+    int retCode = lseek(fd, 0, SEEK_SET);
+
+    if (retCode == -1)
+    {
+        log<level::ERR>(
+            fmt::format("Failed to seek file postion to the beginning"
+                        "in file({}), errorno({}) "
+                        "and errormsg({})",
+                        tempFile.getPath().c_str(), errno, strerror(errno))
+                .c_str());
+        throw std::runtime_error(
+            "Failed to seek file postion to the beginning of the file");
+    }
+
+    // Store file descriptor in FileDescriptor object
+    descriptor.set(fd);
+}
+
+} // namespace dump
+} // namespace watchdog