regulators: Load JSON config file

There are three basic steps to loading the JSON configuration file
for the phosphor-regulators application:
* Find the correct base name using a D-Bus query
* Find the absolute path to the file by searching two directories
* Parse the config file and store the resulting C++ objects

The base name is currently hard-coded while waiting for an EntityManager
enhancement to be completed.  So the first step is partially done.

This commit implements the second and third steps.

Some minor changes were also made to conform these files to the coding
conventions used in the rest of the phosphor-regulators application.

Tested:
  * See test plan:
    https://gist.github.com/smccarney/5d164131a84717b3f2a8835ae0fc466b

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Ie59d473d1fec5ce89c859621eb27579afdcde7aa
diff --git a/phosphor-regulators/src/manager.cpp b/phosphor-regulators/src/manager.cpp
index 9e2f031..d172f27 100644
--- a/phosphor-regulators/src/manager.cpp
+++ b/phosphor-regulators/src/manager.cpp
@@ -16,18 +16,41 @@
 
 #include "manager.hpp"
 
+#include "chassis.hpp"
+#include "config_file_parser.hpp"
+#include "exception_utils.hpp"
+#include "journal.hpp"
+#include "rule.hpp"
 #include "utility.hpp"
 
 #include <sdbusplus/bus.hpp>
 
 #include <chrono>
+#include <exception>
+#include <stdexcept>
+#include <tuple>
+#include <utility>
 #include <variant>
 
 namespace phosphor::power::regulators
 {
 
+namespace fs = std::filesystem;
+
+/**
+ * Standard configuration file directory.  This directory is part of the
+ * firmware install image.  It contains the standard version of the config file.
+ */
+const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"};
+
+/**
+ * Test configuration file directory.  This directory can contain a test version
+ * of the config file.  The test version will override the standard version.
+ */
+const fs::path testConfigFileDir{"/etc/phosphor-regulators"};
+
 Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) :
-    ManagerObject(bus, objPath, true), bus(bus), eventLoop(event), fileName("")
+    ManagerObject{bus, objPath, true}, bus{bus}, eventLoop{event}
 {
     /* Temporarily comment out until D-Bus interface is defined and available.
         // Subscribe to interfacesAdded signal for filename property
@@ -49,7 +72,7 @@
 
     if (!fileName.empty())
     {
-        // TODO Load & parse JSON configuration data file
+        loadConfigFile();
     }
 
     // Obtain dbus service name
@@ -88,7 +111,10 @@
 void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
                             const struct signalfd_siginfo* /*sigInfo*/)
 {
-    // TODO Reload and process the configuration data
+    if (!fileName.empty())
+    {
+        loadConfigFile();
+    }
 }
 
 void Manager::signalHandler(sdbusplus::message::message& msg)
@@ -123,7 +149,10 @@
         }
         // Set fileName and call parse json function
         setFileName(std::get<std::string>(itProp->second));
-        // TODO Load & parse JSON configuration data file
+        if (!fileName.empty())
+        {
+            loadConfigFile();
+        }
     }
 }
 
@@ -151,4 +180,51 @@
     return fileName;
 }
 
+fs::path Manager::findConfigFile()
+{
+    // First look in the test directory
+    fs::path pathName{testConfigFileDir / fileName};
+    if (!fs::exists(pathName))
+    {
+        // Look in the standard directory
+        pathName = standardConfigFileDir / fileName;
+        if (!fs::exists(pathName))
+        {
+            throw std::runtime_error{"Configuration file does not exist: " +
+                                     pathName.string()};
+        }
+    }
+
+    return pathName;
+}
+
+void Manager::loadConfigFile()
+{
+    try
+    {
+        // Find the absolute path to the config file
+        fs::path pathName = findConfigFile();
+
+        // Log info message in journal; config file path is important
+        journal::logInfo("Loading configuration file " + pathName.string());
+
+        // Parse the config file
+        std::vector<std::unique_ptr<Rule>> rules{};
+        std::vector<std::unique_ptr<Chassis>> chassis{};
+        std::tie(rules, chassis) = config_file_parser::parse(pathName);
+
+        // Store config file information in a new System object.  The old System
+        // object, if any, is automatically deleted.
+        system = std::make_unique<System>(std::move(rules), std::move(chassis));
+    }
+    catch (const std::exception& e)
+    {
+        // Log error messages in journal
+        exception_utils::log(e);
+        journal::logErr("Unable to load configuration file");
+
+        // TODO: Create error log entry
+    }
+}
+
 } // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/src/manager.hpp b/phosphor-regulators/src/manager.hpp
index c2d78f1..1763f92 100644
--- a/phosphor-regulators/src/manager.hpp
+++ b/phosphor-regulators/src/manager.hpp
@@ -1,5 +1,22 @@
+/**
+ * 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 "system.hpp"
+
 #include <interfaces/manager_interface.hpp>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/bus/match.hpp>
@@ -9,6 +26,10 @@
 #include <sdeventplus/utility/timer.hpp>
 
 #include <algorithm>
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <vector>
 
 namespace phosphor::power::regulators
 {
@@ -39,75 +60,50 @@
      * Constructor
      * Creates a manager over the regulators.
      *
-     * @param[in] bus - the dbus bus
-     * @param[in] event - the sdevent event
+     * @param bus the dbus bus
+     * @param event the sdevent event
      */
     Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event);
 
     /**
-     * @brief Overridden manager object's configure method
+     * Overridden manager object's configure method
      */
     void configure() override;
 
     /**
-     * @brief Overridden manager object's monitor method
+     * Overridden manager object's monitor method
      *
-     * @param[in] enable - Enable or disable regulator monitoring
+     * @param enable Enable or disable regulator monitoring
      */
     void monitor(bool enable) override;
 
     /**
-     * @brief Timer expired callback function
+     * Timer expired callback function
      */
     void timerExpired();
 
     /**
-     * @brief Callback function to handle receiving a HUP signal
+     * Callback function to handle receiving a HUP signal
      * to reload the configuration data.
      *
-     * @param[in] sigSrc - sd_event_source signal wrapper
-     * @param[in] sigInfo - signal info on signal fd
+     * @param sigSrc sd_event_source signal wrapper
+     * @param sigInfo signal info on signal fd
      */
     void sighupHandler(sdeventplus::source::Signal& sigSrc,
                        const struct signalfd_siginfo* sigInfo);
 
     /**
-     * @brief Callback function to handle interfacesAdded dbus signals
+     * Callback function to handle interfacesAdded dbus signals
      *
-     * @param[in] msg - Expanded sdbusplus message data
+     * @param msg Expanded sdbusplus message data
      */
     void signalHandler(sdbusplus::message::message& msg);
 
   private:
     /**
-     * The dbus bus
-     */
-    sdbusplus::bus::bus& bus;
-
-    /**
-     * Event to loop on
-     */
-    sdeventplus::Event eventLoop;
-
-    /**
-     * List of event timers
-     */
-    std::vector<Timer> timers;
-
-    /**
-     * List of dbus signal matches
-     */
-    std::vector<std::unique_ptr<sdbusplus::bus::match::match>> signals;
-
-    /**
-     * JSON configuration data filename
-     */
-    std::string fileName;
-
-    /**
-     * @brief Set the JSON configuration data filename
+     * Set the JSON configuration data filename
      *
-     * @param[in] fName = filename without `.json` extension
+     * @param fName filename without `.json` extension
      */
     inline void setFileName(const std::string& fName)
     {
@@ -121,11 +117,74 @@
     };
 
     /**
-     * @brief Get the JSON configuration data filename from dbus
+     * Get the JSON configuration data filename from dbus
      *
-     * @return - JSON configuration data filename
+     * @return JSON configuration data filename
      */
     const std::string getFileNameDbus();
+
+    /**
+     * Finds the JSON configuration file.
+     *
+     * Looks for the config file in the test directory and standard directory.
+     *
+     * Throws an exception if the file cannot be found or a file system error
+     * occurs.
+     *
+     * The base name of the config file must have already been obtained and
+     * stored in the fileName data member.
+     *
+     * @return absolute path to config file
+     */
+    std::filesystem::path findConfigFile();
+
+    /**
+     * Loads the JSON configuration file.
+     *
+     * Looks for the config file in the test directory and standard directory.
+     *
+     * If the config file is found, it is parsed and the resulting information
+     * is stored in the system data member.
+     *
+     * If the config file cannot be found or parsing fails, an error is logged.
+     *
+     * The base name of the config file must have already been obtained and
+     * stored in the fileName data member.
+     */
+    void loadConfigFile();
+
+    /**
+     * The dbus bus
+     */
+    sdbusplus::bus::bus& bus;
+
+    /**
+     * Event to loop on
+     */
+    sdeventplus::Event eventLoop;
+
+    /**
+     * List of event timers
+     */
+    std::vector<Timer> timers{};
+
+    /**
+     * List of dbus signal matches
+     */
+    std::vector<std::unique_ptr<sdbusplus::bus::match::match>> signals{};
+
+    /**
+     * JSON configuration file base name.
+     */
+    std::string fileName{};
+
+    /**
+     * Computer system being controlled and monitored by the BMC.
+     *
+     * Contains the information loaded from the JSON configuration file.
+     * Contains nullptr if the configuration file has not been loaded.
+     */
+    std::unique_ptr<System> system{};
 };
 
 } // namespace phosphor::power::regulators