Basic dbus conceptsWhat is dbus?dbus is part of most modern Linux distros. It handles interprocess communication, either between desktop applications or between systems daemons and applications (e.g. a desktop framework like Gnome or KDE, or user applications).
OverviewThe goal of this series of postings is the creation of a small desktop example application written in Python which reads the name of the files on a CD or DVD after the media has been inserted, and to store these names together with the CD label in a file. The graphical user interface will be based on wxWidgets.
The first part contains an introduction to the basic dbus components.
The second part discusses the implementation using the Hardware Abstraction Layer (HAL).
The following part shows the differences in implementation between HAL to the DeviceKit.Disks.
PrerequisitesPython: which is part of any current Linux distribution. (I'm using Python 2.6).
python-dbus: the dbus bindings for Python
python-wxgtk2.8: wxWidgets and its Python bindings (for the GUI)
Useful additionspython-wxglade: GUI builder for wxWidgets, creates Python source code (and C source code...)
d-feet: graphical display of dbus components
dbus componentsIn our example dbus is used to catch the notifications sent by the kernel when a CD or DVD has been inserted, as well as the notification when Gnome has mounted this medium. (There were times when you had to do this manually.)
In some places I will differ from the official dbus designations for these components to make their distinction easier. In some cases different components share the same name. This makes their close relation quite obvious, but it becomes difficult to determine if a given name refers to a connection or an interface, for example.
BusesIn dbus “buses” are at the root of a hierarchy. In normal applications you encounter mostly the following two buses:
bus = dbus.SessionBus()for communication between desktop applications, and
bus = dbus.SystemBus()for communication with system programs and daemons like the NetworkManager, the ModemManager, the Hardware Abstraction Layer daemon (HAL) or DeviceKit, to name but a few.
When you insert a disc, a notification is routed via HAL or DeviceKit, depending on which daemon is installed.
Note: Ubuntu Karmic uses HAL and DeviceKit simultaneously. However, the Hardware Abstraction Layer is being phased out from most Linux distributions, and the upcoming version of Ubuntu 10.04 (Lucid Lynx) will drop it completely.
Connections“Connections” are the layer below the buses. You can use a "connection" to “connect” to a specific daemon or application. Please note the dotted notation:
The NetworkManager is available via a connection named
org.freedesktop.NetworkManger.
The connection
org.freedesktop.Hal will connect you to the HAL daemon.
The "disks" part of DeviceKit is available via the connection named
org.freedesktop.DeviceKit.Disks.
ObjectsThese programs (NetworkManager, HAL, DeviceKit.Disks, etc.) will offer their services via one or more "objects". Object names contain slashes and look like file paths, which might be the reason that they are sometimes called "paths".
Examples:
/org/freedesktop/Hal/devices/org/freedesktop/Hal/Manager
/org/freedesktop/Hal/devices/volume_label_backupcd1________/org/freedesktop/DeviceKit/Disks/devices/sr0 InterfacesObjects export methods and and signals grouped into one or more "interfaces". The names of these interfaces again contain dots, e.g.
org.freedesktop.Hal.Manager or
org.freedesktop.DeviceKit.Disks.Device.
MethodsMethods are functions you can invoke. One method of the
org.freedesktop.Hal.Manager interface for example is
DeviceExists. The interface
org.freedesktop.DeviceKit.Disks implements e.g.
EnumerateDevices, etc.
SignalsAn interface may also export "signals". Signals of the
org.freedesktop.Hal.Manager interface that we are going to use are
DeviceAdded and
DeviceRemoved, which are triggered when you put a new CD into the drive or eject it, but also if any other device is added (an USB stick, a new network interface, etc.), as well as the signal
PropertiesModified.
IntrospectOne interface that is implemented by all objects is
org.freedesktop.DBus.Introspectable which offers one method:
Introspect().
This function returns a description of the object's interfaces, i.e. what methods and signals are available and what parameters they use. The above mentioned utility d-feet uses this method to display the information in a nice graphical way. (Note: In d-feet a "Connection" is called "Bus Name").
If you want to see the dbus messaging at work (for the SystemBus) open a shell and start:
dbus-monitor --systemSummary- bus: top level (SessionBus or SystemBus)
- connection: from the bus to a daemon; names with dots
- object: logical part within that daemon; names with slashes
- interface: one or more within an object (same interface on different objects means same API (methods and signals)); names with dots
- methods and signals (none or more within an object)
ExamplesHow to call a method (HAL)In order to call a method like
DeviceExists, you have to know, that this particular function is
- part of the interface org.freedesktop.Hal.Manager
- that this interface is implemented by the object named /org/freedesktop/Hal/Manager
- which in turn can be reached via the connection org.freedesktop.Hal
- on the SystemBus
In Python:# (4)bus = dbus.SystemBus()# (3 + 2)object = bus.get_object("org.freedesktop.Hal","/org/freedesktop/Hal/Manager")# (1)interface = dbus.Interface(object,"org.freedesktop.Hal.Manager")#result = interface.DeviceExists(...)As mentioned earlier, you have to know which parameter of
bus.get_object and
dbus.Interface is the object name, the connection name, and the interface name. The names themselves are usually not a great help.
Signal callbacks (HAL)In order to make dbus call a method in your program when a signal is triggered you have to add a signal receiver:
bus.add_signal_receiver( handler_name_in_your_program, "signal_name", "interface_name", "connection_name", "object_name")The handler method looks like this:
def handler(...): ...The parameters used when this handler is executed (the so called "signature") differ from signal to signal. (This info can be obtained via the
Introspect interface.) Here the example for the methods
DeviceAdded and
DeviceRemoved:
bus = dbus.SystemBus()bus.add_signal_receiver(device_added_callback, "DeviceAdded", "org.freedesktop.Hal.Manager", "org.freedesktop.Hal", "/org/freedesktop/Hal/Manager")bus.add_signal_receiver(device_removed_callback, "DeviceRemoved", "org.freedesktop.Hal.Manager", "org.freedesktop.Hal", "/org/freedesktop/Hal/Manager")....def device_added_callback(udi): print udidef device_removed_callback(udi): print udi(More on add_signal_receiver in the next post)
How to call a function (DeviceKit.Disks)The fundamental dbus principles described in the HAL example still apply:
# (4)bus = dbus.SystemBus()# (3 + 2)devkit_object = bus.get_object("org.freedesktop.DeviceKit.Disks", "/org/freedesktop/DeviceKit/Disks")# (1)devkit_disks = dbus.Interface(devkit_object, 'org.freedesktop.DeviceKit.Disks')#all_devices = devkit_disks.EnumerateDevices()Note: This time the connection/object/interface names are nearly identical. Keep in mind which parameter describes what
Signal callbacks (DeviceKit.Disks)Even tough a few handler names are identical to HAL and their functions are similar, their parameters are different. Also note that the way
DeviceKit.Disks adds callbacks is different:
devkit_disks.connect_to_signal('DeviceAdded', device_added_callback)devkit_disks.connect_to_signal('DeviceRemoved', device_removed_callback)devkit_disks.connect_to_signal('DeviceChanged', device_changed_callback)def device_added_callback(device): # not really needed print 'Device %s was added' % (device)def device_removed_callback(device): # not really needed print 'Device %s was removed' % (device) def device_changed_callback(device): print 'Device %s was changed' % (device) Other daemonsThere are other interesting daemons, like the
ModemManager, which not only can control modems, but also allows access to your mobile phone, SMS etc.