From 0df00be00999bc3285b8b0a401b48e42fb862250 Mon Sep 17 00:00:00 2001 From: Brad Bishop Date: Thu, 25 May 2017 23:38:37 -0400 Subject: Method support Add support for a method callback. The method callback enables arbitrary DBus method calls. A sample use case could be starting a systemd unit via the sytemd DBus API. Change-Id: If25131d11497c82f862ae1f47da066c5fd8b2e2e Signed-off-by: Brad Bishop --- src/Makefile.am | 3 +- src/example/example.yaml | 15 +++++ src/method.hpp | 138 +++++++++++++++++++++++++++++++++++++++ src/pdmgen.py | 80 +++++++++++++++++++++++ src/sdbusplus.hpp | 22 +++++++ src/templates/generated.mako.hpp | 1 + src/templates/method.mako.cpp | 6 ++ 7 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/method.hpp create mode 100644 src/templates/method.mako.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 4d6658d..0380f6c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,8 @@ TEMPLATES = \ templates/conditional.mako.cpp \ templates/count.mako.cpp \ templates/generated.mako.hpp \ - templates/journal.mako.cpp + templates/journal.mako.cpp \ + templates/method.mako.cpp generated.hpp: $(PDMGEN) $(YAML_PATH) $(TEMPLATES) $(AM_V_GEN)$(PYTHON) ${PDMGEN} \ diff --git a/src/example/example.yaml b/src/example/example.yaml index 2066227..d76e3ad 100644 --- a/src/example/example.yaml +++ b/src/example/example.yaml @@ -78,6 +78,21 @@ severity: INFO message: Hello world from PDM! +- name: example method callback + description: > + 'The method callback invokes the specified DBus method.' + class: callback + callback: method + service: org.freedesktop.systemd1 + path: /org/freedesktop/systemd1 + interface: org.freedesktop.systemd1.Manager + method: StartUnit + args: + - value: foo.unit + type: string + - value: replace + type: string + - name: example callback group description: > 'Callbacks groups are simply named collections of other callbacks. diff --git a/src/method.hpp b/src/method.hpp new file mode 100644 index 0000000..66c19db --- /dev/null +++ b/src/method.hpp @@ -0,0 +1,138 @@ +#pragma once + +#include +#include "callback.hpp" + +namespace phosphor +{ +namespace dbus +{ +namespace monitoring +{ +namespace detail +{ + +/** @class CallDBusMethod + * @brief Provide explicit call forwarding to + * DBusInterface::callMethodNoReply. + * + * @tparam DBusInterface - The DBus interface to use. + * @tparam MethodArgs - DBus method argument types. + */ +template +struct CallDBusMethod +{ + static void op( + const std::string& bus, + const std::string& path, + const std::string& iface, + const std::string& method, + MethodArgs&& ...args) + { + DBusInterface::callMethodNoReply( + bus, + path, + iface, + method, + std::forward(args)...); + } +}; +} // namespace detail + +/** @class MethodBase + * @brief Invoke DBus method callback implementation. + * + * The method callback invokes the client supplied DBus method. + */ +class MethodBase : public Callback +{ + public: + MethodBase() = delete; + MethodBase(const MethodBase&) = delete; + MethodBase(MethodBase&&) = default; + MethodBase& operator=(const MethodBase&) = delete; + MethodBase& operator=(MethodBase&&) = default; + virtual ~MethodBase() = default; + MethodBase( + const std::string& b, + const std::string& p, + const std::string& i, + const std::string& m) + : Callback(), + bus(b), + path(p), + interface(i), + method(m) {} + + /** @brief Callback interface implementation. */ + void operator()() override = 0; + + protected: + const std::string& bus; + const std::string& path; + const std::string& interface; + const std::string& method; +}; + +/** @class Method + * @brief C++ type specific logic for the method callback. + * + * @tparam DBusInterface - The DBus interface to use to call the method. + * @tparam MethodArgs - DBus method argument types. + */ +template +class Method : public MethodBase +{ + public: + Method() = delete; + Method(const Method&) = default; + Method(Method&&) = default; + Method& operator=(const Method&) = default; + Method& operator=(Method&&) = default; + ~Method() = default; + Method( + const std::string& bus, + const std::string& path, + const std::string& iface, + const std::string& method, + MethodArgs&& ... arguments) + : MethodBase(bus, path, iface, method), + args(std::forward(arguments)...) {} + + /** @brief Callback interface implementation. */ + void operator()() override + { + std::experimental::apply( + detail::CallDBusMethod::op, + std::tuple_cat( + std::make_tuple(bus), + std::make_tuple(path), + std::make_tuple(interface), + std::make_tuple(method), + args)); + } + + private: + std::tuple args; +}; + +/** @brief Argument type deduction for constructing Method instances. */ +template +auto makeMethod( + const std::string& bus, + const std::string& path, + const std::string& iface, + const std::string& method, + MethodArgs&& ... arguments) +{ + return std::make_unique>( + bus, + path, + iface, + method, + std::forward(arguments)...); +} + +} // namespace monitoring +} // namespace dbus +} // namespace phosphor diff --git a/src/pdmgen.py b/src/pdmgen.py index 6dc6861..5d4bf29 100755 --- a/src/pdmgen.py +++ b/src/pdmgen.py @@ -635,6 +635,85 @@ class Journal(Callback, Renderer): indent=indent) +class Method(ConfigEntry, Renderer): + '''Handle the method callback config file directive.''' + + def __init__(self, *a, **kw): + self.service = kw.pop('service') + self.path = kw.pop('path') + self.interface = kw.pop('interface') + self.method = kw.pop('method') + self.args = [TrivialArgument(**x) for x in kw.pop('args', {})] + super(Method, self).__init__(**kw) + + def factory(self, objs): + args = { + 'class': 'interface', + 'interface': 'element', + 'name': self.service + } + add_unique(ConfigEntry( + configfile=self.configfile, **args), objs) + + args = { + 'class': 'pathname', + 'pathname': 'element', + 'name': self.path + } + add_unique(ConfigEntry( + configfile=self.configfile, **args), objs) + + args = { + 'class': 'interface', + 'interface': 'element', + 'name': self.interface + } + add_unique(ConfigEntry( + configfile=self.configfile, **args), objs) + + args = { + 'class': 'propertyname', + 'propertyname': 'element', + 'name': self.method + } + add_unique(ConfigEntry( + configfile=self.configfile, **args), objs) + + super(Method, self).factory(objs) + + def setup(self, objs): + '''Resolve elements.''' + + self.service = get_index( + objs, + 'interface', + self.service) + + self.path = get_index( + objs, + 'pathname', + self.path) + + self.interface = get_index( + objs, + 'interface', + self.interface) + + self.method = get_index( + objs, + 'propertyname', + self.method) + + super(Method, self).setup(objs) + + def construct(self, loader, indent): + return self.render( + loader, + 'method.mako.cpp', + c=self, + indent=indent) + + class CallbackGraphEntry(Group): '''An entry in a traversal list for groups of callbacks.''' @@ -724,6 +803,7 @@ class Everything(Renderer): 'callback': { 'journal': Journal, 'group': GroupOfCallbacks, + 'method': Method, }, 'condition': { 'count': CountCondition, diff --git a/src/sdbusplus.hpp b/src/sdbusplus.hpp index 7dbf44d..8afb8b5 100644 --- a/src/sdbusplus.hpp +++ b/src/sdbusplus.hpp @@ -30,6 +30,28 @@ class SDBusPlus } public: + /** @brief Invoke a method; ignore reply. */ + template + static void callMethodNoReply( + const std::string& busName, + const std::string& path, + const std::string& interface, + const std::string& method, + Args&& ... args) + { + auto reqMsg = getBus().new_method_call( + busName.c_str(), + path.c_str(), + interface.c_str(), + method.c_str()); + reqMsg.append(std::forward(args)...); + getBus().call_noreply(reqMsg); + + // TODO: openbmc/openbmc#1719 + // invoke these methods async, with a callback + // handler that checks for errors and logs. + } + /** @brief Invoke a method. */ template static auto callMethod( diff --git a/src/templates/generated.mako.hpp b/src/templates/generated.mako.hpp index 15835bf..9e89983 100644 --- a/src/templates/generated.mako.hpp +++ b/src/templates/generated.mako.hpp @@ -7,6 +7,7 @@ #include "count.hpp" #include "data_types.hpp" #include "journal.hpp" +#include "method.hpp" #include "propertywatchimpl.hpp" #include "sdbusplus.hpp" diff --git a/src/templates/method.mako.cpp b/src/templates/method.mako.cpp new file mode 100644 index 0000000..31cdf65 --- /dev/null +++ b/src/templates/method.mako.cpp @@ -0,0 +1,6 @@ +makeMethod( +${indent(1)}ConfigInterfaces::get()[${c.service}], +${indent(1)}ConfigPaths::get()[${c.path}], +${indent(1)}ConfigInterfaces::get()[${c.interface}], +${indent(1)}ConfigProperties::get()[${c.method}], +${indent(1)}${(',\n' + indent(1)).join([val.argument(loader, indent=indent +1) for val in c.args])})\ -- cgit v1.2.1