Find the search modifier to use

The search modifier comes from the AdditionalData
property.  The modifier can come from a different
entry in that property depending on the error.

For example, it may be a device path, or an inventory
item path, or a hostboot procedure ID.

Try each option until one is found.  That's probably OK
if one isn't found as some policy entries don't need a
modifier, but there's no way to know ahead of time.

Change-Id: Iad4f6e919f61b7ad507e80448e47bdc32998fab4
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/policy_find.cpp b/policy_find.cpp
index 877e5b7..daf872c 100644
--- a/policy_find.cpp
+++ b/policy_find.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 #include <phosphor-logging/log.hpp>
+#include <sstream>
 #include "policy_find.hpp"
 
 namespace ibm
@@ -50,6 +51,32 @@
 }
 
 /**
+ * Finds a value in the AdditionalData property, which is
+ * an array of strings in the form of:
+ *
+ *    NAME=VALUE
+ *
+ * @param[in] additionalData - the AdditionalData property contents
+ * @param[in] name - the name of the value to find
+ *
+ * @return optional<std::string> - the data value
+ */
+optional_ns::optional<std::string> getAdditionalDataItem(
+        const std::vector<std::string>& additionalData,
+        const std::string& name)
+{
+    for (const auto& item : additionalData)
+    {
+        if (item.find(name+"=") != std::string::npos)
+        {
+            return item.substr(item.find('=') + 1);
+        }
+    }
+
+    return {};
+}
+
+/**
  * Returns the search modifier to use.
  *
  * The modifier is used when the error name itself isn't granular
@@ -67,18 +94,77 @@
 auto getSearchModifier(
         const DbusPropertyMap& properties)
 {
-    std::string modifier;
+    // The modifier may be one of several things within the
+    // AdditionalData property.  Try them all until one
+    // is found.
 
-    auto data = getProperty<std::vector<std::string>>(
-            properties,
-            "AdditionalData");
+    auto data =
+        getProperty<std::vector<std::string>>(properties, "AdditionalData");
 
-    if (data)
+    if (!data)
     {
-        //TODO
+        return std::string{};
     }
 
-    return modifier;
+    // AdditionalData fields where the value is the modifier
+    static const std::vector<std::string> ADFields{"CALLOUT_INVENTORY_PATH",
+                                                   "RAIL_NAME", "INPUT_NAME"};
+
+    optional_ns::optional<std::string> mod;
+    for (const auto& field : ADFields)
+    {
+        mod = getAdditionalDataItem(*data, field);
+        if (mod && !(*mod).empty())
+        {
+            return *mod;
+        }
+    }
+
+    // Next are the AdditionalData fields where the value needs
+    // to be massaged to get the modifier.
+
+    // A device path, but we only care about the type
+    mod = getAdditionalDataItem(*data, "CALLOUT_DEVICE_PATH");
+    if (mod)
+    {
+        // The table only handles I2C and FSI
+        if ((*mod).find("i2c") != std::string::npos)
+        {
+            return std::string{"I2C"};
+        }
+        else if ((*mod).find("fsi") != std::string::npos)
+        {
+            return std::string{"FSI"};
+        }
+    }
+
+    // A hostboot procedure ID
+    mod = getAdditionalDataItem(*data, "PROCEDURE");
+    if (mod)
+    {
+        // Convert decimal (e.g. 109) to hex (e.g. 6D)
+        std::ostringstream stream;
+        try
+        {
+            stream << std::hex << std::stoul((*mod).c_str());
+            auto value = stream.str();
+
+            if (!value.empty())
+            {
+                std::transform(
+                        value.begin(), value.end(), value.begin(), toupper);
+                return value;
+            }
+        }
+        catch (std::exception& e)
+        {
+            using namespace phosphor::logging;
+            log<level::ERR>("Invalid PROCEDURE value found",
+                            entry("PROCEDURE=%s", *mod));
+        }
+    }
+
+    return std::string{};
 }
 
 PolicyProps find(