blob: 954980911be8c153c31baae2e860acc466bc7036 [file] [log] [blame]
/*
* Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
*
* Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
*
*/
#include "DbusSystemdInput.h"
#include "utils/log.h"
namespace velia {
/** @brief Construct the systemd unit watcher for arbitrary dbus object. Mainly for tests. */
DbusSystemdInput::DbusSystemdInput(std::shared_ptr<AbstractManager> manager, sdbus::IConnection& connection, const std::string& busname, const std::string& managerObjectPath, const std::string& managerIface, const std::string& unitIface)
: AbstractInput(std::move(manager))
, m_log(spdlog::get("input"))
, m_busName(busname)
, m_unitIface(unitIface)
, m_proxyManager(sdbus::createProxy(connection, m_busName, managerObjectPath))
{
// Subscribe to systemd events. Systemd may not generate signals unless explicitly called
m_proxyManager->callMethod("Subscribe").onInterface(managerIface).withArguments().dontExpectReply();
// Register to a signal introducing new unit
m_proxyManager->uponSignal("UnitNew").onInterface(managerIface).call([&](const std::string& unitName, const sdbus::ObjectPath& unitObjectPath) {
if (m_proxyUnits.find(unitObjectPath) == m_proxyUnits.end()) {
registerSystemdUnit(connection, unitName, unitObjectPath);
}
});
m_proxyManager->finishRegistration();
/* Track all current units. Method ListUnits(a(ssssssouso) out) method returns a dbus struct type with information
* about the unit (see https://www.freedesktop.org/wiki/Software/systemd/dbus/#Manager-ListUnits).
* In our code we need only the first (index 0, the unit name) field and seventh (index 6, unit object path) field.
*/
std::vector<sdbus::Struct<std::string, std::string, std::string, std::string, std::string, std::string, sdbus::ObjectPath, uint32_t, std::string, sdbus::ObjectPath>> units;
m_proxyManager->callMethod("ListUnits").onInterface(managerIface).storeResultsTo(units);
for (const auto& unit : units) {
registerSystemdUnit(connection, unit.get<0>(), unit.get<6>());
}
}
/** @brief Construct the systemd watcher for well-known systemd paths. */
DbusSystemdInput::DbusSystemdInput(std::shared_ptr<AbstractManager> manager, sdbus::IConnection& connection)
: DbusSystemdInput(std::move(manager), connection, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "org.freedesktop.systemd1.Unit")
{
}
/** @brief Registers a systemd unit by its unit name and unit dbus objectpath. */
void DbusSystemdInput::registerSystemdUnit(sdbus::IConnection& connection, const std::string& unitName, const sdbus::ObjectPath& unitObjectPath)
{
auto proxyUnit = sdbus::createProxy(connection, m_busName, unitObjectPath);
proxyUnit->uponSignal("PropertiesChanged").onInterface("org.freedesktop.DBus.Properties").call([&, unitName](const std::string& iface, const std::map<std::string, sdbus::Variant>& changed, [[maybe_unused]] const std::vector<std::string>& invalidated) {
if (iface != m_unitIface) {
return;
}
std::string nActiveState, nSubState;
if (auto it = changed.find("ActiveState"); it != changed.end()) {
nActiveState = it->second.get<std::string>();
}
if (auto it = changed.find("SubState"); it != changed.end()) {
nSubState = it->second.get<std::string>();
}
onUnitStateChange(unitName, nActiveState, nSubState);
m_log->trace("Systemd unit '{}' changed state ({} {})", unitName, nActiveState, nSubState);
});
proxyUnit->finishRegistration();
// Query the current state of this unit
std::string nActiveState = proxyUnit->getProperty("ActiveState").onInterface(m_unitIface);
std::string nSubState = proxyUnit->getProperty("SubState").onInterface(m_unitIface);
onUnitStateChange(unitName, nActiveState, nSubState);
m_proxyUnits.emplace(std::make_pair(unitObjectPath, std::move(proxyUnit)));
m_log->trace("Registered systemd unit watcher for '{}' ({} {})", unitName, nActiveState, nSubState);
}
/** @brief Callback for unit state change */
void DbusSystemdInput::onUnitStateChange(const std::string& name, const std::string& activeState, const std::string& subState)
{
if (activeState == "failed" || (activeState == "activating" && subState == "auto-restart")) {
m_failedUnits.insert(name); // this unit is in failed state
} else {
m_failedUnits.erase(name); // this unit is now OK
}
updateState(m_failedUnits.empty() ? State::OK : State::ERROR);
}
DbusSystemdInput::~DbusSystemdInput() = default;
}