Skip to content

Instantly share code, notes, and snippets.

@mvidner
Last active March 29, 2023 11:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mvidner/ea606c22bc02ff98dd5a0d2ece313ee1 to your computer and use it in GitHub Desktop.
Save mvidner/ea606c22bc02ff98dd5a0d2ece313ee1 to your computer and use it in GitHub Desktop.

D-Bus Crash Course: Part 1

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.

What is D-Bus?

D-Bus is an IPC system:

  • many processes

  • single machine

    (☡ remote access is possible but non-standard)

Where and How do I connect?

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.

How do I call a method?

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.

Calling the example method

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.

☡ Beyond the bare minimum

Listing available services

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]

Listing objects for a service

$ 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]

Listing methods for an object

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.

☡☡ Why do I need to type the long names?

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment