D-Bus (Desktop Bus) is a low-latency, low-overhead, easy-to-use message bus technology which supports application launch and linking. It is primarly used on
GNU/Linux desktops but has been ported to other platforms including
Microsoft Windows and
Apple Mac OS X.  This post provides a quick overview of
D-Bus concepts, some history, and some examples of how to use
D-Bus in your shell scripts.
Originally both the
KDE and
GNOME desktop projects used
CORBA for inter-application communication. Over time however, for various reasons,
KDE migrated from
CORBA to
Desktop Comunications Protocol (DCOP) and
GNOME migrated to
Bonono. This lead to the situation where
GNU/Linux desktop distributions had to support two different inter-application lauch and linking models and many standard desktop applications could not communicate seamlessly with each other. To ameliorate this unsatisfactory situation,
D-Bus (the name was suggested by
Harri Porten) was conceived and developed by
Red Hat as part of the
freedesktop.org project. The design of
D-Bus was heavily influenced by
DCOP. From the start, it was designed to be a replacement for the two competing technologies. The initial source code module was created by
Havoc Pennington in late 2002. Development was quite slow with many changes to the wire protocol. However by 2006 the specification was relatively stable. First
GNOME and then
KDE made the decision to transition to
D-Bus in order to support a single unified applcation linking and lauching technology on
GNU/linux desktops.
In many ways
D-Bus is similar to
Sun Microsystems ToolTalk which is the undelying technology in
Common Desktop Environment, and
Microsoft's Object Linking and Embedding (OLE) technology.
The basic
D-Bus protocol is a low latancy peer-to-peer or client-server binary protocol. It is not intended for inter-machine use but rather for intra-machine use. It works in terms of messages rather than byte streams. A
message bus is used when many-to-many communication is desired. Normally applications communicate via such a message bus but direct application-to-application communication is also possible.
When communicating on a message bus, applications can query which other applications and services are available, as well as activate one on demand. A
daemon, or service, must be launched before any applications can connect to a message bus. This
daemon is responsible for keeping track of the applications that are connected and for properly routing messages from source to destination. The
D-Bus specification defines two well-known buses called the
system bus and the
session bus. These buses are special in that they have well-defined semantics and associated services.
A
message bus can start (launch) an application on behalf of another application. An application that can be started in this way is called a
service and must have a well-known name. To find an application corresponding to a particular name, the bus daemon looks for service description files which are XML files that map names to applications. Different
message buses will look for these files in different places on a system. Once the application is launched, it registers a
service name.
Service names use reverse domain syntax (similar to
Java), e.g
org.freedesktop.Notifications.
D-Bus is object-orientated and supports the notion of named object interfaces. As in
C++, a method is an operation on an instantiated object. Methods may have optional typed parameters and may return typed values.
Signals are broadcasts from an object to any interested observer. All methods, method returns, signals, errors, etc. are encoded as
D-Bus messages consisting of a packet header with source and destination addresses, a type signature and the body containing the parameters (for signals and method calls) or the return values (for a method return). The type signature describes the types of the data contained within the message. D-Bus uses interfaces to provide a namespacing mechanism for methods. An interface is a group of related methods and signals, identified by a name which is a series of dot-separated components starting with a reversed domain name.
An application can expose one or more objects. An
object path is used to specify the address of each object. The syntax of an
object path is similar to that of the full pathname of a file on a Unix platform. For example,
/org/freedesktop/Notification is the
object path to an object provided by the
org.freedesktop.Notification service.
Service names,
interface names and
object paths do not have to be related in terms of their names but in many cases they are with the with the
object path typically being a slashed version of the dotted
service name as in the above example.
Messages buses can also expose interfaces, i.e.
org.freedesktop.DBus.
The
D-Bus specification mandates two special
D-Bus interfaces -
org.freedesktop.DBus.Introspect and
org.freedesktop.DBus.Properties. For our first example, let us ask the system message bus to list all it's methods.
dbus-send --system --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.freedesktop.DBus \
/org/freedesktop/DBus \
org.freedesktop.DBus.Introspectable.Introspect
method return sender=org.freedesktop.DBus -> dest=:1.51 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">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus">
<method name="Hello">
<arg direction="out" type="s"/>
</method>
<method name="RequestName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="ReleaseName">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="StartServiceByName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="UpdateActivationEnvironment">
<arg direction="in" type="a{ss}"/>
</method>
<method name="NameHasOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="b"/>
</method>
<method name="ListNames">
<arg direction="out" type="as"/>
</method>
<method name="ListActivatableNames">
<arg direction="out" type="as"/>
</method>
<method name="AddMatch">
<arg direction="in" type="s"/>
</method>
<method name="RemoveMatch">
<arg direction="in" type="s"/>
</method>
<method name="GetNameOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="s"/>
</method>
<method name="ListQueuedOwners">
<arg direction="in" type="s"/>
<arg direction="out" type="as"/>
</method>
<method name="GetConnectionUnixUser">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetConnectionUnixProcessID">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetAdtAuditSessionData">
<arg direction="in" type="s"/>
<arg direction="out" type="ay"/>
</method>
<method name="GetConnectionSELinuxSecurityContext">
<arg direction="in" type="s"/>
<arg direction="out" type="ay"/>
</method>
<method name="ReloadConfig">
</method>
<method name="GetId">
<arg direction="out" type="s"/>
</method>
<signal name="NameOwnerChanged">
<arg type="s"/>
<arg type="s"/>
<arg type="s"/>
</signal>
<signal name="NameLost">
<arg type="s"/>
</signal>
<signal name="NameAcquired">
<arg type="s"/>
</signal>
</interface>
</node>
"
The following command lists out the names of services which can be activated (launched).
dbus-send --system --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.freedesktop.DBus \
/org/freedesktop/DBus \
org.freedesktop.DBus.ListActivableNames
method return sender=org.freedesktop.DBus -> dest=:1.68 reply_serial=2
array [
string "org.freedesktop.DBus"
string "org.fedoraproject.Config.Services"
string "org.gnome.CPUFreqSelector"
string "org.freedesktop.ConsoleKit"
string "org.freedesktop.PackageKitAptBackend"
string "org.freedesktop.PackageKit"
string "org.freedesktop.NetworkManagerSystemSettings"
string "org.freedesktop.PackageKitTestBackend"
string "org.gnome.ClockApplet.Mechanism"
string "org.freedesktop.PolicyKit"
string "fi.epitest.hostap.WPASupplicant"
string "org.gnome.GConf.Defaults"
string "org.gnome.SystemMonitor.Mechanism"
string "org.freedesktop.nm_dispatcher"
]
Instead of using
dbus-send which requires a number of parameters, a simpler utility to use is
dbus which is part of the Google
dbus-cli package. Here is some sample output.
bash-3.2$ ./dbus
org.freedesktop.DBus
org.freedesktop.Notifications
org.freedesktop.PowerManagement
com.redhat.imsettings.IMInfo
org.gnome.SessionManager
org.gnome.GConf
org.freedesktop.PackageKit
com.redhat.imsettings
org.gnome.SettingsDaemon
com.redhat.setroubleshoot
org.gnome.ScreenSaver
org.bluez.applet
com.redhat.imsettings.GConf
Another useful utility is
qdbus.
# list all GNOME-related buses
$ qdbus | grep gnome
org.gnome.SessionManager
org.gnome.SettingsDaemon
org.gnome.ScreenSaver
org.gnome.GConf
# list ScreenSaver interfaces
bash-3.2$ qdbus org.gnome.ScreenSaver
/
# list methods for ScreenSaver interface
$ qdbus org.gnome.ScreenSaver /ScreenSaver
method QString org.freedesktop.DBus.Introspectable.Introspect()
signal void org.gnome.ScreenSaver.ActiveChanged(bool new_value)
signal void org.gnome.ScreenSaver.AuthenticationRequestBegin()
signal void org.gnome.ScreenSaver.AuthenticationRequestEnd()
method void org.gnome.ScreenSaver.Cycle()
method bool org.gnome.ScreenSaver.GetActive()
method uint org.gnome.ScreenSaver.GetActiveTime()
method QStringList org.gnome.ScreenSaver.GetInhibitors()
method bool org.gnome.ScreenSaver.GetSessionIdle()
method uint org.gnome.ScreenSaver.GetSessionIdleTime()
method uint org.gnome.ScreenSaver.Inhibit(QString application_name, QString reason)
method void org.gnome.ScreenSaver.Lock()
signal void org.gnome.ScreenSaver.SessionIdleChanged(bool new_value)
signal void org.gnome.ScreenSaver.SessionPowerManagementIdleChanged(bool new_value)
method void org.gnome.ScreenSaver.SetActive(bool value)
method void org.gnome.ScreenSaver.SimulateUserActivity()
method uint org.gnome.ScreenSaver.Throttle(QString application_name, QString reason)
method void org.gnome.ScreenSaver.UnInhibit(uint cookie)
method void org.gnome.ScreenSaver.UnThrottle(uint cookie)
# retrieve session idle time. Zero in this case as I have been working on this post.
$ qdbus org.gnome.ScreenSaver /ScreenSaver org.gnome.ScreenSaver.GetSessionIdleTime
0
# activate the screen saver - may or may not lock depending on settings
$ qdbus org.gnome.ScreenSaver /ScreenSaver org.gnome.ScreenSaver.SetActive True
# lock screen using the screensaver
$ qdbus org.gnome.ScreenSaver /ScreenSaver org.gnome.ScreenSaver.Lock
# query whether this computer can be suspended?
$ qdbus org.freedesktop.PowerManagement /org/freedesktop/PowerManagement org.freedesktop.PowerManagement.CanSuspend
true
You can also query devices via
HAL as the following examples show.$ dbus-send --system --print-reply --dest=org.freedesktop.Hal \
/org/freedesktop/Hal/devices/computer \
org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.0 -> dest=:1.106 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">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
<method>
<interface>
<interface name="org.freedesktop.Hal.Device">
........
<interface>
<interface name="org.freedesktop.Hal.Device.SystemPowerManagement">
........
<interface>
<interface name="org.freedesktop.Hal.Device.CPUFreq">
........
<method name="GetCPUFreqAvailableGovernors">
<arg name="return_code" direction="out" type="as"/>
<method>
<interface>
<node>
"
$ dbus-send --system --print-reply --dest=org.freedesktop.Hal \
/org/freedesktop/Hal/devices/computer \
org.freedesktop.Hal.Device.CPUFreq.GetCPUFreqAvailableGovernors
method return sender=:1.0 -> dest=:1.109 reply_serial=2
array [
string "ondemand"
string "userspace"
string "performance"
]
$ dbus-send --system --print-reply --dest=org.freedesktop.Hal \
/org/freedesktop/Hal/Manager \
org.freedesktop.Hal.Manager.GetAllDevices
method return sender=:1.0 -> dest=:1.115 reply_serial=2
array [
string "/org/freedesktop/Hal/devices/volume_part_1_size_403367936"
string "/org/freedesktop/Hal/devices/net_46_b2_32_e5_29_04"
string "/org/freedesktop/Hal/devices/computer_logicaldev_input_0"
string "/org/freedesktop/Hal/devices/usb_device_45e_734_noserial_if0_logicaldev_input"
string "/org/freedesktop/Hal/devices/usb_device_45e_734_noserial_if1_logicaldev_input"
string "/org/freedesktop/Hal/devices/computer_logicaldev_input"
string "/org/freedesktop/Hal/devices/volume_uuid_9e1859de_0cf0_4048_96f5_4ef32ac904f7"
string "/org/freedesktop/Hal/devices/computer"
string "/org/freedesktop/Hal/devices/volume_part2_size_205632000"
string "/org/freedesktop/Hal/devices/volume_uuid_a332a6d6_a034_4f19_b470_bacf7ffa81e2"
string "/org/freedesktop/Hal/devices/volume_uuid_RyytQp_f73U_LzdN_6JjC_08yN_Aw3e_p3fNF1"
string "/org/freedesktop/Hal/devices/storage_model_DVD_Writer_1070d"
string "/org/freedesktop/Hal/devices/platform_serial8250_serial_platform_1"
string "/org/freedesktop/Hal/devices/storage_serial_SATA_ST3500320AS_9QM49NMQ"
string "/org/freedesktop/Hal/devices/storage_serial_SATA_ST3500320AS_9QM4GKDK"
.........
]
$ dbus-send --system --print-reply --dest=org.freedesktop.Hal \
/org/freedesktop/Hal/devices/storage_model_DVD_Writer_1070d
org.freedesktop.Hal.Device.GetProperty string:'info.vendor'
method return sender=:1.0 -> dest=:1.120 reply_serial=2
string "HP"
While D-Bus was not really designed with shell scripting in mind, it is possible to use various utilities to access system and session data via D-Bus. I hope you have not found this post too long-winded and that you will go away and experiment with D-Bus on your own computer. Unfortunately there is a lot of out-of-date information, terminology and examples on the Internet relating to D-Bus. Always refer back to the specification when in doubt. All the examples included on this port were tested on Fedora 10.
P.S. I plan to cover scripting D-Bus signals in a future post.