Exposing a D-Bus Interface in Linux — Part 1

From basic concepts to code

Rodrigo Perazzo
CESAR Update
4 min readMar 14, 2018

--

While reviewing code for the KNoT platform (powered by CESAR) and checking how some components inside the IoT Gateway communicate with each other, I couldn’t find a single place or article with all details required to understand, write and debug a Linux service that exposes a D-Bus interface. So I decided to make one (or a few ones, as I’m fond of quick reading posts).

I hope that doing this we decrease the barrier to contribute to KNoT projects and make easier to new developers understand what probably only sound obvious for developers with a strong experience on Linux.

KNoT Gateway — Internal Components Communication

What is D-Bus?

D-Bus is a inter-process communication (IPC) and remote procedure call (RPC) mechanism, like Binder in Android, that was designed specifically to facilitate multiple applications in the same machine to communicate with each other and also to make easy for them to communicate with the OS, including kernel, system daemons and system processes.

System x Session Message Bus

D-Bus employs two message buses: system and session. The first one is a machine wide privileged daemon, like inetd and httpd, that implies heavy restrictions on what type of messages are allowed. The second one is created each time a user logs into the desktop and it’s attached to that user’s context. I’m going to focus on system bus, since it requires an extra setup and it’s the one used on KNoT, but APIs used later are identical for session bus.

Setup

Before trying to write anything is important to check if everything is running as it should and that you have the right privileges to debug and use system bus.

First of all, the dbus-daemon should be running. Why? Because it acts as a bridge between processes, receiving messages from the sender app and redirecting to the target app, which by the way is done via sockets. So, without dbus-daemon there is no D-Bus to work with.

ubuntu@ubuntu-xenial:~$ ps aux | grep dbusmessage+  1143  0.0  0.0  42908  3860 ?        Ss   20:25   0:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation

Debugging

So, if the dbus-daemon is already running, you’re probably able to check what kind of messages are being passed through it. For that we’re going to use the dbus-monitor command.

The dbus-monitor command by default show messages going through the current user session bus, but you can use options --system and --session to decide which message bus you want to monitor and option --address if you’re running as root and want to see messages from other users (like this case here).

Unfortunately, the default D-Bus policy (at least on Ubuntu) prevents most of the messages (except signals) that goes through system bus from being viewable by dbus-monitor, so trying to monitoring it should show something like that:

ubuntu@ubuntu-xenial:~$ dbus-monitor --systemdbus-monitor: unable to enable new-style monitoring: org.freedesktop.DBus.Error.AccessDenied: "Rejected send message, 1 matched rules; type="method_call", sender=":1.13" (uid=1000 pid=19867 comm="dbus-monitor --system ") interface="org.freedesktop.DBus.Monitoring" member="BecomeMonitor" error name="(unset)" requested_reply="0" destination="org.freedesktop.DBus" (bus)". Falling back to eavesdropping.

To change that we need to set a global policy to be able to eavesdrop anything after the individual /etc/dbus-1/system.d/*.conf files applied their restrictions, in order words, create a file /etc/dbus-1/system-local.conf with the following contents:

Then reboot machine to pick up the configuration changes. Now running dbus-monitor as root should display everything going through system bus:

ubuntu@ubuntu-xenial:~$ sudo dbus-monitor --systemsignal time=1520958487.574555 sender=org.freedesktop.DBus -> destination=:1.18 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string “:1.18”
method call time=1520958487.741542 sender=:1.4 -> destination=org.freedesktop.ConsoleKit serial=185 path=/org/freedesktop/ConsoleKit/Manager; interface=org.freedesktop.ConsoleKit.Manager; member=GetSessions

It is also possible to filter those messages by providing type and/or sender arguments surrounded by double quotes:

ubuntu@ubuntu-xenial:~$ sudo dbus-monitor "type=error,sender='org.freedesktop.DBus'" --systemerror time=1521049052.321314 sender=org.freedesktop.DBus -> destination=:1.5 error_name=org.freedesktop.DBus.Error.ServiceUnknown reply_serial=8368
string "The name org.freedesktop.ConsoleKit was not provided by any .service files"

This is very useful to filter only messages from our services. I had only one issue using dbus-monitor: After a while, it stopped showing messages due to a connection issue. Deleting files under /var/run/dbus and reloading the machine did the trick for me.

sudo rm -Rf /var/run/dbus/*

D-Feet

D-Feet is a graphical debugger for D-Bus and it can be used to inspect D-Bus interfaces of running programs and invoke methods on those interfaces. You can monitor both System and Session bus, as you can see below:

If you have installed a GUI on your Linux distro, you can install D-Feet directly on that machine or use D-Feet via TCP, as it was described here. I didn’t try it myself since I usually run Linux on VMs without GUI and there is no D-Feet for MacOS, but if you can, you definitely should try it.

I think it is enough for this first part, now you can download KNoT gateway image, flash it to your Raspberry Pi 3 and starting sniffing around how KNoT services communicate with each other. :)

On next articles, we’re going to understand how to define a D-Bus interface, what of type messages exist and how to write code to make use of all of that!

Special thanks to @cktakahasi, Tiago Barros, Thiago Figueredo Cardoso and Victor Rattis who reviewed this post and gave me useful feedback.

References

--

--

Rodrigo Perazzo
CESAR Update

Dad & Husband. SW / Android / Arduino / IoT Engineer @inovacao_cesar. In pursuit of good habits for life.