utility:JSON: Create JSON config file loader
Create JSON config file loader utility functions that all fan
applications will use when loading a JSON config file to be parsed.
Change-Id: I9d57dafb96a0fe6a73052f5b3e88055f2c6ef728
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/json_config.hpp b/json_config.hpp
new file mode 100644
index 0000000..55ca85a
--- /dev/null
+++ b/json_config.hpp
@@ -0,0 +1,154 @@
+/**
+ * Copyright © 2020 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.
+ */
+#pragma once
+
+#include "sdbusplus.hpp"
+
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdeventplus/source/signal.hpp>
+
+#include <filesystem>
+#include <fstream>
+
+namespace phosphor::fan
+{
+
+namespace fs = std::filesystem;
+using json = nlohmann::json;
+using namespace phosphor::logging;
+
+constexpr auto confOverridePath = "/etc/phosphor-fan-presence";
+constexpr auto confBasePath = "/usr/share/phosphor-fan-presence";
+constexpr auto confDbusPath = "/xyz/openbmc_project/inventory/system/chassis";
+constexpr auto confDbusIntf =
+ "xyz.openbmc_project.Inventory.Decorator.Compatible";
+constexpr auto confDbusProp = "Names";
+
+class JsonConfig
+{
+ public:
+ /**
+ * Get the json configuration file. The first location found to contain
+ * the json config file for the given fan application is used from the
+ * following locations in order.
+ * 1.) From the confOverridePath location
+ * 2.) From config file found using a property value as a relative
+ * path extension on the base path from the dbus object where:
+ * path = Path set in confDbusPath
+ * interface = Interface set in confDbusIntf
+ * property = Property set in confDbusProp
+ * 3.) *DEFAULT* - From the confBasePath location
+ *
+ * @brief Get the configuration file to be used
+ *
+ * @param[in] bus - The dbus bus object
+ * @param[in] appName - The phosphor-fan-presence application name
+ * @param[in] fileName - Application's configuration file's name
+ *
+ * @return filesystem path
+ * The filesystem path to the configuration file to use
+ */
+ static const fs::path getConfFile(sdbusplus::bus::bus& bus,
+ const std::string& appName,
+ const std::string& fileName)
+ {
+ // Check override location
+ fs::path confFile = fs::path{confOverridePath} / appName / fileName;
+ if (fs::exists(confFile))
+ {
+ return confFile;
+ }
+
+ try
+ {
+ // Retrieve json config relative path location from dbus
+ auto confDbusValue =
+ util::SDBusPlus::getProperty<std::vector<std::string>>(
+ bus, confDbusPath, confDbusIntf, confDbusProp);
+ // Look for a config file at each entry relative to the base
+ // path and use the first one found
+ auto it = std::find_if(
+ confDbusValue.begin(), confDbusValue.end(),
+ [&confFile, &appName, &fileName](auto const& entry) {
+ confFile =
+ fs::path{confBasePath} / appName / entry / fileName;
+ return fs::exists(confFile);
+ });
+ if (it == confDbusValue.end())
+ {
+ // Property exists, but no config file found. Use default base
+ // path
+ confFile = fs::path{confBasePath} / appName / fileName;
+ }
+ }
+ catch (const util::DBusError&)
+ {
+ // Property unavailable, attempt default base path
+ confFile = fs::path{confBasePath} / appName / fileName;
+ }
+
+ if (!fs::exists(confFile))
+ {
+ log<level::ERR>("No JSON config file found",
+ entry("DEFAULT_FILE=%s", confFile.c_str()));
+ throw std::runtime_error("No JSON config file found");
+ }
+
+ return confFile;
+ }
+
+ /**
+ * @brief Load the JSON config file
+ *
+ * @param[in] confFile - File system path of the configuration file to load
+ *
+ * @return Parsed JSON object
+ * The parsed JSON configuration file object
+ */
+ static const json load(const fs::path& confFile)
+ {
+ std::ifstream file;
+ json jsonConf;
+
+ if (fs::exists(confFile))
+ {
+ file.open(confFile);
+ try
+ {
+ jsonConf = json::parse(file);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Failed to parse JSON config file",
+ entry("JSON_FILE=%s", confFile.c_str()),
+ entry("JSON_ERROR=%s", e.what()));
+ throw std::runtime_error("Failed to parse JSON config file");
+ }
+ }
+ else
+ {
+ log<level::ERR>("Unable to open JSON config file",
+ entry("JSON_FILE=%s", confFile.c_str()));
+ throw std::runtime_error("Unable to open JSON config file");
+ }
+
+ return jsonConf;
+ }
+};
+
+} // namespace phosphor::fan