Change async_method_call to be no-throw

The async_method_call() would throw despite the inferred promise that it
would not, because the handler would be passed an error_code object. But
that would only protect from the dbus method call itself.  When it came
time to unpack the response, the read_into_tuple(...) method call would
throw if the received types did not match the expected types. And
because this throw would happen in a separate boost::asio context, the
throw would always be fatal. Now, any exception during the D-Bus call or
unpacking of parameters will result in an error_code getting passed into
the handler so it can take appropriate action.

This also updates the example to remove try/catch statements around the
async_method_call and yield_method_call examples and shows what happens
if the method calls fail because of different types of errors
(api/function does not exist vs. incorrect unpack types).

Tested-by: run asio-example to see that it works as expected:
    # /tmp/asio-example
    voidBar() -> 42
    fooYield(yield, 41)...
    async_send callback
    error with async_send
    ipmiInterface:execute(61)
    async_method_call callback
    /org/openbmc/control/bmc0
    /org/openbmc/control/flash/bmc
    fooYield(yield, 41)...
    ipmi call returns OK!
    foo(41) -> 42
    async_method_call callback
    async_method_call expected failure: generic:foo(41) -> 42
    yielding call to foo OK! (-> 42)
    22
    async call to Properties.Get serialized via yield OK!
    TestYieldFunction return 42
    yielding call to foo OK! (-> 42)
    yielding call to TestYieldFunction serialized via yield OK!
    fooYield(yield, 41)...
    foo(41) -> 42
    async call to Properties.Get serialized via yield OK!
    yielding call to foo OK! (-> 42)
    TestYieldFunction expected error: generic:22
    TestYieldFunctionNotExits expected error: generic:53
    *** tick ***
    *** tock ***
    *** tick ***
    *** tick ***
    *** tick ***

Change-Id: I53c91484ed496556342b3ed0a58b690872a2d676
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>

stuff

Change-Id: I48da27be7ba8c63f44c12a8b79fffb8f3e085648
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
2 files changed
tree: 6134b9a736a6d2baef422988a8ce2fe1a3359c2c
  1. docs/
  2. example/
  3. sdbusplus/
  4. test/
  5. tools/
  6. .clang-format
  7. .gitignore
  8. bootstrap.sh
  9. configure.ac
  10. LICENSE
  11. MAINTAINERS
  12. Makefile.am
  13. README.md
  14. sdbusplus.pc.in
README.md

sdbusplus

sdbusplus contains two parts:

  1. A C++ library (libsdbusplus) for interacting with D-Bus, built on top of the sd-bus library from systemd.
  2. A tool (sdbus++) to generate C++ bindings to simplify the development of D-Bus-based applications.

Dependencies

The sdbusplus library requires sd-bus, which is contained in libsystemd.

The sdbus++ application requires python and the python libraries mako and py-inflection.

C++ library

The sdbusplus library builds on top of the sd-bus library to create a modern C++ API for D-Bus. The library attempts to be as lightweight as possible, usually compiling to exactly the sd-bus API calls that would have been necessary, while also providing compile-time type-safety and memory leak protection afforded by modern C++ practices.

Consider the following code:

auto b = bus::new_default_system();
auto m = b.new_method_call("org.freedesktop.login1",
                           "/org/freedesktop/login1",
                           "org.freedesktop.login1.Manager",
                           "ListUsers");
auto reply = b.call(m);

std::vector<std::tuple<uint32_t, std::string, message::object_path>> users;
reply.read(users);

In a few, relatively succinct, C++ lines this snippet will create a D-Bus connection to the system bus, and call the systemd login manager to get a list of active users. The message and bus objects are automatically freed when they leave scope and the message format strings are generated at compile time based on the types being read. Compare this to the corresponding server code within logind.

In general, the library attempts to mimic the naming conventions of the sd-bus library: ex. sd_bus_call becomes sdbusplus::bus::call, sd_bus_get_unique_name becomes sdbusplus::bus::get_unique_name, sd_bus_message_get_signature becomes sdbusplus::message::get_signature, etc. This allows a relatively straight-forward translation back to the sd-bus functions for looking up the manpage details.

Binding generation tool

sdbusplus also contains a bindings generator tool: sdbus++. The purpose of a bindings generator is to reduce the boilerplate associated with creating D-Bus server or client applications. When creating a server application, rather than creating sd-bus vtables and writing C-style functions to handle each vtable callback, you can create a small YAML file to define your D-Bus interface and the sdbus++ tool will create a C++ class that implements your D-Bus interface. This class has a set of virtual functions for each method and property, which you can overload to create your own customized behavior for the interface.

There are currently two types of YAML files: interface and error. Interfaces are used to create server and client D-Bus interfaces. Errors are used to define C++ exceptions which can be thrown and will automatically turn into D-Bus error responses.

[[ D-Bus client bindings are not yet implemented. See openbmc/openbmc#851. ]]

Generating bindings

How to use tools/sdbus++

The path of your file will be the interface name. For example, for an interface org.freedesktop.Example, you would create the files org/freedesktop/Example.interface.yaml and org/freedesktop/Example.errors.yaml] for interfaces and errors respectively. These can then be used to generate the server and error bindings:

sdbus++ interface server-header org.freedesktop.Example > \
    org/freedesktop/Example/server.hpp
sdbus++ interface server-cpp org.freedesktop.Example > \
    org/freedesktop/Example/server.cpp
sdbus++ error exception-header org.freedesktop.Example > \
    org/freedesktop/Example/error.hpp \
sdbus++ error exception-cpp org.freedesktop.Example > \
    org/freedesktop/Example/error.cpp

Markdown-based documentation can also be generated from the interface and exception files:

sdbus++ interface markdown org.freedesktop.Example > \
    org/freedesktop/Example.md
sdbus++ error markdown org.freedesktop.Example >> \
    org/freedesktop/Example.md

See the example/Makefile.am for more details.

Installing sdbusplus on custom distributions

Installation of sdbusplus bindings on a custom distribution requires a few packages to be installed prior. Although these packages are the same for several distributions the names of these packages do differ. Below are the packages needed for Ubuntu and Fedora.

Installation on Ubuntu

sudo apt install git autoconf libtool pkg-config g++ autoconf-archive libsystemd-dev python python-yaml python-mako python-inflection

Installation on Fedora

sudo dnf install git autoconf libtool gcc-c++ pkgconfig autoconf-archive systemd-devel python python-pip python-yaml python-mako

Install the inflection package using the pip utility (on Fedora)

pip install inflection