Copied FFDCFile and supporting code from phosphor-power

Change-Id: Ife659c941c70f66edcb531df2e0d4da8bf158b06
diff --git a/analyzer/meson.build b/analyzer/meson.build
index 87b6fcc..092a759 100644
--- a/analyzer/meson.build
+++ b/analyzer/meson.build
@@ -2,8 +2,12 @@
 sdbusplus = dependency('sdbusplus', version : '>=1.0')
 
 # gather analyzer sources to be used here and elsewhere if needed
-analyzer_src = files('analyzer_main.cpp',
-                     'hei_user_interface.cpp')
+analyzer_src = files(
+    'analyzer_main.cpp',
+    'hei_user_interface.cpp',
+    '../util/ffdc_file.cpp',
+    '../util/temporary_file.cpp',
+)
 
 # Create hardware error analyzer library
 analyzer = static_library('analyzer',
diff --git a/util/ffdc_file.cpp b/util/ffdc_file.cpp
new file mode 100644
index 0000000..a067e0b
--- /dev/null
+++ b/util/ffdc_file.cpp
@@ -0,0 +1,45 @@
+#include "util/ffdc_file.hpp"
+
+#include <errno.h>     // for errno
+#include <fcntl.h>     // for open()
+#include <string.h>    // for strerror()
+#include <sys/stat.h>  // for open()
+#include <sys/types.h> // for open()
+
+#include <stdexcept>
+#include <string>
+
+namespace util
+{
+
+FFDCFile::FFDCFile(FFDCFormat format, uint8_t subType, uint8_t version) :
+    format{format}, subType{subType}, version{version}
+{
+    // 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)};
+    }
+
+    // Store file descriptor in FileDescriptor object
+    descriptor.set(fd);
+}
+
+void FFDCFile::remove()
+{
+    // Close file descriptor.  Does nothing if descriptor was already closed.
+    // Returns -1 if close failed.
+    if (descriptor.close() == -1)
+    {
+        throw std::runtime_error{std::string{"Unable to close FFDC file: "} +
+                                 strerror(errno)};
+    }
+
+    // Delete temporary file.  Does nothing if file was already deleted.
+    // Throws an exception if the deletion failed.
+    tempFile.remove();
+}
+
+} // namespace util
diff --git a/util/ffdc_file.hpp b/util/ffdc_file.hpp
new file mode 100644
index 0000000..f23fddd
--- /dev/null
+++ b/util/ffdc_file.hpp
@@ -0,0 +1,156 @@
+#pragma once
+
+#include "util/file_descriptor.hpp"
+#include "util/temporary_file.hpp"
+#include "xyz/openbmc_project/Logging/Create/server.hpp"
+
+#include <cstdint>
+#include <filesystem>
+
+namespace util
+{
+
+namespace fs = std::filesystem;
+using FFDCFormat =
+    sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat;
+
+/**
+ * @class FFDCFile
+ *
+ * File that contains FFDC (first failure data capture) data.
+ *
+ * This class is used to store FFDC data in an error log.  The FFDC data is
+ * passed to the error logging system using a file descriptor.
+ *
+ * The constructor creates the file and opens it for both reading and writing.
+ *
+ * Use getFileDescriptor() to obtain the file descriptor needed to read or write
+ * data to the file.
+ *
+ * Use remove() to delete the file.  Otherwise the file will be deleted by the
+ * destructor.
+ *
+ * FFDCFile objects cannot be copied, but they can be moved.  This enables them
+ * to be stored in containers like std::vector.
+ */
+class FFDCFile
+{
+  public:
+    // Specify which compiler-generated methods we want
+    FFDCFile()                = delete;
+    FFDCFile(const FFDCFile&) = delete;
+    FFDCFile(FFDCFile&&)      = default;
+    FFDCFile& operator=(const FFDCFile&) = delete;
+    FFDCFile& operator=(FFDCFile&&) = default;
+    ~FFDCFile()                     = default;
+
+    /**
+     * Constructor.
+     *
+     * Creates the file and opens it for both reading and writing.
+     *
+     * Throws an exception if an error occurs.
+     *
+     * @param format format type of the contained data
+     * @param subType format subtype; used for the 'Custom' type
+     * @param version version of the data format; used for the 'Custom' type
+     */
+    explicit FFDCFile(FFDCFormat format, uint8_t subType = 0,
+                      uint8_t version = 0);
+
+    /**
+     * Returns the file descriptor for the file.
+     *
+     * The file is open for both reading and writing.
+     *
+     * @return file descriptor
+     */
+    int getFileDescriptor()
+    {
+        // Return the integer file descriptor within the FileDescriptor object
+        return descriptor();
+    }
+
+    /**
+     * Returns the format type of the contained data.
+     *
+     * @return format type
+     */
+    FFDCFormat getFormat() const
+    {
+        return format;
+    }
+
+    /**
+     * Returns the absolute path to the file.
+     *
+     * @return absolute path
+     */
+    const fs::path& getPath() const
+    {
+        return tempFile.getPath();
+    }
+
+    /**
+     * Returns the format subtype.
+     *
+     * @return subtype
+     */
+    uint8_t getSubType() const
+    {
+        return subType;
+    }
+
+    /**
+     * Returns the version of the data format.
+     *
+     * @return version
+     */
+    uint8_t getVersion() const
+    {
+        return version;
+    }
+
+    /**
+     * Closes and deletes the file.
+     *
+     * Does nothing if the file has already been removed.
+     *
+     * Throws an exception if an error occurs.
+     */
+    void remove();
+
+  private:
+    /**
+     * Format type of the contained data.
+     */
+    FFDCFormat format{FFDCFormat::Text};
+
+    /**
+     * Format subtype; used for the 'Custom' type.
+     */
+    uint8_t subType{0};
+
+    /**
+     * Version of the data format; used for the 'Custom' type.
+     */
+    uint8_t version{0};
+
+    /**
+     * Temporary file where FFDC data is stored.
+     *
+     * The TemporaryFile destructor will automatically delete the file if it was
+     * not explicitly deleted using remove().
+     */
+    TemporaryFile tempFile{};
+
+    /**
+     * File descriptor for reading from/writing to the file.
+     *
+     * The FileDescriptor destructor will automatically close the file if it was
+     * not explicitly closed using remove().
+     */
+    FileDescriptor descriptor{};
+};
+
+} // namespace util
diff --git a/util/file_descriptor.hpp b/util/file_descriptor.hpp
new file mode 100644
index 0000000..51c125e
--- /dev/null
+++ b/util/file_descriptor.hpp
@@ -0,0 +1,134 @@
+#pragma once
+
+#include <unistd.h> // for close()
+
+namespace util
+{
+
+/**
+ * @class FileDescriptor
+ *
+ * This class manages an open file descriptor.
+ *
+ * The file descriptor can be closed by calling close().  Otherwise it will be
+ * closed by the destructor.
+ *
+ * FileDescriptor objects cannot be copied, but they can be moved.  This enables
+ * them to be stored in containers like std::vector.
+ */
+class FileDescriptor
+{
+  public:
+    FileDescriptor()                      = default;
+    FileDescriptor(const FileDescriptor&) = delete;
+    FileDescriptor& operator=(const FileDescriptor&) = delete;
+
+    /**
+     * Constructor.
+     *
+     * @param[in] fd - File descriptor
+     */
+    explicit FileDescriptor(int fd) : fd(fd) {}
+
+    /**
+     * Move constructor.
+     *
+     * Transfers ownership of a file descriptor.
+     *
+     * @param other - FileDescriptor object being moved
+     */
+    FileDescriptor(FileDescriptor&& other) : fd(other.fd)
+    {
+        other.fd = -1;
+    }
+
+    /**
+     * Move assignment operator.
+     *
+     * Closes the file descriptor owned by this object, if any.  Then transfers
+     * ownership of the file descriptor owned by the other object.
+     *
+     * @param other - FileDescriptor object being moved
+     */
+    FileDescriptor& operator=(FileDescriptor&& other)
+    {
+        // Verify not assigning object to itself (a = std::move(a))
+        if (this != &other)
+        {
+            set(other.fd);
+            other.fd = -1;
+        }
+        return *this;
+    }
+
+    /**
+     * Destructor.
+     *
+     * Closes the file descriptor if necessary.
+     */
+    ~FileDescriptor()
+    {
+        close();
+    }
+
+    /**
+     * Returns the file descriptor.
+     *
+     * @return File descriptor.  Returns -1 if this object does not contain an
+     *         open file descriptor.
+     */
+    int operator()()
+    {
+        return fd;
+    }
+
+    /**
+     * Returns whether this object contains an open file descriptor.
+     *
+     * @return true if object contains an open file descriptor, false otherwise.
+     */
+    operator bool() const
+    {
+        return fd != -1;
+    }
+
+    /**
+     * Closes the file descriptor.
+     *
+     * Does nothing if the file descriptor was not set or was already closed.
+     *
+     * @return 0 if descriptor was successfully closed.  Returns -1 if an error
+     *         occurred; errno will be set appropriately.
+     */
+    int close()
+    {
+        int rc = 0;
+        if (fd >= 0)
+        {
+            rc = ::close(fd);
+            fd = -1;
+        }
+        return rc;
+    }
+
+    /**
+     * Sets the file descriptor.
+     *
+     * Closes the previous file descriptor if necessary.
+     *
+     * @param[in] descriptor - File descriptor
+     */
+    void set(int descriptor)
+    {
+        close();
+        fd = descriptor;
+    }
+
+  private:
+    /**
+     * File descriptor.
+     */
+    int fd = -1;
+};
+
+} // namespace util
diff --git a/util/temporary_file.cpp b/util/temporary_file.cpp
new file mode 100644
index 0000000..0bd4d08
--- /dev/null
+++ b/util/temporary_file.cpp
@@ -0,0 +1,77 @@
+#include "util/temporary_file.hpp"
+
+#include <errno.h>  // for errno
+#include <stdlib.h> // for mkstemp()
+#include <string.h> // for strerror()
+#include <unistd.h> // for close()
+
+#include <stdexcept>
+#include <string>
+
+namespace util
+{
+
+TemporaryFile::TemporaryFile()
+{
+    // Build template path required by mkstemp()
+    std::string templatePath =
+        fs::temp_directory_path() / "openpower-hw-diags-XXXXXX";
+
+    // Generate unique file name, create file, and open it.  The XXXXXX
+    // characters are replaced by mkstemp() to make the file name unique.
+    int fd = mkstemp(templatePath.data());
+    if (fd == -1)
+    {
+        throw std::runtime_error{
+            std::string{"Unable to create temporary file: "} + strerror(errno)};
+    }
+
+    // Store path to temporary file
+    path = templatePath;
+
+    // Close file descriptor
+    if (close(fd) == -1)
+    {
+        // Save errno value; will likely change when we delete temporary file
+        int savedErrno = errno;
+
+        // Delete temporary file.  The destructor won't be called because the
+        // exception below causes this constructor to exit without completing.
+        remove();
+
+        throw std::runtime_error{
+            std::string{"Unable to close temporary file: "} +
+            strerror(savedErrno)};
+    }
+}
+
+TemporaryFile& TemporaryFile::operator=(TemporaryFile&& file)
+{
+    // Verify not assigning object to itself (a = std::move(a))
+    if (this != &file)
+    {
+        // Delete temporary file owned by this object
+        remove();
+
+        // Move temporary file path from other object, transferring ownership
+        path = std::move(file.path);
+
+        // Clear path in other object; after move path is in unspecified state
+        file.path.clear();
+    }
+    return *this;
+}
+
+void TemporaryFile::remove()
+{
+    if (!path.empty())
+    {
+        // Delete temporary file from file system
+        fs::remove(path);
+
+        // Clear path to indicate file has been deleted
+        path.clear();
+    }
+}
+
+} // namespace util
diff --git a/util/temporary_file.hpp b/util/temporary_file.hpp
new file mode 100644
index 0000000..3065401
--- /dev/null
+++ b/util/temporary_file.hpp
@@ -0,0 +1,113 @@
+#pragma once
+
+#include <filesystem>
+#include <utility>
+
+namespace util
+{
+
+namespace fs = std::filesystem;
+
+/**
+ * @class TemporaryFile
+ *
+ * A temporary file in the file system.
+ *
+ * The temporary file is created by the constructor.  The absolute path to the
+ * file can be obtained using getPath().
+ *
+ * The temporary file can be deleted by calling remove().  Otherwise the file
+ * will be deleted by the destructor.
+ *
+ * TemporaryFile objects cannot be copied, but they can be moved.  This enables
+ * them to be stored in containers like std::vector.
+ */
+class TemporaryFile
+{
+  public:
+    // Specify which compiler-generated methods we want
+    TemporaryFile(const TemporaryFile&) = delete;
+    TemporaryFile& operator=(const TemporaryFile&) = delete;
+
+    /**
+     * Constructor.
+     *
+     * Creates a temporary file in the temporary directory (normally /tmp).
+     *
+     * Throws an exception if the file cannot be created.
+     */
+    TemporaryFile();
+
+    /**
+     * Move constructor.
+     *
+     * Transfers ownership of a temporary file.
+     *
+     * @param file TemporaryFile object being moved
+     */
+    TemporaryFile(TemporaryFile&& file) : path{std::move(file.path)}
+    {
+        // Clear path in other object; after move path is in unspecified state
+        file.path.clear();
+    }
+
+    /**
+     * Move assignment operator.
+     *
+     * Deletes the temporary file owned by this object.  Then transfers
+     * ownership of the temporary file owned by the other object.
+     *
+     * Throws an exception if an error occurs during the deletion.
+     *
+     * @param file TemporaryFile object being moved
+     */
+    TemporaryFile& operator=(TemporaryFile&& file);
+
+    /**
+     * Destructor.
+     *
+     * Deletes the temporary file if necessary.
+     */
+    ~TemporaryFile()
+    {
+        try
+        {
+            remove();
+        }
+        catch (...)
+        {
+            // Destructors should not throw exceptions
+        }
+    }
+
+    /**
+     * Deletes the temporary file.
+     *
+     * Does nothing if the file has already been deleted.
+     *
+     * Throws an exception if an error occurs during the deletion.
+     */
+    void remove();
+
+    /**
+     * Returns the absolute path to the temporary file.
+     *
+     * Returns an empty path if the file has been deleted.
+     *
+     * @return temporary file path
+     */
+    const fs::path& getPath() const
+    {
+        return path;
+    }
+
+  private:
+    /**
+     * Absolute path to the temporary file.
+     *
+     * Empty when file has been deleted.
+     */
+    fs::path path{};
+};
+
+} // namespace util