blob: dcf2744aec1a2945d0cc24eb043b92c240dcad46 [file] [log] [blame]
#pragma once
#include "dbus_types.hpp"
#include <sdbusplus/bus/match.hpp>
namespace openpower::pels
{
namespace match_rules = sdbusplus::bus::match::rules;
/**
* @class DBusWatcher
*
* The base class for the PropertyWatcher and InterfaceWatcher classes.
*/
class DBusWatcher
{
public:
DBusWatcher() = delete;
virtual ~DBusWatcher() = default;
DBusWatcher(const DBusWatcher&) = default;
DBusWatcher& operator=(const DBusWatcher&) = default;
DBusWatcher(DBusWatcher&&) = default;
DBusWatcher& operator=(DBusWatcher&&) = default;
/**
* @brief Constructor
*
* @param[in] path - The D-Bus path that will be watched
* @param[in] interface - The D-Bus interface that will be watched
*/
DBusWatcher(const std::string& path, const std::string& interface) :
_path(path), _interface(interface)
{
}
protected:
/**
* @brief The D-Bus path
*/
std::string _path;
/**
* @brief The D-Bus interface
*/
std::string _interface;
/**
* @brief The match objects for the propertiesChanged and
* interfacesAdded signals.
*/
std::vector<sdbusplus::bus::match_t> _matches;
};
/**
* @class PropertyWatcher
*
* This class allows the user to be kept up to data with a D-Bus
* property's value. It does this by calling a user specified function
* that is passed the variant that contains the property's value when:
*
* 1) The property is read when the class is constructed, if
* the property is on D-Bus at the time.
* 2) The property changes (via a property changed signal).
* 3) An interfacesAdded signal is received with that property.
*
* The DataInterface class is used to access D-Bus, and is a template
* to avoid any circular include issues as that class is one of the
* users of this one.
*/
template <typename DataIface>
class PropertyWatcher : public DBusWatcher
{
public:
PropertyWatcher() = delete;
~PropertyWatcher() = default;
PropertyWatcher(const PropertyWatcher&) = delete;
PropertyWatcher& operator=(const PropertyWatcher&) = delete;
PropertyWatcher(PropertyWatcher&&) = delete;
PropertyWatcher& operator=(PropertyWatcher&&) = delete;
using PropertySetFunc = std::function<void(const DBusValue&)>;
/**
* @brief Constructor
*
* Reads the property if it is on D-Bus, and sets up the match
* objects for the propertiesChanged and interfacesAdded signals.
*
* @param[in] bus - The sdbusplus bus object
* @param[in] path - The D-Bus path of the property
* @param[in] interface - The D-Bus interface that contains the property
* @param[in] propertyName - The property name
* @param[in] service - The D-Bus service to use for the property read.
* Can be empty to look it up instead.
* @param[in] dataIface - The DataInterface object
* @param[in] func - The callback used any time the property is read
*/
PropertyWatcher(sdbusplus::bus_t& bus, const std::string& path,
const std::string& interface,
const std::string& propertyName, const std::string& service,
const DataIface& dataIface, PropertySetFunc func) :
DBusWatcher(path, interface),
_name(propertyName), _setFunc(func)
{
_matches.emplace_back(
bus, match_rules::propertiesChanged(_path, _interface),
std::bind(std::mem_fn(&PropertyWatcher::propChanged), this,
std::placeholders::_1));
_matches.emplace_back(
bus,
match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
std::bind(std::mem_fn(&PropertyWatcher::interfaceAdded), this,
std::placeholders::_1));
try
{
read(dataIface, service);
}
catch (const sdbusplus::exception_t& e)
{
// Path doesn't exist now
}
}
/**
* @brief Constructor
*
* Reads the property if it is on D-Bus, and sets up the match
* objects for the propertiesChanged and interfacesAdded signals.
*
* Unlike the other constructor, this contructor doesn't take the
* service to use for the property read so it will look it up with
* an ObjectMapper GetObject call.
*
* @param[in] bus - The sdbusplus bus object
* @param[in] path - The D-Bus path of the property
* @param[in] interface - The D-Bus interface that contains the property
* @param[in] propertyName - The property name
* @param[in] dataIface - The DataInterface object
* @param[in] func - The callback used any time the property is read
*/
PropertyWatcher(sdbusplus::bus_t& bus, const std::string& path,
const std::string& interface,
const std::string& propertyName, const DataIface& dataIface,
PropertySetFunc func) :
PropertyWatcher(bus, path, interface, propertyName, "", dataIface, func)
{
}
/**
* @brief Reads the property on D-Bus, and calls
* the user defined function with the value.
*
* If the passed in service is empty, look up the service to use.
*
* @param[in] dataIface - The DataInterface object
* @param[in] service - The D-Bus service to make the getProperty
* call with, if not empty
*/
void read(const DataIface& dataIface, std::string service)
{
if (service.empty())
{
service = dataIface.getService(_path, _interface);
}
if (!service.empty())
{
DBusValue value;
dataIface.getProperty(service, _path, _interface, _name, value);
_setFunc(value);
}
}
/**
* @brief The propertiesChanged callback
*
* Calls the user defined function with the property value
*
* @param[in] msg - The sdbusplus message object
*/
void propChanged(sdbusplus::message_t& msg)
{
DBusInterface interface;
DBusPropertyMap properties;
msg.read(interface, properties);
auto prop = properties.find(_name);
if (prop != properties.end())
{
_setFunc(prop->second);
}
}
/**
* @brief The interfacesAdded callback
*
* Calls the user defined function with the property value
*
* @param[in] msg - The sdbusplus message object
*/
void interfaceAdded(sdbusplus::message_t& msg)
{
sdbusplus::message::object_path path;
DBusInterfaceMap interfaces;
msg.read(path, interfaces);
auto iface = interfaces.find(_interface);
if (iface != interfaces.end())
{
auto prop = iface->second.find(_name);
if (prop != iface->second.end())
{
_setFunc(prop->second);
}
}
}
private:
/**
* @brief The D-Bus property name
*/
std::string _name;
/**
* @brief The function that will be called any time the
* property is read.
*/
PropertySetFunc _setFunc;
};
/**
* @class InterfaceWatcher
*
* This class allows the user to be kept up to data with a D-Bus
* interface's properties.. It does this by calling a user specified
* function that is passed a map of the D-Bus property names and values
* on that interface when:
*
* 1) The interface is read when the class is constructed, if
* the interface is on D-Bus at the time.
* 2) The interface has a property that changes (via a property changed signal).
* 3) An interfacesAdded signal is received.
*
* The DataInterface class is used to access D-Bus, and is a template
* to avoid any circular include issues as that class is one of the
* users of this one.
*/
template <typename DataIface>
class InterfaceWatcher : public DBusWatcher
{
public:
InterfaceWatcher() = delete;
~InterfaceWatcher() = default;
InterfaceWatcher(const InterfaceWatcher&) = delete;
InterfaceWatcher& operator=(const InterfaceWatcher&) = delete;
InterfaceWatcher(InterfaceWatcher&&) = delete;
InterfaceWatcher& operator=(InterfaceWatcher&&) = delete;
using InterfaceSetFunc = std::function<void(const DBusPropertyMap&)>;
/**
* @brief Constructor
*
* Reads all properties on the interface if it is on D-Bus,
* and sets up the match objects for the propertiesChanged
* and interfacesAdded signals.
*
* @param[in] bus - The sdbusplus bus object
* @param[in] path - The D-Bus path of the property
* @param[in] interface - The D-Bus interface that contains the property
* @param[in] dataIface - The DataInterface object
* @param[in] func - The callback used any time the property is read
*/
InterfaceWatcher(sdbusplus::bus_t& bus, const std::string& path,
const std::string& interface, const DataIface& dataIface,
InterfaceSetFunc func) :
DBusWatcher(path, interface),
_setFunc(func)
{
_matches.emplace_back(
bus, match_rules::propertiesChanged(_path, _interface),
std::bind(std::mem_fn(&InterfaceWatcher::propChanged), this,
std::placeholders::_1));
_matches.emplace_back(
bus,
match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
std::bind(std::mem_fn(&InterfaceWatcher::interfaceAdded), this,
std::placeholders::_1));
try
{
read(dataIface);
}
catch (const sdbusplus::exception_t& e)
{
// Path doesn't exist now
}
}
/**
* @brief Reads the interface's properties on D-Bus, and
* calls the the user defined function with the property map.
*
* @param[in] dataIface - The DataInterface object
*/
void read(const DataIface& dataIface)
{
auto service = dataIface.getService(_path, _interface);
if (!service.empty())
{
auto properties =
dataIface.getAllProperties(service, _path, _interface);
_setFunc(properties);
}
}
/**
* @brief The propertiesChanged callback
*
* Calls the user defined function with the property map. Only the
* properties that changed will be in the map.
*
* @param[in] msg - The sdbusplus message object
*/
void propChanged(sdbusplus::message_t& msg)
{
DBusInterface interface;
DBusPropertyMap properties;
msg.read(interface, properties);
_setFunc(properties);
}
/**
* @brief The interfacesAdded callback
*
* Calls the user defined function with the property map
*
* @param[in] msg - The sdbusplus message object
*/
void interfaceAdded(sdbusplus::message_t& msg)
{
sdbusplus::message::object_path path;
DBusInterfaceMap interfaces;
msg.read(path, interfaces);
auto iface = interfaces.find(_interface);
if (iface != interfaces.end())
{
_setFunc(iface->second);
}
}
private:
/**
* @brief The function that will be called any time the
* interface is read.
*/
InterfaceSetFunc _setFunc;
};
} // namespace openpower::pels