BMCDump: dump subtype input parameter support

The BMC dump is not accepting any parameters to specify the type of
dump or level of data to be collected. It was always creating a default
user-requested dump.

This commit allows for the specification of the dump type during the
dump creation process. A new table has been added in a new header file
which stores the valid dump types. When a new dump is requested, the
specified dump type is verified against this table. If no dump type is
mentioned, a user-requested BMC dump is created for backward
compatibility. If an invalid dump type is mentioned, an Invalid
Argument exception is thrown.

Tested:
- Create BMC dump with no arguments
- Create BMC dump with type as user
- Create a error log dump and make sure other types of dumps
  which is using internal interface is not impacted.
- Built with master and p10bmc

Change-Id: I79f68be6ac6892ac7754b7221db64c22330b1822
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
diff --git a/dump_manager_bmc.cpp b/dump_manager_bmc.cpp
index 8161f41..6c99099 100644
--- a/dump_manager_bmc.cpp
+++ b/dump_manager_bmc.cpp
@@ -4,6 +4,7 @@
 
 #include "bmc_dump_entry.hpp"
 #include "dump_internal.hpp"
+#include "dump_types.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 #include "xyz/openbmc_project/Dump/Create/error.hpp"
 
@@ -31,6 +32,7 @@
 using namespace phosphor::logging;
 
 bool Manager::fUserDumpInProgress = false;
+constexpr auto BMC_DUMP = "BMC_DUMP";
 
 namespace internal
 {
@@ -50,11 +52,6 @@
         lg2::warning("BMC dump accepts not more than 2 additional parameters");
     }
 
-    if (Manager::fUserDumpInProgress == true)
-    {
-        elog<sdbusplus::xyz::openbmc_project::Common::Error::Unavailable>();
-    }
-
     // Get the originator id and type from params
     std::string originatorId;
     originatorTypes originatorType;
@@ -62,8 +59,29 @@
     phosphor::dump::extractOriginatorProperties(params, originatorId,
                                                 originatorType);
 
-    std::vector<std::string> paths;
-    auto id = captureDump(Type::UserRequested, paths);
+    using CreateParameters =
+        sdbusplus::common::xyz::openbmc_project::dump::Create::CreateParameters;
+
+    DumpTypes dumpType = DumpTypes::USER;
+    std::string type = extractParameter<std::string>(
+        convertCreateParametersToString(CreateParameters::DumpType), params);
+    if (!type.empty())
+    {
+        dumpType = validateDumpType(type, BMC_DUMP);
+    }
+    std::string path = extractParameter<std::string>(
+        convertCreateParametersToString(CreateParameters::FilePath), params);
+
+    if ((Manager::fUserDumpInProgress == true) && (dumpType == DumpTypes::USER))
+    {
+        lg2::info("Another user initiated dump in progress");
+        elog<sdbusplus::xyz::openbmc_project::Common::Error::Unavailable>();
+    }
+
+    lg2::info("Initiating new BMC dump with type: {TYPE} path: {PATH}", "TYPE",
+              dumpTypeToString(dumpType).value(), "PATH", path);
+
+    auto id = captureDump(dumpType, path);
 
     // Entry Object path.
     auto objPath = std::filesystem::path(baseEntryPath) / std::to_string(id);
@@ -89,13 +107,23 @@
         elog<InternalFailure>();
     }
 
-    Manager::fUserDumpInProgress = true;
+    if (dumpType == DumpTypes::USER)
+    {
+        Manager::fUserDumpInProgress = true;
+    }
     return objPath.string();
 }
 
 uint32_t Manager::captureDump(Type type,
                               const std::vector<std::string>& fullPaths)
 {
+    // get dreport type map entry
+    auto tempType = TypeMap.find(type);
+    return captureDump(stringToDumpType(tempType->second).value(),
+                       fullPaths.front());
+}
+uint32_t Manager::captureDump(DumpTypes type, const std::string& path)
+{
     // Get Dump size.
     auto size = getAllowedSize();
 
@@ -107,12 +135,10 @@
         auto id = std::to_string(lastEntryId + 1);
         dumpPath /= id;
 
-        // get dreport type map entry
-        auto tempType = TypeMap.find(type);
+        auto strType = dumpTypeToString(type).value();
         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);
+              path.empty() ? "" : path.c_str(), "-t", strType.c_str(), nullptr);
 
         // dreport script execution is failed.
         auto error = errno;
@@ -124,7 +150,7 @@
     else if (pid > 0)
     {
         Child::Callback callback = [this, type, pid](Child&, const siginfo_t*) {
-            if (type == Type::UserRequested)
+            if (type == DumpTypes::USER)
             {
                 lg2::info("User initiated dump completed, resetting flag");
                 Manager::fUserDumpInProgress = false;
diff --git a/dump_manager_bmc.hpp b/dump_manager_bmc.hpp
index 6dbb7ad..22e688d 100644
--- a/dump_manager_bmc.hpp
+++ b/dump_manager_bmc.hpp
@@ -113,6 +113,14 @@
      */
     uint32_t captureDump(Type type, const std::vector<std::string>& fullPaths);
 
+    /** @brief Capture BMC Dump based on the Dump type.
+     *  @param[in] type - Type of the dump to pass to dreport
+     *  @param[in] path - An absolute path to the file
+     *             to be included as part of Dump package.
+     *  @return id - The Dump entry id number.
+     */
+    uint32_t captureDump(DumpTypes type, const std::string& path);
+
     /** @brief Remove specified watch object pointer from the
      *        watch map and associated entry from the map.
      *        @param[in] path - unique identifier of the map
diff --git a/dump_types.cpp b/dump_types.cpp
new file mode 100644
index 0000000..539bca4
--- /dev/null
+++ b/dump_types.cpp
@@ -0,0 +1,77 @@
+#include "dump_types.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace dump
+{
+DUMP_TYPE_TABLE dumpTypeTable = {
+    {"xyz.openbmc_project.Dump.Create.DumpType.UserRequested",
+     {DumpTypes::USER, "BMC_DUMP"}}};
+
+DUMP_TYPE_TO_STRING_MAP dumpTypeToStringMap = {
+    {DumpTypes::USER, "user"},
+};
+
+std::optional<std::string> dumpTypeToString(const DumpTypes& dumpType)
+{
+    auto it = dumpTypeToStringMap.find(dumpType);
+    if (it != dumpTypeToStringMap.end())
+    {
+        return it->second;
+    }
+    return std::nullopt;
+}
+
+std::optional<DumpTypes> stringToDumpType(const std::string& str)
+{
+    auto it = std::ranges::find_if(dumpTypeToStringMap,
+                                   [&str](const auto& pair) {
+        return pair.second == str;
+    });
+
+    if (it != dumpTypeToStringMap.end())
+    {
+        return it->first;
+    }
+    return std::nullopt;
+}
+
+DumpTypes validateDumpType(const std::string& type, const std::string& category)
+{
+    using namespace phosphor::logging;
+    using InvalidArgument =
+        sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+    using Argument = xyz::openbmc_project::Common::InvalidArgument;
+    // Dump type user will be return if type is empty
+    DumpTypes dumpType = DumpTypes::USER;
+    if (type.empty())
+    {
+        return dumpType;
+    }
+
+    // Find any matching dump collection type for the category
+    auto it = std::find_if(dumpTypeTable.begin(), dumpTypeTable.end(),
+                           [&](const auto& pair) {
+        return pair.first == type && pair.second.second == category;
+    });
+
+    if (it != dumpTypeTable.end())
+    {
+        dumpType = it->second.first;
+    }
+    else
+    {
+        lg2::error("An invalid dump type: {TYPE} passed", "TYPE", type);
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("BMC_DUMP_TYPE"),
+                              Argument::ARGUMENT_VALUE(type.c_str()));
+    }
+    return dumpType;
+}
+
+} // namespace dump
+} // namespace phosphor
diff --git a/dump_types.hpp b/dump_types.hpp
new file mode 100644
index 0000000..be1e92d
--- /dev/null
+++ b/dump_types.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <optional>
+#include <ranges>
+#include <string>
+#include <unordered_map>
+
+namespace phosphor
+{
+namespace dump
+{
+// Overall dump category for example BMC dump
+using DUMP_CATEGORY = std::string;
+
+// Dump type
+using DUMP_TYPE = std::string;
+
+// Dump collection indicator
+using DUMP_COLLECTION_TYPE = std::string;
+
+// Dump types
+enum class DumpTypes
+{
+    USER,
+};
+
+// A table of dump types
+using DUMP_TYPE_TABLE =
+    std::unordered_map<DUMP_TYPE, std::pair<DumpTypes, DUMP_CATEGORY>>;
+
+// Mapping between dump type and dump collection type string
+using DUMP_TYPE_TO_STRING_MAP =
+    std::unordered_map<DumpTypes, DUMP_COLLECTION_TYPE>;
+
+/**
+ * @brief Converts a DumpTypes enum value to dump name.
+ *
+ * @param[in] dumpType The DumpTypes value to be converted.
+ * @return Name of the dump as string, std::nullopt if not found.
+ */
+std::optional<std::string> dumpTypeToString(const DumpTypes& dumpType);
+
+/**
+ * @brief Converts dump name to its corresponding DumpTypes enum value.
+ *
+ * @param[in] str The string to be converted to a DumpTypes value.
+ * @return The DumpTypes value that corresponds to the name or std::nullopt if
+ * not found.
+ */
+std::optional<DumpTypes> stringToDumpType(const std::string& str);
+
+/**
+ * @brief Validates dump type and returns corresponding collection type
+ *
+ * This function checks the provided dump type against the specified category.
+ * If the dump type is empty, it defaults to "user". If the dump type does not
+ * exist or does not match with the specified category, it logs an error and
+ * throws an InvalidArgument exception.
+ *
+ * @param[in] type - The dump type to be validated.
+ * @param[in] category - The category to match against the dump type.
+ *
+ * @return The corresponding dump collection type if the dump type and category
+ * match an entry in the dumpTypeTable. If the type is empty or does not match
+ * any entry, it returns "user".
+ *
+ * @throws InvalidArgument - Thrown if the type does not match the specified
+ * category or does not exist in the table.
+ */
+DumpTypes validateDumpType(const std::string& type,
+                           const std::string& category);
+
+} // namespace dump
+} // namespace phosphor
diff --git a/dump_utils.cpp b/dump_utils.cpp
index 792138c..da703ec 100644
--- a/dump_utils.cpp
+++ b/dump_utils.cpp
@@ -1,5 +1,7 @@
 #include "dump_utils.hpp"
 
+#include "dump_types.hpp"
+
 #include <phosphor-logging/lg2.hpp>
 
 namespace phosphor
diff --git a/dump_utils.hpp b/dump_utils.hpp
index 6d39bef..01e4166 100644
--- a/dump_utils.hpp
+++ b/dump_utils.hpp
@@ -1,5 +1,6 @@
 #pragma once
 #include "dump_manager.hpp"
+#include "dump_types.hpp"
 
 #include <systemd/sd-event.h>
 #include <unistd.h>
@@ -300,5 +301,39 @@
     return (getHostState() == HostState::Quiesced);
 }
 
+/** @brief Extract the dump create parameters
+ *  @param[in] key - The name of the parameter
+ *  @param[in] params - The map of parameters passed as input
+ *
+ *  @return On success, a std::optional containing the value of the parameter
+ * (of type T). On failure (key not found in the map or the value is not of type
+ * T), returns an empty std::optional.
+ */
+template <typename T>
+T extractParameter(const std::string& key,
+                   phosphor::dump::DumpCreateParams& params)
+{
+    using InvalidArgument =
+        sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+    using Argument = xyz::openbmc_project::Common::InvalidArgument;
+
+    auto it = params.find(key);
+    if (it != params.end())
+    {
+        const auto& [foundKey, variantValue] = *it;
+        if (std::holds_alternative<T>(variantValue))
+        {
+            return std::get<T>(variantValue);
+        }
+        else
+        {
+            lg2::error("An invalid input passed for key: {KEY}", "KEY", key);
+            elog<InvalidArgument>(Argument::ARGUMENT_NAME(key.c_str()),
+                                  Argument::ARGUMENT_VALUE("INVALID INPUT"));
+        }
+    }
+    return T{};
+}
+
 } // namespace dump
 } // namespace phosphor
diff --git a/meson.build b/meson.build
index 1ba3e32..5696b45 100644
--- a/meson.build
+++ b/meson.build
@@ -160,6 +160,7 @@
         'dump_offload.cpp',
         'dump_manager_faultlog.cpp',
         'faultlog_dump_entry.cpp',
+        'dump_types.cpp'
     ]
 
 phosphor_dump_manager_dependency = [