control: Load config file(s) when available

Fan control's config file(s) are loaded when the fan control service
starts if found either in the override location, default location, or
the compatible interface is available. When the required config file(s)
are not found at startup, it waits to receive the interfacesAdded signal
that the compatible interface is available and then loads its config
file(s).

If at any point another interfacesAdded signal for the compatible
interface is received after successfully loading its config file(s),
this signal is ignored. So the only method to reload the config file(s)
is by providing the SIGHUP signal.

Change-Id: I14f93a684bb00a6a32f8cb228cbfb9ce3f022c92
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/manager.cpp b/control/json/manager.cpp
index 4ba61e7..e3de8dc 100644
--- a/control/json/manager.cpp
+++ b/control/json/manager.cpp
@@ -58,14 +58,12 @@
 
 Manager::Manager(const sdeventplus::Event& event) :
     _bus(util::SDBusPlus::getBus()), _event(event),
-    _mgr(util::SDBusPlus::getBus(), CONTROL_OBJPATH),
+    _mgr(util::SDBusPlus::getBus(), CONTROL_OBJPATH), _loadAllowed(true),
     _powerState(std::make_unique<PGoodState>(
         util::SDBusPlus::getBus(),
         std::bind(std::mem_fn(&Manager::powerStateChanged), this,
                   std::placeholders::_1)))
-{
-    load();
-}
+{}
 
 void Manager::sighupHandler(sdeventplus::source::Signal&,
                             const struct signalfd_siginfo*)
@@ -78,11 +76,13 @@
 
     try
     {
+        _loadAllowed = true;
         load();
     }
     catch (std::runtime_error& re)
     {
         // Restore saved available and active profiles
+        _loadAllowed = false;
         _profiles.swap(profiles);
         _activeProfiles.swap(activeProfiles);
         log<level::ERR>("Error reloading configs, no changes made",
@@ -92,51 +92,57 @@
 
 void Manager::load()
 {
-    // Load the available profiles and which are active
-    setProfiles();
-
-    // Load the zone configurations
-    auto zones = getConfig<Zone>(false, _event, this);
-    // Load the fan configurations and move each fan into its zone
-    auto fans = getConfig<Fan>(false);
-    for (auto& fan : fans)
+    if (_loadAllowed)
     {
-        configKey fanProfile =
-            std::make_pair(fan.second->getZone(), fan.first.second);
-        auto itZone = std::find_if(
-            zones.begin(), zones.end(), [&fanProfile](const auto& zone) {
-                return Manager::inConfig(fanProfile, zone.first);
-            });
-        if (itZone != zones.end())
+        // Load the available profiles and which are active
+        setProfiles();
+
+        // Load the zone configurations
+        auto zones = getConfig<Zone>(false, _event, this);
+        // Load the fan configurations and move each fan into its zone
+        auto fans = getConfig<Fan>(false);
+        for (auto& fan : fans)
         {
-            if (itZone->second->getTarget() != fan.second->getTarget() &&
-                fan.second->getTarget() != 0)
+            configKey fanProfile =
+                std::make_pair(fan.second->getZone(), fan.first.second);
+            auto itZone = std::find_if(
+                zones.begin(), zones.end(), [&fanProfile](const auto& zone) {
+                    return Manager::inConfig(fanProfile, zone.first);
+                });
+            if (itZone != zones.end())
             {
-                // Update zone target to current target of the fan in the
-                // zone
-                itZone->second->setTarget(fan.second->getTarget());
+                if (itZone->second->getTarget() != fan.second->getTarget() &&
+                    fan.second->getTarget() != 0)
+                {
+                    // Update zone target to current target of the fan in the
+                    // zone
+                    itZone->second->setTarget(fan.second->getTarget());
+                }
+                itZone->second->addFan(std::move(fan.second));
             }
-            itZone->second->addFan(std::move(fan.second));
         }
+
+        // Load any events configured
+        auto events = getConfig<Event>(true, this, zones);
+
+        // Enable zones
+        _zones = std::move(zones);
+        std::for_each(_zones.begin(), _zones.end(),
+                      [](const auto& entry) { entry.second->enable(); });
+
+        // Clear current timers and signal subscriptions before enabling events
+        // To save reloading services and/or objects into cache, do not clear
+        // cache
+        _timers.clear();
+        _signals.clear();
+
+        // Enable events
+        _events = std::move(events);
+        std::for_each(_events.begin(), _events.end(),
+                      [](const auto& entry) { entry.second->enable(); });
+
+        _loadAllowed = false;
     }
-
-    // Load any events configured
-    auto events = getConfig<Event>(true, this, zones);
-
-    // Enable zones
-    _zones = std::move(zones);
-    std::for_each(_zones.begin(), _zones.end(),
-                  [](const auto& entry) { entry.second->enable(); });
-
-    // Clear current timers and signal subscriptions before enabling events
-    // To save reloading services and/or objects into cache, do not clear cache
-    _timers.clear();
-    _signals.clear();
-
-    // Enable events
-    _events = std::move(events);
-    std::for_each(_events.begin(), _events.end(),
-                  [](const auto& entry) { entry.second->enable(); });
 }
 
 void Manager::powerStateChanged(bool powerStateOn)
diff --git a/control/json/manager.hpp b/control/json/manager.hpp
index ce1c869..df6edb3 100644
--- a/control/json/manager.hpp
+++ b/control/json/manager.hpp
@@ -422,6 +422,16 @@
         return _powerState->isPowerOn();
     }
 
+    /**
+     * @brief Load all the fan control JSON configuration files
+     *
+     * This is where all the fan control JSON configuration files are parsed and
+     * loaded into their associated objects. Anything that needs to be done when
+     * the Manager object is constructed or handling a SIGHUP to reload the
+     * configurations needs to be done here.
+     */
+    void load();
+
   private:
     /* The sdbusplus bus object to use */
     sdbusplus::bus::bus& _bus;
@@ -432,6 +442,9 @@
     /* The sdbusplus manager object to set the ObjectManager interface */
     sdbusplus::server::manager::manager _mgr;
 
+    /* Whether loading the config files is allowed or not */
+    bool _loadAllowed;
+
     /* The system's power state determination object */
     std::unique_ptr<PowerState> _powerState;
 
@@ -466,16 +479,6 @@
     std::map<configKey, std::unique_ptr<Event>> _events;
 
     /**
-     * @brief Load all the fan control JSON configuration files
-     *
-     * This is where all the fan control JSON configuration files are parsed and
-     * loaded into their associated objects. Anything that needs to be done when
-     * the Manager object is constructed or handling a SIGHUP to reload the
-     * configurations needs to be done here.
-     */
-    void load();
-
-    /**
      * @brief Callback for power state changes
      *
      * @param[in] powerStateOn - Whether the power state is on or not
diff --git a/control/main.cpp b/control/main.cpp
index 9c7f8a2..9284ae4 100644
--- a/control/main.cpp
+++ b/control/main.cpp
@@ -71,6 +71,10 @@
 #ifdef CONTROL_USE_JSON
         json::Manager manager(event);
 
+        // Handle loading fan control's config file(s)
+        phosphor::fan::JsonConfig config(
+            std::bind(&json::Manager::load, &manager));
+
         // Enable SIGHUP handling to reload JSON configs
         stdplus::signal::block(SIGHUP);
         sdeventplus::source::Signal signal(
diff --git a/json_config.hpp b/json_config.hpp
index ecd6353..951eae0 100644
--- a/json_config.hpp
+++ b/json_config.hpp
@@ -319,30 +319,6 @@
             return confFile;
         }
 
-        // TODO - Will be removed in a following commit in place of using a
-        // constructor to populate the compatible values
-        auto compatObjPaths = getCompatObjPaths();
-        if (!compatObjPaths.empty())
-        {
-            for (auto& path : compatObjPaths)
-            {
-                try
-                {
-                    // Retrieve json config compatible relative path
-                    // locations (last one found will be what's used if more
-                    // than one dbus object implementing the comptaible
-                    // interface exists).
-                    _confCompatValues =
-                        util::SDBusPlus::getProperty<std::vector<std::string>>(
-                            bus, path, confCompatIntf, confCompatProp);
-                }
-                catch (const util::DBusError&)
-                {
-                    // Property unavailable on this dbus object path.
-                }
-            }
-        }
-
         // Look for a config file at each entry relative to the base
         // path and use the first one found
         auto it = std::find_if(