presence: Use shared JSON config file finding & loading header

Use the shared JSON config determination header for loading its
JSON configuration file.

Tested:
    run phosphor-fan-presence-tach can find and load config.json from
    /usr/share/phosphor-fan-presence/presence and
    /etc/phosphor-fan-presence/presence/ in witherspoon qemu

Change-Id: I5c4fdf7d96de7c956ca24b4a5751911cc1bf1f03
Signed-off-by: Jolie Ku <jolie_ku@wistron.com>
diff --git a/presence/json_parser.cpp b/presence/json_parser.cpp
new file mode 100644
index 0000000..83267a7
--- /dev/null
+++ b/presence/json_parser.cpp
@@ -0,0 +1,281 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "json_parser.hpp"
+
+#include "anyof.hpp"
+#include "fallback.hpp"
+#include "gpio.hpp"
+#include "json_config.hpp"
+#include "sdbusplus.hpp"
+#include "tach.hpp"
+
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <string>
+
+namespace phosphor
+{
+namespace fan
+{
+namespace presence
+{
+
+using json = nlohmann::json;
+namespace fs = std::filesystem;
+using namespace phosphor::logging;
+
+policies JsonConfig::_policies;
+const std::map<std::string, methodHandler> JsonConfig::_methods = {
+    {"tach", method::getTach}, {"gpio", method::getGpio}};
+const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
+    {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
+
+JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) : _bus(bus)
+{
+    using config = fan::JsonConfig;
+
+    // Load and process the json configuration
+    process(config::load(config::getConfFile(bus, confAppName, confFileName)));
+}
+
+const policies& JsonConfig::get()
+{
+    return _policies;
+}
+
+void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
+                               const struct signalfd_siginfo* sigInfo)
+{
+    try
+    {
+        using config = fan::JsonConfig;
+
+        // Load and process the json configuration
+        process(
+            config::load(config::getConfFile(_bus, confAppName, confFileName)));
+
+        for (auto& p : _policies)
+        {
+            p->monitor();
+        }
+        log<level::INFO>("Configuration loaded successfully");
+    }
+    catch (std::runtime_error& re)
+    {
+        log<level::ERR>("Error loading config, no config changes made",
+                        entry("LOAD_ERROR=%s", re.what()));
+    }
+}
+
+void JsonConfig::process(const json& jsonConf)
+{
+    policies policies;
+    std::vector<fanPolicy> fans;
+    // Set the expected number of fan entries
+    // to be size of the list of fan json config entries
+    // (Must be done to eliminate vector reallocation of fan references)
+    fans.reserve(jsonConf.size());
+    for (auto& member : jsonConf)
+    {
+        if (!member.contains("name") || !member.contains("path") ||
+            !member.contains("methods") || !member.contains("rpolicy"))
+        {
+            log<level::ERR>("Missing required fan presence properties",
+                            entry("REQUIRED_PROPERTIES=%s",
+                                  "{name, path, methods, rpolicy}"));
+            throw std::runtime_error(
+                "Missing required fan presence properties");
+        }
+
+        // Loop thru the configured methods of presence detection
+        std::vector<std::unique_ptr<PresenceSensor>> sensors;
+        for (auto& method : member["methods"].items())
+        {
+            if (!method.value().contains("type"))
+            {
+                log<level::ERR>(
+                    "Missing required fan presence method type",
+                    entry("FAN_NAME=%s",
+                          member["name"].get<std::string>().c_str()));
+                throw std::runtime_error(
+                    "Missing required fan presence method type");
+            }
+            // The method type of fan presence detection
+            // (Must have a supported function within the method namespace)
+            auto type = method.value()["type"].get<std::string>();
+            std::transform(type.begin(), type.end(), type.begin(), tolower);
+            auto func = _methods.find(type);
+            if (func != _methods.end())
+            {
+                // Call function for method type
+                auto sensor = func->second(fans.size(), method.value());
+                if (sensor)
+                {
+                    sensors.emplace_back(std::move(sensor));
+                }
+            }
+            else
+            {
+                log<level::ERR>(
+                    "Invalid fan presence method type",
+                    entry("FAN_NAME=%s",
+                          member["name"].get<std::string>().c_str()),
+                    entry("METHOD_TYPE=%s", type.c_str()));
+                throw std::runtime_error("Invalid fan presence method type");
+            }
+        }
+        auto fan = std::make_tuple(member["name"], member["path"]);
+        // Create a fan object
+        fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
+
+        // Add fan presence policy
+        auto policy = getPolicy(member["rpolicy"], fans.back());
+        if (policy)
+        {
+            policies.emplace_back(std::move(policy));
+        }
+    }
+
+    // Success, refresh fans and policies lists
+    _fans.clear();
+    _fans.swap(fans);
+
+    _policies.clear();
+    _policies.swap(policies);
+}
+
+std::unique_ptr<RedundancyPolicy>
+    JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
+{
+    if (!rpolicy.contains("type"))
+    {
+        log<level::ERR>(
+            "Missing required fan presence policy type",
+            entry("FAN_NAME=%s",
+                  std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
+            entry("REQUIRED_PROPERTIES=%s", "{type}"));
+        throw std::runtime_error("Missing required fan presence policy type");
+    }
+
+    // The redundancy policy type for fan presence detection
+    // (Must have a supported function within the rpolicy namespace)
+    auto type = rpolicy["type"].get<std::string>();
+    std::transform(type.begin(), type.end(), type.begin(), tolower);
+    auto func = _rpolicies.find(type);
+    if (func != _rpolicies.end())
+    {
+        // Call function for redundancy policy type and return the policy
+        return func->second(fpolicy);
+    }
+    else
+    {
+        log<level::ERR>(
+            "Invalid fan presence policy type",
+            entry("FAN_NAME=%s",
+                  std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
+            entry("RPOLICY_TYPE=%s", type.c_str()));
+        throw std::runtime_error("Invalid fan presence methods policy type");
+    }
+}
+
+/**
+ * Methods of fan presence detection function definitions
+ */
+namespace method
+{
+// Get a constructed presence sensor for fan presence detection by tach
+std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
+{
+    if (!method.contains("sensors") || method["sensors"].size() == 0)
+    {
+        log<level::ERR>("Missing required tach method properties",
+                        entry("FAN_ENTRY=%d", fanIndex),
+                        entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
+        throw std::runtime_error("Missing required tach method properties");
+    }
+
+    std::vector<std::string> sensors;
+    for (auto& sensor : method["sensors"])
+    {
+        sensors.emplace_back(sensor.get<std::string>());
+    }
+
+    return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
+                                                            std::move(sensors));
+}
+
+// Get a constructed presence sensor for fan presence detection by gpio
+std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
+{
+    if (!method.contains("physpath") || !method.contains("devpath") ||
+        !method.contains("key"))
+    {
+        log<level::ERR>(
+            "Missing required gpio method properties",
+            entry("FAN_ENTRY=%d", fanIndex),
+            entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
+        throw std::runtime_error("Missing required gpio method properties");
+    }
+
+    auto physpath = method["physpath"].get<std::string>();
+    auto devpath = method["devpath"].get<std::string>();
+    auto key = method["key"].get<unsigned int>();
+
+    return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(fanIndex, physpath,
+                                                            devpath, key);
+}
+
+} // namespace method
+
+/**
+ * Redundancy policies for fan presence detection function definitions
+ */
+namespace rpolicy
+{
+// Get an `Anyof` redundancy policy for the fan
+std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
+{
+    std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
+    for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
+    {
+        pSensors.emplace_back(*fanSensor);
+    }
+
+    return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
+}
+
+// Get a `Fallback` redundancy policy for the fan
+std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
+{
+    std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
+    for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
+    {
+        // Place in the order given to fallback correctly
+        pSensors.emplace_back(*fanSensor);
+    }
+
+    return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
+}
+
+} // namespace rpolicy
+
+} // namespace presence
+} // namespace fan
+} // namespace phosphor