Enable the BMC dump offload through given URI

Add the support for reading the dump and writing to the
path provided. The supported for writing the BMC dump
is added.
Tested using offloading a BMC dump to a remote system
using NBD.

Change-Id: I5250b41ed162905548a7d522b9dc86533d0a9a83
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 1c8c9ae..dd5f354 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,8 @@
 	dump_utils.hpp \
 	watch.hpp \
 	elog_watch.hpp \
-	dump_serialize.hpp
+	dump_serialize.hpp \
+	dump_offload.hpp
 
 nobase_nodist_include_HEADERS = \
 	xyz/openbmc_project/Dump/Internal/Create/server.hpp
@@ -28,7 +29,8 @@
 	elog_watch.cpp \
 	dump_serialize.cpp \
 	dump_utils.cpp \
-        system_dump_entry.cpp
+	system_dump_entry.cpp \
+	dump_offload.cpp
 
 phosphor_dump_monitor_SOURCES = \
 	watch.cpp \
diff --git a/bmc_dump_entry.cpp b/bmc_dump_entry.cpp
index 10395d8..ce59a7f 100644
--- a/bmc_dump_entry.cpp
+++ b/bmc_dump_entry.cpp
@@ -1,6 +1,7 @@
 #include "bmc_dump_entry.hpp"
 
 #include "dump_manager.hpp"
+#include "dump_offload.hpp"
 
 #include <phosphor-logging/log.hpp>
 
@@ -29,6 +30,12 @@
     phosphor::dump::Entry::delete_();
 }
 
+void Entry::initiateOffload(std::string uri)
+{
+    phosphor::dump::offload::requestOffload(file, id, uri);
+    offloaded(true);
+}
+
 } // namespace bmc
 } // namespace dump
 } // namespace phosphor
diff --git a/bmc_dump_entry.hpp b/bmc_dump_entry.hpp
index 8a535e1..9219be4 100644
--- a/bmc_dump_entry.hpp
+++ b/bmc_dump_entry.hpp
@@ -6,7 +6,7 @@
 #include "xyz/openbmc_project/Object/Delete/server.hpp"
 #include "xyz/openbmc_project/Time/EpochTime/server.hpp"
 
-#include <experimental/filesystem>
+#include <filesystem>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server/object.hpp>
 
@@ -63,6 +63,11 @@
      */
     void delete_() override;
 
+    /** @brief Method to initiate the offload of dump
+     *  @param[in] uri - URI to offload dump
+     */
+    void initiateOffload(std::string uri) override;
+
   private:
     /** @Dump file name */
     fs::path file;
diff --git a/dump_offload.cpp b/dump_offload.cpp
new file mode 100644
index 0000000..8adaf5d
--- /dev/null
+++ b/dump_offload.cpp
@@ -0,0 +1,87 @@
+#include "config.h"
+
+#include "dump_offload.hpp"
+
+#include <fstream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <xyz/openbmc_project/Common/File/error.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace dump
+{
+namespace offload
+{
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace phosphor::logging;
+
+void requestOffload(fs::path file, uint32_t dumpId, std::string writePath)
+{
+    using namespace sdbusplus::xyz::openbmc_project::Common::File::Error;
+    using ErrnoOpen = xyz::openbmc_project::Common::File::Open::ERRNO;
+    using PathOpen = xyz::openbmc_project::Common::File::Open::PATH;
+    using ErrnoWrite = xyz::openbmc_project::Common::File::Write::ERRNO;
+    using PathWrite = xyz::openbmc_project::Common::File::Write::PATH;
+
+    // open a dump file for a transfer.
+    fs::path dumpPath(BMC_DUMP_PATH);
+    dumpPath /= std::to_string(dumpId);
+    dumpPath /= file.filename();
+
+    std::ifstream infile{dumpPath, std::ios::in | std::ios::binary};
+    if (!infile.good())
+    {
+        // Unable to open the dump file
+        log<level::ERR>("Failed to open the dump from file ",
+                        entry("ERR=%d", errno),
+                        entry("DUMPFILE=%s", dumpPath.c_str()));
+        elog<InternalFailure>();
+    }
+
+    std::ofstream outfile{writePath, std::ios::out | std::ios::binary};
+    if (!outfile.good())
+    {
+        // Unable to open the write interface
+        auto err = errno;
+        log<level::ERR>("Write device path does not exist at ",
+                        entry("ERR=%d", errno),
+                        entry("WRITEINTERFACE=%s", writePath.c_str()));
+        elog<Open>(ErrnoOpen(err), PathOpen(writePath.c_str()));
+    }
+
+    infile.exceptions(std::ifstream::failbit | std::ifstream::badbit |
+                      std::ifstream::eofbit);
+    outfile.exceptions(std::ifstream::failbit | std::ifstream::badbit |
+                       std::ifstream::eofbit);
+
+    try
+    {
+        log<level::INFO>("Opening File for RW ",
+                         entry("FILENAME=%s", file.filename().c_str()));
+        outfile << infile.rdbuf() << std::flush;
+        infile.close();
+        outfile.close();
+    }
+    catch (std::ofstream::failure& oe)
+    {
+        auto err = errno;
+        log<level::ERR>("Failed to write to write interface ",
+                        entry("ERR=%s", oe.what()),
+                        entry("WRITEINTERFACE=%s", writePath.c_str()));
+        elog<Write>(ErrnoWrite(err), PathWrite(writePath.c_str()));
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Failed get the dump from file ",
+                        entry("ERR=%s", e.what()),
+                        entry("DUMPFILE=%s", dumpPath.c_str()));
+        elog<InternalFailure>();
+    }
+}
+
+} // namespace offload
+} // namespace dump
+} // namespace phosphor
diff --git a/dump_offload.hpp b/dump_offload.hpp
new file mode 100644
index 0000000..2678061
--- /dev/null
+++ b/dump_offload.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <experimental/filesystem>
+
+namespace phosphor
+{
+namespace dump
+{
+namespace offload
+{
+
+namespace fs = std::experimental::filesystem;
+
+/**
+ * @brief Kicks off the instructions to
+ *        start offload of the dump using dbus
+ *
+ * @param[in] file - dump filename with relative path.
+ * @param[in] dumpId - id of the dump.
+ * @param[in] writePath[in] - path to write the dump file.
+ *
+ **/
+void requestOffload(fs::path file, uint32_t dumpId, std::string writePath);
+
+} // namespace offload
+} // namespace dump
+} // namespace phosphor