I’ve been working on a d-bus service to replace some of the management guts of my project for a while now. We started out creating a simple service, but some of our management processes take a long time to run, causing a timeout error when calling these methods. I needed a way to run these tasks in the background and report status to any possible clients. I’d like to outline my approach to making this possible. This will be a multi-part blog series starting from the bottom: a very simple, synchronous d-bus service. By the end of this series, we’ll have a small codebase with asynchronous tasks which can be interacted with (input/output) from D-Bus clients.
All of this code is written with python3.5 on Ubuntu 17.04 (beta), is MIT licensed, and can be found on Github: https://github.com/larryprice/python-dbus-blog-series/tree/part1.
What is D-Bus?
In computing, D-Bus or DBus (for “Desktop Bus”), a software bus, is an inter-process communication (IPC) and remote procedure call (RPC) mechanism that allows communication between multiple computer programs (that is, processes) concurrently running on the same machine.
D-Bus allows different processes to communicate indirectly through a known interface. The bus can be system-wide or user-specific (session-based). A D-Bus service will post a list of available objects with available methods which D-Bus clients can consume. It’s at the heart of much Linux desktop software, allowing processes to communicate with one another without forcing direct dependencies.
A synchronous service
Let’s start by building a base of a simple, synchronous service. We’re going to initialize a loop as a context to run our service within, claim a unique name for our service on the session bus, and then start the loop.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Make this binary executable (
chmod +x service) and run it. Your service should run indefinitely and do… nothing. Although we’ve already written a lot of code, we haven’t added any objects or methods which can be accessed on our service. Let’s fix that.
1 2 3 4 5 6 7 8 9 10 11 12
We’ve defined a D-Bus object
RandomData which can be accessed using the path
/com/larry_price/test/RandomData. This style of string is the general style of an object path. We’ve defined an interface implemented by
com.larry_price.test.RandomData with a single method
quick as declared with the
@dbus.service.method context decorator.
quick will take in a single parameter,
bits, which must be an integer as designated by the
in_signature in our context decorator.
quick will return a string as specified by the
out_signature parameter. All that
quick does is return a random string given a number of bits. It’s simple and it’s fast.
Now that we have an object, we need to declare an instance of that object in our service to attach it properly. Let’s assume that
random_data.py is in a directory
dbustest with an empty
__init__.py, and our service binary is still sitting in the root directory. Just before we start the loop in the
service binary, we can add the following code:
1 2 3 4 5 6 7 8 9
We don’t need to do anything with the object we’ve initialized; creating it is enough to attach it to our D-Bus service and prevent it from being garbage collected until the service exits. We pass in
bus_name so that
RandomData will connect to the right bus name.
A synchronous client
Now that you have an object with an available method on our service, you’re probably interested in calling that method. You can do this on the command line with something like
dbus-send, or you could find the service using a GUI tool such as
d-feet and call the method directly. But eventually we’ll want to do this with a custom program, so let’s build a very small program to get started.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
A large chunk of this code is parsing an input argument as an integer. By default,
client will request a 16-bit random number unless it gets a number as input from the command line. Next we spin up a reference to the session bus and attempt to find our
RandomData object on the bus using our known service name and object path. Once that’s initialized, we can directly call the
quick method over the bus with the specified number of bits and print the result.
Make this binary executable also. If you try to run
client without running
service, you should see an error message explaining that the
com.larry-price.test D-Bus service is not running (which would be true). Start
service, and then run
client with a few different input options and observe the results:
1 2 3 4 5 6 7 8 9
That’s all there is to it. A simple, synchronous server and client. The server and client do not directly depend on each other but are able to communicate unidirectionally through simple method calls.
Next time, I’ll go into detail on how we can create an asynchronous service and client, and hopefully utilize signals to add a new direction to our communication.
Again, all the code can be found on Github: https://github.com/larryprice/python-dbus-blog-series/tree/part1.