Create dump manager for each dump type.

Currently all types of dumps exist in the same path
and under the single dump manager. When there are
multiple dumps to be created separate path is needed
for creating and managing the dump. this commit
is splitting the dump manager into multiple objects
without  adding any new functionality. There will be
only one dump manager process but it will contain
seperate dump manager objects for system and BMC
dumps as per current scope.

Tested the existing dump functions with the build
- created bmc dump
- created system dump using notify
- deleted dump entry
- offloaded bmc dump

Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
Change-Id: Id4806660be1f1ba0b3cb6f840ae185a967f05a83
diff --git a/dump_manager_bmc.cpp b/dump_manager_bmc.cpp
new file mode 100644
index 0000000..f6556f1
--- /dev/null
+++ b/dump_manager_bmc.cpp
@@ -0,0 +1,232 @@
+#include "config.h"
+
+#include "dump_manager_bmc.hpp"
+
+#include "bmc_dump_entry.hpp"
+#include "dump_internal.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+#include "xyz/openbmc_project/Dump/Create/error.hpp"
+
+#include <sys/inotify.h>
+#include <unistd.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <regex>
+
+namespace phosphor
+{
+namespace dump
+{
+namespace bmc
+{
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace phosphor::logging;
+
+namespace internal
+{
+
+void Manager::create(Type type, std::vector<std::string> fullPaths)
+{
+    dumpMgr.phosphor::dump::bmc::Manager::captureDump(type, fullPaths);
+}
+
+} // namespace internal
+
+uint32_t Manager::createDump()
+{
+    std::vector<std::string> paths;
+    return captureDump(Type::UserRequested, paths);
+}
+
+uint32_t Manager::captureDump(Type type,
+                              const std::vector<std::string>& fullPaths)
+{
+    // Get Dump size.
+    auto size = getAllowedSize();
+
+    pid_t pid = fork();
+
+    if (pid == 0)
+    {
+        fs::path dumpPath(dumpDir);
+        auto id = std::to_string(lastEntryId + 1);
+        dumpPath /= id;
+
+        // get dreport type map entry
+        auto tempType = TypeMap.find(type);
+
+        execl("/usr/bin/dreport", "dreport", "-d", dumpPath.c_str(), "-i",
+              id.c_str(), "-s", std::to_string(size).c_str(), "-q", "-v", "-p",
+              fullPaths.empty() ? "" : fullPaths.front().c_str(), "-t",
+              tempType->second.c_str(), nullptr);
+
+        // dreport script execution is failed.
+        auto error = errno;
+        log<level::ERR>("Error occurred during dreport function execution",
+                        entry("ERRNO=%d", error));
+        elog<InternalFailure>();
+    }
+    else if (pid > 0)
+    {
+        auto rc = sd_event_add_child(eventLoop.get(), nullptr, pid,
+                                     WEXITED | WSTOPPED, callback, nullptr);
+        if (0 > rc)
+        {
+            // Failed to add to event loop
+            log<level::ERR>("Error occurred during the sd_event_add_child call",
+                            entry("RC=%d", rc));
+            elog<InternalFailure>();
+        }
+    }
+    else
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
+        elog<InternalFailure>();
+    }
+
+    return ++lastEntryId;
+}
+
+void Manager::createEntry(const fs::path& file)
+{
+    // Dump File Name format obmcdump_ID_EPOCHTIME.EXT
+    static constexpr auto ID_POS = 1;
+    static constexpr auto EPOCHTIME_POS = 2;
+    std::regex file_regex("obmcdump_([0-9]+)_([0-9]+).([a-zA-Z0-9]+)");
+
+    std::smatch match;
+    std::string name = file.filename();
+
+    if (!((std::regex_search(name, match, file_regex)) && (match.size() > 0)))
+    {
+        log<level::ERR>("Invalid Dump file name",
+                        entry("FILENAME=%s", file.filename().c_str()));
+        return;
+    }
+
+    auto idString = match[ID_POS];
+    auto msString = match[EPOCHTIME_POS];
+
+    try
+    {
+        auto id = stoul(idString);
+        // Entry Object path.
+        auto objPath = fs::path(baseEntryPath) / std::to_string(id);
+
+        entries.insert(
+            std::make_pair(id, std::make_unique<bmc::Entry>(
+                                   bus, objPath.c_str(), id, stoull(msString),
+                                   fs::file_size(file), file, *this)));
+    }
+    catch (const std::invalid_argument& e)
+    {
+        log<level::ERR>(e.what());
+        return;
+    }
+}
+
+void Manager::watchCallback(const UserMap& fileInfo)
+{
+    for (const auto& i : fileInfo)
+    {
+        // For any new dump file create dump entry object
+        // and associated inotify watch.
+        if (IN_CLOSE_WRITE == i.second)
+        {
+            removeWatch(i.first);
+
+            createEntry(i.first);
+        }
+        // Start inotify watch on newly created directory.
+        else if ((IN_CREATE == i.second) && fs::is_directory(i.first))
+        {
+            auto watchObj = std::make_unique<Watch>(
+                eventLoop, IN_NONBLOCK, IN_CLOSE_WRITE, EPOLLIN, i.first,
+                std::bind(
+                    std::mem_fn(&phosphor::dump::bmc::Manager::watchCallback),
+                    this, std::placeholders::_1));
+
+            childWatchMap.emplace(i.first, std::move(watchObj));
+        }
+    }
+}
+
+void Manager::removeWatch(const fs::path& path)
+{
+    // Delete Watch entry from map.
+    childWatchMap.erase(path);
+}
+
+void Manager::restore()
+{
+    fs::path dir(dumpDir);
+    if (!fs::exists(dir) || fs::is_empty(dir))
+    {
+        return;
+    }
+
+    // Dump file path: <DUMP_PATH>/<id>/<filename>
+    for (const auto& p : fs::directory_iterator(dir))
+    {
+        auto idStr = p.path().filename().string();
+
+        // Consider only directory's with dump id as name.
+        // Note: As per design one file per directory.
+        if ((fs::is_directory(p.path())) &&
+            std::all_of(idStr.begin(), idStr.end(), ::isdigit))
+        {
+            lastEntryId =
+                std::max(lastEntryId, static_cast<uint32_t>(std::stoul(idStr)));
+            auto fileIt = fs::directory_iterator(p.path());
+            // Create dump entry d-bus object.
+            if (fileIt != fs::end(fileIt))
+            {
+                createEntry(fileIt->path());
+            }
+        }
+    }
+}
+
+size_t Manager::getAllowedSize()
+{
+    using namespace sdbusplus::xyz::openbmc_project::Dump::Create::Error;
+    using Reason = xyz::openbmc_project::Dump::Create::QuotaExceeded::REASON;
+
+    auto size = 0;
+
+    // Get current size of the dump directory.
+    for (const auto& p : fs::recursive_directory_iterator(dumpDir))
+    {
+        if (!fs::is_directory(p))
+        {
+            size += fs::file_size(p);
+        }
+    }
+
+    // Convert size into KB
+    size = size / 1024;
+
+    // Set the Dump size to Maximum  if the free space is greater than
+    // Dump max size otherwise return the available size.
+
+    size = (size > BMC_DUMP_TOTAL_SIZE ? 0 : BMC_DUMP_TOTAL_SIZE - size);
+
+    if (size < BMC_DUMP_MIN_SPACE_REQD)
+    {
+        // Reached to maximum limit
+        elog<QuotaExceeded>(Reason("Not enough space: Delete old dumps"));
+    }
+    if (size > BMC_DUMP_MAX_SIZE)
+    {
+        size = BMC_DUMP_MAX_SIZE;
+    }
+
+    return size;
+}
+
+} // namespace bmc
+} // namespace dump
+} // namespace phosphor