Untar tarball

Untar the tarball to the image dir. The image dir is
<IMG_UPLOAD_DIR>/<id>/. The MANIFEST is first extracted,
the version is read from the MANIFEST and used to compute the id.

Change-Id: Ifed01e980202404e104ce7c20beb85ab23527913
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/configure.ac b/configure.ac
index 8e2ddbf..8f04258 100755
--- a/configure.ac
+++ b/configure.ac
@@ -64,6 +64,10 @@
 AS_IF([test "x$IMG_UPLOAD_DIR" == "x"], [IMG_UPLOAD_DIR="/tmp/images"])
 AC_DEFINE_UNQUOTED([IMG_UPLOAD_DIR], ["$IMG_UPLOAD_DIR"], [Directory where downloaded software images are placed])
 
+AC_ARG_VAR(MANIFEST_FILE_NAME, [The name of the MANIFEST file])
+AS_IF([test "x$MANIFEST_FILE_NAME" == "x"], [MANIFEST_FILE_NAME="MANIFEST"])
+AC_DEFINE_UNQUOTED([MANIFEST_FILE_NAME], ["$MANIFEST_FILE_NAME"], [The name of the MANIFEST file])
+
 # Check for header files.
 AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])])
 AC_CHECK_HEADER(sdbusplus/server.hpp, ,[AC_MSG_ERROR([Could not find sdbusplus/server.hpp...openbmc/sdbusplus package required])])
diff --git a/image_manager.cpp b/image_manager.cpp
index 25b08ba..02bc9a9 100644
--- a/image_manager.cpp
+++ b/image_manager.cpp
@@ -1,4 +1,14 @@
 #include <string>
+#include <experimental/filesystem>
+#include <stdlib.h>
+#include <cstring>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <phosphor-logging/log.hpp>
+#include "config.h"
+#include "version.hpp"
 #include "image_manager.hpp"
 
 namespace phosphor
@@ -8,11 +18,148 @@
 namespace manager
 {
 
+using namespace phosphor::logging;
+namespace fs = std::experimental::filesystem;
+
+struct RemovablePath
+{
+    fs::path path;
+
+    RemovablePath(const fs::path& path) : path(path) {}
+    ~RemovablePath()
+    {
+        fs::remove_all(path);
+    }
+};
+
 int processImage(const std::string& tarFilePath)
 {
+    if (!fs::is_regular_file(tarFilePath))
+    {
+        log<level::ERR>("Error tarball does not exist",
+                        entry("FILENAME=%s", tarFilePath));
+        return -1;
+
+    }
+    RemovablePath tarPathRemove(tarFilePath);
+    fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR});
+    tmpDirPath /= "imageXXXXXX";
+
+    // Need tmp dir to write MANIFEST file to.
+    if (!mkdtemp(const_cast<char*>(tmpDirPath.c_str())))
+    {
+        log<level::ERR>("Error occured during mkdtemp",
+                        entry("ERRNO=%d", errno));
+        return -1;
+    }
+
+    RemovablePath tmpDirRemove(tmpDirPath);
+    fs::path manifestPath = tmpDirPath;
+    manifestPath /= MANIFEST_FILE_NAME;
+    int status = 0;
+    pid_t pid = fork();
+
+    // Get the MANIFEST FILE
+    if (pid == 0)
+    {
+        // child process
+        execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), MANIFEST_FILE_NAME,
+              "-C", tmpDirPath.c_str(), (char*)0);
+        // execl only returns on fail
+        log<level::ERR>("Failed to untar file",
+                        entry("FILENAME=%s", tarFilePath));
+        return -1;
+    }
+    else if (pid > 0)
+    {
+        waitpid(pid, &status, 0);
+    }
+    else
+    {
+        log<level::ERR>("fork() failed.");
+        return -1;
+    }
+
+    // Verify the manifest file
+    if (!fs::is_regular_file(manifestPath))
+    {
+        log<level::ERR>("Error No manifest file");
+        return -1;
+    }
+
+    // Get version
+    auto version = Version::getValue(manifestPath.string(), "version");
+    if (version.empty())
+    {
+        log<level::ERR>("Error unable to read version from manifest file");
+        return -1;
+    }
+
+    // Compute id
+    auto id = Version::getId(version);
+
+    fs::path imageDirPath = std::string{IMG_UPLOAD_DIR};
+    imageDirPath /= id;
+
+    if (mkdir(imageDirPath.c_str(), S_IRWXU) != 0)
+    {
+        log<level::ERR>("Error occured during mkdir",
+                        entry("ERRNO=%d", errno));
+        return -1;
+    }
+
+    // Untar tarball
+    auto rc = unTar(tarFilePath, imageDirPath.string());
+    if (rc < 0)
+    {
+        log<level::ERR>("Error occured during untar");
+        return -1;
+    }
+
     return 0;
 }
 
+int unTar(const std::string& tarFilePath, const std::string& extractDirPath)
+{
+    if (tarFilePath.empty())
+    {
+        log<level::ERR>("Error TarFilePath is empty");
+        return -1;
+    }
+    if (extractDirPath.empty())
+    {
+        log<level::ERR>("Error ExtractDirPath is empty");
+        return -1;
+    }
+
+    log<level::INFO>("Untaring",
+                     entry("FILENAME=%s", tarFilePath),
+                     entry("EXTRACTIONDIR=%s", extractDirPath));
+    int status = 0;
+    pid_t pid = fork();
+
+    if (pid == 0)
+    {
+        // child process
+        execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(),
+              "-C", extractDirPath.c_str(), (char*)0);
+        // execl only returns on fail
+        log<level::ERR>("Failed to untar file",
+                        entry("FILENAME=%s", tarFilePath));
+        return -1;
+    }
+    else if (pid > 0)
+    {
+        waitpid(pid, &status, 0);
+    }
+    else
+    {
+        log<level::ERR>("fork() failed.");
+        return -1;
+    }
+
+    return 0;
+}
 } // namespace manager
 } // namespace software
 } // namepsace phosphor
diff --git a/image_manager.hpp b/image_manager.hpp
index e57599f..49baffe 100644
--- a/image_manager.hpp
+++ b/image_manager.hpp
@@ -16,6 +16,16 @@
  */
 int processImage(const std::string& tarballFilePath);
 
+/**
+ * @brief Untar the tarball.
+ *
+ * @param[in]  tarballFilePath - Tarball path.
+ * @param[in]  extractDirPath  - Dir path to extract tarball ball to.
+ * @param[out] result          - 0 if successful.
+ */
+int unTar(const std::string& tarballFilePath,
+          const std::string& extractDirPath);
+
 } // namespace manager
 } // namespace software
 } // namespace phosphor