Copied FFDCFile and supporting code from phosphor-power
Change-Id: Ife659c941c70f66edcb531df2e0d4da8bf158b06
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