Goal for this part: learn enough to call a method.
How to read this:
☡ A Knuth-style Dangerous Bend sign ☡ precedes parts that should be skipped at first.
-
many processes
-
single machine
(☡ remote access is possible but non-standard)
Connect to a bus daemon.
☡ Connections without a bus, called peer connections, are less common. Notably PulseAudio uses one.
Choose between the system bus and the session bus (also called user bus), according to where your target service resides, and the library in your language will figure out the connection details.
☡ Some applications use a dedicated bus, for example desktop accessibility or Agama.
As an example, we will ask systemd for the default system target,
which will typically be graphical.target
for a GUI
or multi-user.target
for a console-only system.
You need two⁴ main things: an object and a method name.
The object is identified by an object path, like /org/freedesktop/systemd1
.
Method names are CamelCased (but your language binding may translate it to another convention).
⁴ Well actually we need four things: also the service that hosts the object, and the interface that the method belongs to.
Service, also known as connection or destination in some contexts,
is named like org.freedesktop.NetworkManager
☡ Each connection will also have a unique name like
:1.42
.
The interface names are typically prefixed by service names,
like org.freedesktop.NetworkManager.Device.Generic
.
Here we give busctl
the service, object, interface and method names
and get a string return value back:
$ busctl call \
org.freedesktop.systemd1 \
/org/freedesktop/systemd1 \
org.freedesktop.systemd1.Manager \
GetDefaultTarget
s "graphical.target"
You may wonder how we know the right names to use. Apart from reading documentation for humans, D-Bus specifies documentation for machines, so called introspection, and tools can show it to us.
This course aims to be language agnostic, which means we will use The One True Language, the shell.
The modern way to interact with D-Bus in the shell is busctl
(part of systemd).
For a graphical UI, use D-Feet.
The bare invocation shows you services on the system bus. Some named services are running, others will be activated automatically as you try to use them.
$ busctl
NAME PID PROCESS USER CONNECTION [more columns]
:1.13946 11253 busctl mvidner :1.13946
org.freedesktop.locale1 - - - (activatable)
org.freedesktop.systemd1 1 systemd root :1.0
org.freedesktop.NetworkManager 1179 NetworkManager root :1.7
... [more lines]
$ busctl tree org.freedesktop.systemd1
└─/org
└─/org/freedesktop
├─/org/freedesktop/LogControl1
└─/org/freedesktop/systemd1
├─/org/freedesktop/systemd1/job
└─/org/freedesktop/systemd1/unit
├─/org/freedesktop/systemd1/unit/ModemManager_2eservice
├─/org/freedesktop/systemd1/unit/NetworkManager_2dwait_2donline_2eservice
[500 more units]
Introspecting the main systemd object lists the GetDefaultTarget
method
under the org.freedesktop.systemd1.Manager
.
I've also included the org.freedesktop.DBus.Introspectable
interface
with its only Introspect
method which enables these examples.
$ busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.systemd1.Manager interface - - -
.GetDefaultTarget method - s -
(...)
.Architecture property s "x86-64" const
.Reloading signal b - -
Lastly, we also see several concepts that we will explain the next time: type, property, signal.
Two dangerous bends in part 1, I gotta be kidding? Well this is a topic that has bothered me from the very start.
Some namespacing is justified, as not everyone agrees on the choice of an editor or package manager.
But still, why not allow just busctl call /org/freedesktop/systemd1 GetDefaultTarget
, right?
Back when D-Bus was young, the services would expose their API on the root object /
and you would select the service to call. As the usage evolved, it became useful
to be able to migrate APIs across services and processes. This works best
if the object path stays the same, namespaced, and you only change the service name.
For details see the D-Bus API Design guidelines.
And what about the interface?
An easily apparent reason is disambiguating the sin
method
in the name.euler.Math
interface
from the one in the name.augustine.Morality
interface.
But I believe the actual reason is access control: some services allow or deny access
to certain interfaces+methods and thus would have to deny all method calls
trying to evade that by omitting the interface (spec link).
Some bindings, notably cockpit, lets you write only the object path and infers the service and interface names from it, if they follow the convention.