blob: 624eb600e07717337d87b5809e4c710bb7460f48 [file] [log] [blame]
#pragma once
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <sdbusplus/server.hpp>
#include <xyz/openbmc_project/Inventory/Manager/server.hpp>
#include "events.hpp"
#include "functor.hpp"
#include "types.hpp"
#include "serialize.hpp"
namespace phosphor
{
namespace inventory
{
namespace manager
{
template <typename T>
using ServerObject = T;
using ManagerIface =
sdbusplus::xyz::openbmc_project::Inventory::server::Manager;
/** @struct PropertiesVariant
* @brief Wrapper for sdbusplus PropertiesVariant.
*
* A wrapper is useful since MakeInterface is instantiated with 'int'
* to deduce the return type of its methods, which does not depend
* on T.
*
* @tparam T - The sdbusplus server binding type.
*/
template <typename T, typename Enable = void>
struct PropertiesVariant {};
template <typename T>
struct PropertiesVariant<T, typename std::enable_if<std::is_object<T>::value>::type>
{
using Type = typename T::PropertiesVariant;
};
template <typename T>
using PropertiesVariantType = typename PropertiesVariant<T>::Type;
template <typename T, typename U = int>
struct HasProperties : std::false_type
{
};
template <typename T>
struct HasProperties <T,
decltype((void) std::declval<typename T::PropertiesVariant>(), 0)> :
std::true_type
{
};
template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
any_ns::any propMake(
sdbusplus::bus::bus& bus, const char* path, const Interface& props)
{
using InterfaceVariant =
std::map<std::string, PropertiesVariantType<T>>;
InterfaceVariant v;
for (const auto& p : props)
{
v.emplace(
p.first,
convertVariant<PropertiesVariantType<T>>(p.second));
}
return any_ns::any(std::make_shared<T>(bus, path, v));
}
template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
any_ns::any propMake(
sdbusplus::bus::bus& bus, const char* path, const Interface& props)
{
return any_ns::any(std::make_shared<T>(bus, path));
}
template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
void propAssign(const Interface& props, any_ns::any& holder)
{
auto& iface = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
for (const auto& p : props)
{
iface.setPropertyByName(
p.first, convertVariant<PropertiesVariantType<T>>(p.second));
}
}
template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
void propAssign(const Interface& props, any_ns::any& holder)
{
}
template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
void propSerialize(
const std::string& path, const std::string& iface,
const any_ns::any& holder)
{
const auto& object =
*any_ns::any_cast<const std::shared_ptr<T> &>(holder);
cereal::serialize(path, iface, object);
}
template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
void propSerialize(
const std::string& path, const std::string& iface,
const any_ns::any& holder)
{
cereal::serialize(path, iface);
}
template <typename T, std::enable_if_t<HasProperties<T>::value, bool> = true>
void propDeSerialize(
const std::string& path, const std::string& iface, any_ns::any& holder)
{
auto& object = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
cereal::deserialize(path, iface, object);
}
template <typename T, std::enable_if_t<!HasProperties<T>::value, bool> = false>
void propDeSerialize(
const std::string& path, const std::string& iface, any_ns::any& holder)
{
}
/** @struct MakeInterface
* @brief Adapt an sdbusplus interface proxy.
*
* Template instances are builder functions that create
* adapted sdbusplus interface proxy interface objects.
*
* @tparam T - The type of the interface being adapted.
*/
template <typename T>
struct MakeInterface
{
static any_ns::any make(
sdbusplus::bus::bus& bus, const char* path, const Interface& props)
{
return propMake<T>(bus, path, props);
}
static void assign(const Interface& props, any_ns::any& holder)
{
propAssign<T>(props, holder);
}
static void serialize(
const std::string& path, const std::string& iface,
const any_ns::any& holder)
{
propSerialize<T>(path, iface, holder);
}
static void deserialize(
const std::string& path, const std::string& iface, any_ns::any& holder)
{
propDeSerialize<T>(path, iface, holder);
}
};
/** @class Manager
* @brief OpenBMC inventory manager implementation.
*
* A concrete implementation for the xyz.openbmc_project.Inventory.Manager
* DBus API.
*/
class Manager final :
public ServerObject<ManagerIface>
{
public:
Manager() = delete;
Manager(const Manager&) = delete;
Manager& operator=(const Manager&) = delete;
Manager(Manager&&) = default;
Manager& operator=(Manager&&) = default;
~Manager() = default;
/** @brief Construct an inventory manager.
*
* @param[in] bus - An sdbusplus bus connection.
* @param[in] busname - The DBus busname to own.
* @param[in] root - The DBus path on which to implement
* an inventory manager.
* @param[in] iface - The DBus inventory interface to implement.
*/
Manager(sdbusplus::bus::bus&&, const char*, const char*, const char*);
using EventInfo = std::tuple <
std::vector<EventBasePtr>,
std::vector<Action >>;
/** @brief Start processing DBus messages. */
void run() noexcept;
/** @brief Provided for testing only. */
void shutdown() noexcept;
/** @brief sd_bus Notify method implementation callback. */
void notify(
std::map<sdbusplus::message::object_path, Object> objs) override;
/** @brief Event processing entry point. */
void handleEvent(sdbusplus::message::message&,
const Event& event,
const EventInfo& info);
/** @brief Drop one or more objects from DBus. */
void destroyObjects(
const std::vector<const char*>& paths);
/** @brief Add objects to DBus. */
void createObjects(
const std::map<sdbusplus::message::object_path, Object>& objs);
/** @brief Add or update objects on DBus. */
void updateObjects(
const std::map<sdbusplus::message::object_path, Object>& objs,
bool restoreFromCache = false);
/** @brief Restore persistent inventory items */
void restore();
/** @brief Invoke an sdbusplus server binding method.
*
* Invoke the requested method with a reference to the requested
* sdbusplus server binding interface as a parameter.
*
* @tparam T - The sdbusplus server binding interface type.
* @tparam U - The type of the sdbusplus server binding member.
* @tparam Args - Argument types of the binding member.
*
* @param[in] path - The DBus path on which the method should
* be invoked.
* @param[in] interface - The DBus interface hosting the method.
* @param[in] member - Pointer to sdbusplus server binding member.
* @param[in] args - Arguments to forward to the binding member.
*
* @returns - The return/value type of the binding method being
* called.
*/
template<typename T, typename U, typename ...Args>
decltype(auto) invokeMethod(const char* path, const char* interface,
U&& member, Args&& ...args)
{
auto& iface = getInterface<T>(path, interface);
return (iface.*member)(std::forward<Args>(args)...);
}
using SigArgs = std::vector <
std::unique_ptr <
std::tuple <
Manager*,
const DbusSignal*,
const EventInfo* >>>;
using SigArg = SigArgs::value_type::element_type;
private:
using InterfaceComposite = std::map<std::string, any_ns::any>;
using ObjectReferences = std::map<std::string, InterfaceComposite>;
using Events = std::vector<EventInfo>;
// The int instantiations are safe since the signature of these
// functions don't change from one instantiation to the next.
using MakerType = std::add_pointer_t <
decltype(MakeInterface<int>::make) >;
using AssignerType = std::add_pointer_t <
decltype(MakeInterface<int>::assign) >;
using SerializerType = std::add_pointer_t <
decltype(MakeInterface<int>::serialize) >;
using DeserializerType = std::add_pointer_t <
decltype(MakeInterface<int>::deserialize) >;
using Makers = std::map<std::string,
std::tuple<MakerType, AssignerType,
SerializerType, DeserializerType>>;
/** @brief Provides weak references to interface holders.
*
* Common code for all types for the templated getInterface
* methods.
*
* @param[in] path - The DBus path for which the interface
* holder instance should be provided.
* @param[in] interface - The DBus interface for which the
* holder instance should be provided.
*
* @returns A weak reference to the holder instance.
*/
const any_ns::any& getInterfaceHolder(
const char*, const char*) const;
any_ns::any& getInterfaceHolder(
const char*, const char*);
/** @brief Provides weak references to interface holders.
*
* @tparam T - The sdbusplus server binding interface type.
*
* @param[in] path - The DBus path for which the interface
* should be provided.
* @param[in] interface - The DBus interface to obtain.
*
* @returns A weak reference to the interface holder.
*/
template<typename T>
auto& getInterface(const char* path, const char* interface)
{
auto& holder = getInterfaceHolder(path, interface);
return *any_ns::any_cast<std::shared_ptr<T> &>(holder);
}
template<typename T>
auto& getInterface(const char* path, const char* interface) const
{
auto& holder = getInterfaceHolder(path, interface);
return *any_ns::any_cast<T>(holder);
}
/** @brief Add or update interfaces on DBus. */
void updateInterfaces(
const sdbusplus::message::object_path& path,
const Object& interfaces,
ObjectReferences::iterator pos,
bool emitSignals = true,
bool restoreFromCache = false);
/** @brief Provided for testing only. */
volatile bool _shutdown;
/** @brief Path prefix applied to any relative paths. */
const char* _root;
/** @brief A container of sdbusplus server interface references. */
ObjectReferences _refs;
/** @brief A container contexts for signal callbacks. */
SigArgs _sigargs;
/** @brief A container of sdbusplus signal matches. */
std::vector<sdbusplus::bus::match_t> _matches;
/** @brief Persistent sdbusplus DBus bus connection. */
sdbusplus::bus::bus _bus;
/** @brief sdbusplus org.freedesktop.DBus.ObjectManager reference. */
sdbusplus::server::manager::manager _manager;
/** @brief A container of pimgen generated events and responses. */
static const Events _events;
/** @brief A container of pimgen generated factory methods. */
static const Makers _makers;
};
} // namespace manager
} // namespace inventory
} // namespace phosphor
// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4