move entity_map_json to libipmid

entity_map_json is used by dbus-sdr sensor handling so it needs to be in
a common location or loading order matters. This change moves the file
from the ipmi20 provider library to libipmi, a common library.

Tested: Found that even though the dbus-sdr provider loads before
        ipmi20, it loads properly without missing symbols.

Change-Id: I9ab6833c78e6f3c89c02cf998ce0a36353059c3d
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/libipmid/entity_map_json.cpp b/libipmid/entity_map_json.cpp
new file mode 100644
index 0000000..76fbb25
--- /dev/null
+++ b/libipmid/entity_map_json.cpp
@@ -0,0 +1,119 @@
+#include <ipmid/entity_map_json.hpp>
+#include <ipmid/types.hpp>
+#include <nlohmann/json.hpp>
+
+#include <exception>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace ipmi
+{
+namespace sensor
+{
+
+EntityInfoMapContainer* EntityInfoMapContainer::getContainer()
+{
+    static std::unique_ptr<EntityInfoMapContainer> instance;
+
+    if (!instance)
+    {
+        /* TODO: With multi-threading this would all need to be locked so
+         * the first thread to hit it would set it up.
+         */
+        EntityInfoMap builtEntityMap = buildEntityMapFromFile();
+        instance = std::unique_ptr<EntityInfoMapContainer>(
+            new EntityInfoMapContainer(builtEntityMap));
+    }
+
+    return instance.get();
+}
+
+const EntityInfoMap& EntityInfoMapContainer::getIpmiEntityRecords()
+{
+    return entityRecords;
+}
+
+EntityInfoMap buildEntityMapFromFile()
+{
+    const char* entityMapJsonFilename =
+        "/usr/share/ipmi-providers/entity-map.json";
+    EntityInfoMap builtMap;
+
+    std::ifstream mapFile(entityMapJsonFilename);
+    if (!mapFile.is_open())
+    {
+        return builtMap;
+    }
+
+    auto data = nlohmann::json::parse(mapFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        return builtMap;
+    }
+
+    return buildJsonEntityMap(data);
+}
+
+EntityInfoMap buildJsonEntityMap(const nlohmann::json& data)
+{
+    EntityInfoMap builtMap;
+
+    if (data.type() != nlohmann::json::value_t::array)
+    {
+        return builtMap;
+    }
+
+    try
+    {
+        for (const auto& entry : data)
+        {
+            /* It's an array entry with the following fields: id,
+             * containerEntityId, containerEntityInstance, isList, isLinked,
+             * entities[4]
+             */
+            EntityInfo obj;
+            Id recordId = entry.at("id").get<Id>();
+            obj.containerEntityId =
+                entry.at("containerEntityId").get<uint8_t>();
+            obj.containerEntityInstance =
+                entry.at("containerEntityInstance").get<uint8_t>();
+            obj.isList = entry.at("isList").get<bool>();
+            obj.isLinked = entry.at("isLinked").get<bool>();
+
+            auto jsonEntities = entry.at("entities");
+
+            if (jsonEntities.type() != nlohmann::json::value_t::array)
+            {
+                throw std::runtime_error(
+                    "Invalid type for entities entry, must be array");
+            }
+            if (jsonEntities.size() != obj.containedEntities.size())
+            {
+                throw std::runtime_error(
+                    "Entities must be in pairs of " +
+                    std::to_string(obj.containedEntities.size()));
+            }
+
+            for (std::size_t i = 0; i < obj.containedEntities.size(); i++)
+            {
+                obj.containedEntities[i] = std::make_pair(
+                    jsonEntities[i].at("id").get<uint8_t>(),
+                    jsonEntities[i].at("instance").get<uint8_t>());
+            }
+
+            builtMap.insert({recordId, obj});
+        }
+    }
+    catch (const std::exception& e)
+    {
+        /* If any entry is invalid, the entire file cannot be trusted. */
+        builtMap.clear();
+    }
+
+    return builtMap;
+}
+
+} // namespace sensor
+} // namespace ipmi
diff --git a/libipmid/meson.build b/libipmid/meson.build
index 3a8042c..0d6360c 100644
--- a/libipmid/meson.build
+++ b/libipmid/meson.build
@@ -6,13 +6,22 @@
   systemd,
 ]
 
+entity_map_json_lib = static_library(
+  'entity_map_json',
+  'entity_map_json.cpp',
+  include_directories: root_inc,
+  dependencies: [nlohmann_json_dep, sdbusplus_dep],
+  implicit_include_directories: false)
+
+entity_map_json_dep = declare_dependency(link_with: entity_map_json_lib)
+
 libipmid = library(
   'ipmid',
   'sdbus-asio.cpp',
   'signals.cpp',
   'systemintf-sdbus.cpp',
   'utils.cpp',
-  dependencies: ipmid_pre,
+  dependencies: [ipmid_pre, entity_map_json_dep],
   version: meson.project_version(),
   include_directories: root_inc,
   install: true,