DBus
D-Bus is:
D-Bus supplies both:
The message bus is built on top of a general one-to-one message passing framework, which can be used by any two apps to communicate directly (without going through the message bus daemon). Although, many applications use it with the message bus daemon.
Currently the communicating applications are on one computer, or through unencrypted TCP/IP suitable for use behind a firewall with shared NFS home directories.
The low-level libdbus reference library has no required dependencies; the reference bus daemon's only required dependency is an XML parser (expat). Higher-level bindings specific to particular frameworks (Qt, GLib, Java, C#, Python, etc.) add more dependencies, but can make more assumptions and are thus much simpler to use. Bindings Page. libdbus only supports one-to-one connections, just like a raw network socket.
There is a maximum name length of 255 which applies to bus names, interfaces, and members.
Interfaces have names with type STRING, meaning that they must be valid UTF-8.
There are the standard interfaces for each Object.
The org.freedesktop.DBus.Peer
interface has two methods:
This org.freedesktop.DBus.Introspectable
has one method:
Objects instances may implement Introspect which returns an XML description of the object, including its interfaces (with signals and methods), objects below it in the object path tree, and its properties.
The section “Introspection Data Format” describes the format of this XML string.
Many native APIs will have a concept of object properties or attributes. These can be exposed via the org.freedesktop.DBus.Properties
interface.
If one or more properties change on an object, the org.freedesktop.DBus.Properties.PropertiesChanged signal may be emitted:
where changed_properties is a dictionary containing the changed properties with the new values and invalidated_properties is an array of properties that changed but the value is not conveyed.
An API can optionally make use of this interface for one or more sub-trees of objects. The root of each sub-tree implements this interface so other applications can get all objects, interfaces and properties in a single method call. It is appropriate to use this interface if users of the tree of objects are expected to be interested in all interfaces of all objects in the tree; a more granular API should be used if users of the objects are expected to be interested in a small subset of the objects, a small subset of their interfaces, or both.
The method that applications can use to get all objects and properties is GetManagedObjects:
The return value of this method is a dict whose keys are object paths. All returned object paths are children of the object path implementing this interface, i.e. their object paths start with the ObjectManager's object path plus '/'.
Each value is a dict whose keys are interfaces names. Each value in this inner dict is the same dict that would be returned by the org.freedesktop.DBus.Properties.GetAll()
method for that combination of object path and interface. If an interface has no properties, the empty dict is returned.
Changes are emitted using the following two signals:
Connections have one or more bus names associated with them. A connection has exactly one bus name that is a unique connection name. The unique connection name remains with the connection for its entire lifetime. A bus name is of type STRING, meaning that it must be valid UTF-8.
Member (i.e. method or signal) names:
It is conventional for member names on D-Bus to consist of capitalized words with no punctuation ("camel-case").
GetItems
ItemsChanged
.Error names have the same restrictions as interface names.
Error names have the same naming conventions as interface names, and often contain .Error.; for instance, the owner of example.com
might define the errors com.example.MusicPlayer1.Error.FileNotFound
and com.example.MusicPlayer1.Error.OutOfMemory
. The errors defined by D-Bus itself, such as org.freedesktop.DBus.Error.Failed
, follow a similar pattern.
Each of the message types (METHOD_CALL
, METHOD_RETURN
, ERROR
, and SIGNAL
) has its own expected usage conventions and header fields.
Some messages invoke an operation on a remote object. These are called method call messages and have the type tag METHOD_CALL.
Optionally, the message has an INTERFACE
field giving the interface the method is a part of. Including the INTERFACE
in all method call messages is strongly recommended.
Method call messages also include a PATH
field indicating the object to invoke the method on. If the call is passing through a message bus, the message will also have a DESTINATION field giving the name of the connection to receive the message.
When an application handles a method call message, it is required to return a reply. The reply is identified by a REPLY_SERIAL header field indicating the serial number of the METHOD_CALL being replied to. The reply can have one of two types; either METHOD_RETURN
or ERROR
.
METHOD_RETURN
, the arguments to the reply message are the return value(s) or "out parameters" of the method call.ERROR
, then an "exception" has been thrown, and the call fails; no return value will be provided.A signal emission is simply a single message of type SIGNAL
. It must have three header fields: PATH
giving the object the signal was emitted from, plus INTERFACE
and MEMBER
giving the fully-qualified name of the signal. The INTERFACE
header is required for signals, though it is optional for method calls.
The message bus accepts connections from one or more applications. Once connected, applications can exchange messages with other applications that are also connected to the bus.
In order to route messages among connections, the message bus keeps a mapping from names to connections. Each connection has one unique-for-the-lifetime-of-the-bus name automatically assigned. Applications may request additional names for a connection. Additional names are usually "well-known names" such as "com.example.TextEditor1". When a name is bound to a connection, that connection is said to own the name.
The bus itself owns a special name, org.freedesktop.DBus
, with an object located at /org/freedesktop/DBus
that implements the org.freedesktop.DBus
interface. This service allows applications to make administrative requests of the bus itself. For example, applications can ask the bus to assign a name to a connection.
Each name may have queued owners. When an application requests a name for a connection and the name is already in use, the bus will optionally add the connection to a queue waiting for the name. If the current owner of the name disconnects or releases the name, the next connection in the queue will become the new owner.
This feature causes the right thing to happen if you start two text editors for example; the first one may request "com.example.TextEditor1", and the second will be queued as a possible owner of that name. When the first exits, the second will take over.
Applications may send unicast messages to a specific recipient or to the message bus itself, or broadcast messages to all interested recipients.
Each connection has at least one name, assigned at connection time and returned in response to the org.freedesktop.DBus.Hello
method call. This automatically-assigned name is called the connection's unique name. Unique names are never reused for two different connections to the same bus.
Ownership of a unique name is a prerequisite for interaction with the message bus. It logically follows that the unique name is always the first name that an application comes to own, and the last one that it loses ownership of.
When a connection is closed, all the names that it owns are deleted (or transferred to the next connection in the queue if any).
Messages may have a DESTINATION
field, resulting in a unicast message. If the DESTINATION
field is present, it specifies a message recipient by name. Method calls and replies normally specify this field. The message bus must send messages (of any type) with the DESTINATION
field set to the specified recipient, regardless of whether the recipient has set up a match rule matching the message.
When the message bus receives a signal, if the DESTINATION
field is absent, it is considered to be a broadcast signal, and is sent to all applications with message matching rules that match the message. Most signal messages are broadcasts, and no other message types currently defined in this specification may be broadcast.
A computer may have a system message bus, accessible to all applications on the system. This message bus may be used to broadcast system events, such as adding new hardware devices, changes in the printer queue, and so forth.
D-Bus is designed for two specific cases:
When each application connects to the bus daemon, the daemon immediately assigns it a name, called the unique connection name. A unique name begins with a ':' (colon) character. These names are never reused during the lifetime of the bus daemon - that is, you know a given name will always refer to the same application. An example of a unique name might be :34-907. The numbers after the colon have no meaning other than their uniqueness.
When a name is mapped to a particular application's connection, that application is said to own that name.
Applications may ask to own additional well-known names. For example, you could write a specification to define a name called com.mycompany.TextEditor. Your definition could specify that to own this name, an application should have an object at the path /com/mycompany/TextFileManager supporting the interface org.freedesktop.FileHandler.
Applications could then send messages to this bus name, object, and interface to execute method calls.
You could think of the unique names as IP addresses, and the well-known names as domain names. So com.mycompany.TextEditor
might map to something like :34-907
just as mycompany.com
maps to something like 192.168.0.5
.
Names have a second important use, other than routing messages. They are used to track lifecycle. When an application exits (or crashes), its connection to the message bus will be closed by the operating system kernel. The message bus then sends out notification messages telling remaining applications that the application's names have lost their owner. By tracking these notifications, your application can reliably monitor the lifetime of other applications.
Bus names can also be used to coordinate single-instance applications. If you want to be sure only one com.mycompany.TextEditor
application is running for example, have the text editor application exit if the bus name already has an owner.
Applications using D-Bus are either servers or clients. A server listens for incoming connections; a client connects to a server. Once the connection is established, it is a symmetric flow of messages; the client-server distinction only matters when setting up the connection.
If you're using the bus daemon, as you probably are, your application will be a client of the bus daemon. That is, the bus daemon listens for connections and your application initiates a connection to the bus daemon.
A D-Bus address specifies where a server will listen, and where a client will connect. For example, the address unix:path=/tmp/abcdef
specifies that the server will listen on a UNIX domain socket at the path /tmp/abcdef
and the client will connect to that socket. An address can also specify TCP/IP sockets, or any other transport defined in future iterations of the D-Bus specification.
When using D-Bus with a message bus daemon, libdbus automatically discovers the address of the per-session bus daemon by reading an environment variable. It discovers the systemwide bus daemon by checking a well-known UNIX domain socket path (though you can override this address with an environment variable).
Pulling all these concepts together, to specify a particular method call on a particular object instance, a number of nested components have to be named:
Address -> [Bus Name] -> Object Path -> Interface -> Method
D-Bus works by sending messages between processes.
There are 4 message types:
$ dbus-send --print-reply --system --dest=org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower.EnumerateDevices
method return time=1710584709.846161 sender=:1.24 -> destination=:1.293 serial=982 reply_serial=2
array [
object path "/org/freedesktop/UPower/devices/battery_BAT0"
object path "/org/freedesktop/UPower/devices/line_power_AC"
object path "/org/freedesktop/UPower/devices/line_power_ucsi_source_psy_USBC000o001"
]
$ dbus-send --print-reply --system --dest=org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT0 org.freedesktop.DBus.Properties.GetAll string:org.freedesktop.UPower.Device
method return time=1710585125.085607 sender=:1.24 -> destination=:1.299 serial=1001 reply_serial=2
array [
dict entry(
string "NativePath"
variant string "BAT0"
)
dict entry(
string "Vendor"
variant string "BYD"
)
dict entry(
string "Model"
variant string "DELL WV3K832"
)
dict entry(
string "Serial"
variant string "11861"
)
dict entry(
string "UpdateTime"
variant uint64 1710585119
)
dict entry(
string "Type"
variant uint32 2
)
dict entry(
string "PowerSupply"
variant boolean true
)
dict entry(
string "HasHistory"
variant boolean true
)
dict entry(
string "HasStatistics"
variant boolean true
)
dict entry(
string "Online"
variant boolean false
)
dict entry(
string "Energy"
variant double 53.715
)
dict entry(
string "EnergyEmpty"
variant double 0
)
dict entry(
string "EnergyFull"
variant double 53.715
)
dict entry(
string "EnergyFullDesign"
variant double 54
)
dict entry(
string "EnergyRate"
variant double 0.015
)
dict entry(
string "Voltage"
variant double 16.306
)
dict entry(
string "ChargeCycles"
variant int32 3
)
dict entry(
string "Luminosity"
variant double 0
)
dict entry(
string "TimeToEmpty"
variant int64 12891600
)
dict entry(
string "TimeToFull"
variant int64 0
)
dict entry(
string "Percentage"
variant double 100
)
dict entry(
string "Temperature"
variant double 25.9
)
dict entry(
string "IsPresent"
variant boolean true
)
dict entry(
string "State"
variant uint32 4
)
dict entry(
string "IsRechargeable"
variant boolean true
)
dict entry(
string "Capacity"
variant double 99.4722
)
dict entry(
string "Technology"
variant uint32 0
)
dict entry(
string "WarningLevel"
variant uint32 1
)
dict entry(
string "BatteryLevel"
variant uint32 1
)
dict entry(
string "IconName"
variant string "battery-full-charged-symbolic"
)
]
$ sudo dbus-monitor --system "path='/org/freedesktop/UDisks2'"
signal time=1711795576.609034 sender=org.freedesktop.DBus -> destination=:1.612 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string ":1.612"
signal time=1711795576.609065 sender=org.freedesktop.DBus -> destination=:1.612 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
string ":1.612"
signal time=1711795582.184807 sender=:1.62 -> destination=(null destination) serial=205 path=/org/freedesktop/UDisks2; interface=org.freedesktop.DBus.ObjectManager; member=InterfacesAdded
object path "/org/freedesktop/UDisks2/drives/General_UDisk_General_UDisk_0_3a0"
array [
dict entry(
string "org.freedesktop.UDisks2.Drive"
array [
dict entry(
string "Vendor"
variant string "General"
)
dict entry(
string "Model"
variant string "UDisk"
)
dict entry(
string "Revision"
variant string "5.00"
)
dict entry(
string "Serial"
variant string "General_UDisk-0:0"
...
io.starnight.dbus_test.TestServer
/io/starnight/dbus_test/TestObject
io.starnight.dbus_test.TestInterface
$ dbus-send --session --print-reply \
--type=method_call \
--dest=io.starnight.dbus_test.TestServer \
/io/starnight/dbus_test/TestObject \
org.freedesktop.DBus.Introspectable.Introspect
method return time=1711180494.668633 sender=:1.188 -> destination=:1.189 serial=3 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.78.4 -->
<node>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="out"/>
</method>
<method name="GetAll">
<arg type="s" name="interface_name" direction="in"/>
<arg type="a{sv}" name="properties" direction="out"/>
</method>
<method name="Set">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="in"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface_name"/>
<arg type="a{sv}" name="changed_properties"/>
<arg type="as" name="invalidated_properties"/>
</signal>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg type="s" name="xml_data" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg type="s" name="machine_uuid" direction="out"/>
</method>
</interface>
<interface name="io.starnight.dbus_test.TestInterface">
<method name="Login">
<arg type="s" name="greeting" direction="in">
</arg>
<arg type="s" name="response" direction="out">
</arg>
</method>
<method name="SendMsg">
<arg type="s" name="msg" direction="in">
</arg>
</method>
<signal name="MsgNotification">
<arg type="s" name="sender">
</arg>
<arg type="s" name="msg">
</arg>
</signal>
<property type="s" name="Title" access="readwrite">
</property>
</interface>
</node>
"
$ dbus-monitor --session "path='/io/starnight/dbus_test/TestObject'"
signal time=1711295334.594089 sender=org.freedesktop.DBus -> destination=:1.207 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string ":1.207"
signal time=1711295334.594132 sender=org.freedesktop.DBus -> destination=:1.207 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
string ":1.207"
method call time=1711295345.911629 sender=:1.210 -> destination=:1.206 serial=36 path=/io/starnight/dbus_test/TestObject; interface=org.freedesktop.DBus.Properties; member=GetAll
string "io.starnight.dbus_test.TestInterface"
method call time=1711295355.430745 sender=:1.210 -> destination=:1.206 serial=45 path=/io/starnight/dbus_test/TestObject; interface=io.starnight.dbus_test.TestInterface; member=SendMsg
string "Hello world!"
signal time=1711295355.431750 sender=:1.206 -> destination=(null destination) serial=4 path=/io/starnight/dbus_test/TestObject; interface=io.starnight.dbus_test.TestInterface; member=MsgNotification
string ":1.210"
string "Hello world!"