control: Support reloading configurations with SIGHUP
Support the use of SIGHUP to reload the JSON configuration files where
all configuration files must succeed in being parsed to enable the use
of the config files found. This is particularly useful in placing one or
more override fan control configuration files and having fan control
immediately begin to use them on a system.
To minimize complexity, the creation of the Manager object when fan
control starts or receiving a SIGHUP use the same configuration file
loading method. This way if any future changes in this area only needs
to be done in one place.
Change-Id: Iaf906c3b4611ba88f45b5684935cf477045eeb2b
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/manager.cpp b/control/json/manager.cpp
index a519836..78d30f3 100644
--- a/control/json/manager.cpp
+++ b/control/json/manager.cpp
@@ -57,7 +57,36 @@
Manager::Manager(const sdeventplus::Event& event) :
_bus(util::SDBusPlus::getBus()), _event(event)
{
- // Manager JSON config file is optional
+ load();
+}
+
+void Manager::sighupHandler(sdeventplus::source::Signal&,
+ const struct signalfd_siginfo*)
+{
+ // Save current set of available and active profiles
+ std::map<configKey, std::unique_ptr<Profile>> profiles;
+ profiles.swap(_profiles);
+ std::vector<std::string> activeProfiles;
+ activeProfiles.swap(_activeProfiles);
+
+ try
+ {
+ load();
+ }
+ catch (std::runtime_error& re)
+ {
+ // Restore saved available and active profiles
+ _profiles.swap(profiles);
+ _activeProfiles.swap(activeProfiles);
+ log<level::ERR>("Error reloading configs, no changes made",
+ entry("LOAD_ERROR=%s", re.what()));
+ }
+}
+
+void Manager::load()
+{
+ // TODO - Remove Manager JSON config support since it's not needed with init
+ // mode removed
auto confFile =
fan::JsonConfig::getConfFile(_bus, confAppName, confFileName, true);
if (!confFile.empty())
@@ -65,11 +94,11 @@
_jsonObj = fan::JsonConfig::load(confFile);
}
- // Parse and set the available profiles and which are active
+ // Load the available profiles and which are active
setProfiles();
// Load the zone configurations
- _zones = getConfig<Zone>(false, event, this);
+ 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)
@@ -77,30 +106,39 @@
configKey fanProfile =
std::make_pair(fan.second->getZone(), fan.first.second);
auto itZone = std::find_if(
- _zones.begin(), _zones.end(), [&fanProfile](const auto& zone) {
+ zones.begin(), zones.end(), [&fanProfile](const auto& zone) {
return Manager::inConfig(fanProfile, zone.first);
});
- if (itZone != _zones.end())
+ if (itZone != zones.end())
{
if (itZone->second->getTarget() != fan.second->getTarget() &&
fan.second->getTarget() != 0)
{
- // Update zone target to current target of the fan in the zone
+ // 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));
}
}
+
+ // 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(); });
- // Load any events configured and enable
- _events = getConfig<Event>(true, this, _zones);
+ // 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(); });
-
- _bus.request_name(CONTROL_BUSNAME);
}
const std::vector<std::string>& Manager::getActiveProfiles()
@@ -509,6 +547,8 @@
// Profiles JSON config file is optional
auto confFile = fan::JsonConfig::getConfFile(_bus, confAppName,
Profile::confFileName, true);
+
+ _profiles.clear();
if (!confFile.empty())
{
for (const auto& entry : fan::JsonConfig::load(confFile))
@@ -519,8 +559,10 @@
std::move(obj));
}
}
+
// Ensure all configurations use the same set of active profiles
// (In case a profile's active state changes during configuration)
+ _activeProfiles.clear();
for (const auto& profile : _profiles)
{
if (profile.second->isActive())
diff --git a/control/json/manager.hpp b/control/json/manager.hpp
index 657f51a..c5b591d 100644
--- a/control/json/manager.hpp
+++ b/control/json/manager.hpp
@@ -134,6 +134,13 @@
Manager(const sdeventplus::Event& event);
/**
+ * @brief Callback function to handle receiving a HUP signal to reload the
+ * JSON configurations.
+ */
+ void sighupHandler(sdeventplus::source::Signal&,
+ const struct signalfd_siginfo*);
+
+ /**
* @brief Get the active profiles of the system where an empty list
* represents that only configuration entries without a profile defined will
* be loaded.
@@ -451,6 +458,16 @@
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 Find the service name for a given path and interface from the
* cached dataset
*
diff --git a/control/main.cpp b/control/main.cpp
index 1dcc0b5..9c7f8a2 100644
--- a/control/main.cpp
+++ b/control/main.cpp
@@ -26,6 +26,8 @@
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
#include <sdeventplus/event.hpp>
+#include <sdeventplus/source/signal.hpp>
+#include <stdplus/signal.hpp>
using namespace phosphor::fan::control;
using namespace phosphor::logging;
@@ -68,6 +70,15 @@
{
#ifdef CONTROL_USE_JSON
json::Manager manager(event);
+
+ // Enable SIGHUP handling to reload JSON configs
+ stdplus::signal::block(SIGHUP);
+ sdeventplus::source::Signal signal(
+ event, SIGHUP,
+ std::bind(&json::Manager::sighupHandler, &manager,
+ std::placeholders::_1, std::placeholders::_2));
+
+ phosphor::fan::util::SDBusPlus::getBus().request_name(CONTROL_BUSNAME);
#else
Manager manager(phosphor::fan::util::SDBusPlus::getBus(), event, mode);