11. Application bus
POSIX API (signals, messages, shared memory): Disadvantages
- No message data structure is provided (just number or raw memory)
- → can not verify
- No multicast (one-to-many)
No organized namespace (remember /dev/shm names clash?)
- …
Way too redundant, being solely implemented in kernel-space
History:
- Every application suit is developing it's own high-level IPC
Corba — try to standard
- too overdesigned
D-Bus
Concepts:
- API
- Cross-platform implementation (e. g. on Linux is probably on sockets, but who cares)
- Has dedicated server
- Any client:
- Connects to the bus
- Can browse all service published
- Can call for some service
- Can publish own service and answer for call
- Can subscribe for some broadcast messages (signals)
- Can publish a signal emitter
- Has joint namespace with simple naming conventions to avoid clash
- Standardized by freedesktop.org
- All published objects/methods are introspectable
- Linux: there are at lease two d-buses:
- system — to control system services (by admin)
- session (user) — to control user services (by user)
Architecture
Connection ID and published well-known name (looks like backward worded domain name). Analog of domain name in URL.
- Objects. Analog of filename path in URL.
Of program announce one object, it's path will probably look like well-known name with "." replaced with "/" with leading "/"
One program can announce more than one object
- Interfaces. The suite of announced methods. Analog of Ada of Java interfaces.
- Methods/Signals/Properties.
TODO
Linux
- Two or three message buses (additional bus for accessibility environment):
message+ 2344 1 0 15:28 ? 00:00:10 /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only george 29316 29238 0 19:08 ? 00:00:00 /bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only george 29351 29335 0 19:08 ? 00:00:00 /bin/dbus-daemon --config-file=/usr/share/defaults/at-spi2/accessibility.conf --nofork --print-address 3
D-Bus programming
Useful software:
dbus-send, dbus-monitor from core dbus
busctl from systemd (recommended)
Overview
- Run 'd-feet' to see the installed services:
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ d-feet
See list of dbus command-line tools (press Tab after dbus- to get completion work):
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ dbus- dbus-cleanup-sockets dbus-launch dbus-run-session dbus-update-activation-environment dbus-daemon dbus-monitor dbus-send dbus-uuidgen
Use the 'dbus-send' tool to introspect the properties of NetworkManager:
1 andrewt@comp-core-i7-3615qm-0dbf32 ~ $ dbus-send --system --print-reply --dest=org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/Devices/1 org.freedesktop.DBus.Introspectable.Introspect 2 method return time=1590384778.681292 sender=:1.6 -> destination=:1.141 serial=3362 reply_serial=2 3 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" 4 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> 5 <!-- GDBus 2.60.7 --> 6 <node> 7 <interface name="org.freedesktop.DBus.Properties"> 8 <method name="Get"> 9 <arg type="s" name="interface_name" direction="in"/> 10 <arg type="s" name="property_name" direction="in"/> 11 <arg type="v" name="value" direction="out"/> 12 </method> 13 <method name="GetAll"> 14 <arg type="s" name="interface_name" direction="in"/> 15 <arg type="a{sv}" name="properties" direction="out"/> 16 </method> 17 <method name="Set"> 18 <arg type="s" name="interface_name" direction="in"/> 19 <arg type="s" name="property_name" direction="in"/> 20 <arg type="v" name="value" direction="in"/> 21 </method> 22 <signal name="PropertiesChanged"> 23 <arg type="s" name="interface_name"/> 24 <arg type="a{sv}" name="changed_properties"/> 25 <arg type="as" name="invalidated_properties"/> 26 </signal> 27 </interface> 28 <interface name="org.freedesktop.DBus.Introspectable"> 29 <method name="Introspect"> 30 <arg type="s" name="xml_data" direction="out"/> 31 </method> 32 </interface> 33 <interface name="org.freedesktop.DBus.Peer"> 34 <method name="Ping"/> 35 <method name="GetMachineId"> 36 <arg type="s" name="machine_uuid" direction="out"/> 37 </method> 38 </interface> 39 <interface name="org.freedesktop.NetworkManager.Device.Statistics"> 40 <signal name="PropertiesChanged"> 41 <arg type="a{sv}" name="properties"/> 42 </signal> 43 <property type="u" name="RefreshRateMs" access="readwrite"/> 44 <property type="t" name="TxBytes" access="read"/> 45 <property type="t" name="RxBytes" access="read"/> 46 </interface> 47 <interface name="org.freedesktop.NetworkManager.Device.Generic"> 48 <signal name="PropertiesChanged"> 49 <arg type="a{sv}" name="properties"/> 50 </signal> 51 <property type="s" name="HwAddress" access="read"/> 52 <property type="s" name="TypeDescription" access="read"/> 53 </interface> 54 <interface name="org.freedesktop.NetworkManager.Device"> 55 <method name="Reapply"> 56 <arg type="a{sa{sv}}" name="connection" direction="in"/> 57 <arg type="t" name="version_id" direction="in"/> 58 <arg type="u" name="flags" direction="in"/> 59 </method> 60 <method name="GetAppliedConnection"> 61 <arg type="u" name="flags" direction="in"/> 62 <arg type="a{sa{sv}}" name="connection" direction="out"/> 63 <arg type="t" name="version_id" direction="out"/> 64 </method> 65 <method name="Disconnect"/> 66 <method name="Delete"/> 67 <signal name="StateChanged"> 68 <arg type="u" name="new_state"/> 69 <arg type="u" name="old_state"/> 70 <arg type="u" name="reason"/> 71 </signal> 72 <property type="s" name="Udi" access="read"/> 73 <property type="s" name="Interface" access="read"/> 74 <property type="s" name="IpInterface" access="read"/> 75 <property type="s" name="Driver" access="read"/> 76 <property type="s" name="DriverVersion" access="read"/> 77 <property type="s" name="FirmwareVersion" access="read"/> 78 <property type="u" name="Capabilities" access="read"/> 79 <property type="u" name="Ip4Address" access="read"/> 80 <property type="u" name="State" access="read"/> 81 <property type="(uu)" name="StateReason" access="read"/> 82 <property type="o" name="ActiveConnection" access="read"/> 83 <property type="o" name="Ip4Config" access="read"/> 84 <property type="o" name="Dhcp4Config" access="read"/> 85 <property type="o" name="Ip6Config" access="read"/> 86 <property type="o" name="Dhcp6Config" access="read"/> 87 <property type="b" name="Managed" access="readwrite"/> 88 <property type="b" name="Autoconnect" access="readwrite"/> 89 <property type="b" name="FirmwareMissing" access="read"/> 90 <property type="b" name="NmPluginMissing" access="read"/> 91 <property type="u" name="DeviceType" access="read"/> 92 <property type="ao" name="AvailableConnections" access="read"/> 93 <property type="s" name="PhysicalPortId" access="read"/> 94 <property type="u" name="Mtu" access="read"/> 95 <property type="u" name="Metered" access="read"/> 96 <property type="aa{sv}" name="LldpNeighbors" access="read"/> 97 <property type="b" name="Real" access="read"/> 98 <property type="u" name="Ip4Connectivity" access="read"/> 99 <property type="u" name="Ip6Connectivity" access="read"/> 100 </interface> 101 </node>
More convenient facilities to manage D-Bus are provided by the 'qdbus' tool.
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbusviewer
- How to use command-line tool 'qdbus':
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus --help Usage: qdbus [--system] [--bus busaddress] [--literal] [servicename] [path] [method] [args] servicename the service to connect to (e.g., org.freedesktop.DBus) path the path to the object (e.g., /) method the method to call, with or without the interface args arguments to pass to the call With 0 arguments, qdbus will list the services available on the bus With just the servicename, qdbus will list the object paths available on the service With service name and object path, qdbus will list the methods, signals and properties available on the object Options: --system connect to the system bus --bus busaddress connect to a custom bus --literal print replies literally
- See all system services registered in D-Bus:
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus --system :1.0 org.freedesktop.systemd1 :1.10 org.freedesktop.Accounts :1.113 :1.116 com.redhat.NewPrinterNotification com.redhat.PrinterDriversInstaller :1.12 :1.14 org.freedesktop.ModemManager1 :1.15 org.freedesktop.ColorManager :1.154 :1.3 org.freedesktop.Avahi :1.35 :1.4 org.freedesktop.login1 :1.41 :1.47 :1.49 :1.5 org.freedesktop.PolicyKit1 :1.50 :1.52 :1.53 org.freedesktop.UPower :1.54 :1.6 org.freedesktop.NetworkManager :1.67 :1.68 org.freedesktop.UDisks2 :1.80 :1.9 org.freedesktop.DisplayManager :1.93 :1.94 org.freedesktop.DBus
- See all objects provided by a specific service (completion works!):
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus --system org.freedesktop.NetworkManager / /org /org/freedesktop /org/freedesktop/NetworkManager /org/freedesktop/NetworkManager/IP4Config /org/freedesktop/NetworkManager/IP4Config/5 /org/freedesktop/NetworkManager/IP4Config/3 /org/freedesktop/NetworkManager/IP4Config/4 /org/freedesktop/NetworkManager/ActiveConnection /org/freedesktop/NetworkManager/ActiveConnection/1 /org/freedesktop/NetworkManager/ActiveConnection/6 /org/freedesktop/NetworkManager/AgentManager /org/freedesktop/NetworkManager/Devices /org/freedesktop/NetworkManager/Devices/2 /org/freedesktop/NetworkManager/Devices/3 /org/freedesktop/NetworkManager/Devices/1 /org/freedesktop/NetworkManager/DHCP4Config /org/freedesktop/NetworkManager/DHCP4Config/6 /org/freedesktop/NetworkManager/DnsManager /org/freedesktop/NetworkManager/IP6Config /org/freedesktop/NetworkManager/IP6Config/9 /org/freedesktop/NetworkManager/IP6Config/3 /org/freedesktop/NetworkManager/IP6Config/4 /org/freedesktop/NetworkManager/Settings /org/freedesktop/NetworkManager/Settings/2 /org/freedesktop/NetworkManager/Settings/3 /org/freedesktop/NetworkManager/Settings/1
- See methods and properties provided by a specific object (commpletion works again!):
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/Devices/1 signal void org.freedesktop.DBus.Properties.PropertiesChanged(QString interface_name, QVariantMap changed_properties, QStringList invalidated_properties) method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface_name, QString property_name) method QVariantMap org.freedesktop.DBus.Properties.GetAll(QString interface_name) method void org.freedesktop.DBus.Properties.Set(QString interface_name, QString property_name, QDBusVariant value) method QString org.freedesktop.DBus.Introspectable.Introspect() method QString org.freedesktop.DBus.Peer.GetMachineId() method void org.freedesktop.DBus.Peer.Ping() property readwrite uint org.freedesktop.NetworkManager.Device.Statistics.RefreshRateMs property read qulonglong org.freedesktop.NetworkManager.Device.Statistics.RxBytes property read qulonglong org.freedesktop.NetworkManager.Device.Statistics.TxBytes signal void org.freedesktop.NetworkManager.Device.Statistics.PropertiesChanged(QVariantMap properties) property read QString org.freedesktop.NetworkManager.Device.Generic.HwAddress property read QString org.freedesktop.NetworkManager.Device.Generic.TypeDescription signal void org.freedesktop.NetworkManager.Device.Generic.PropertiesChanged(QVariantMap properties) property read QDBusObjectPath org.freedesktop.NetworkManager.Device.ActiveConnection property readwrite bool org.freedesktop.NetworkManager.Device.Autoconnect property read QList<QDBusObjectPath> org.freedesktop.NetworkManager.Device.AvailableConnections property read uint org.freedesktop.NetworkManager.Device.Capabilities property read uint org.freedesktop.NetworkManager.Device.DeviceType property read QDBusObjectPath org.freedesktop.NetworkManager.Device.Dhcp4Config property read QDBusObjectPath org.freedesktop.NetworkManager.Device.Dhcp6Config property read QString org.freedesktop.NetworkManager.Device.Driver property read QString org.freedesktop.NetworkManager.Device.DriverVersion property read bool org.freedesktop.NetworkManager.Device.FirmwareMissing property read QString org.freedesktop.NetworkManager.Device.FirmwareVersion property read QString org.freedesktop.NetworkManager.Device.Interface property read uint org.freedesktop.NetworkManager.Device.Ip4Address property read QDBusObjectPath org.freedesktop.NetworkManager.Device.Ip4Config property read uint org.freedesktop.NetworkManager.Device.Ip4Connectivity property read QDBusObjectPath org.freedesktop.NetworkManager.Device.Ip6Config property read uint org.freedesktop.NetworkManager.Device.Ip6Connectivity property read QString org.freedesktop.NetworkManager.Device.IpInterface property read {D-Bus type "aa{sv}"} org.freedesktop.NetworkManager.Device.LldpNeighbors property readwrite bool org.freedesktop.NetworkManager.Device.Managed property read uint org.freedesktop.NetworkManager.Device.Metered property read uint org.freedesktop.NetworkManager.Device.Mtu property read bool org.freedesktop.NetworkManager.Device.NmPluginMissing property read QString org.freedesktop.NetworkManager.Device.PhysicalPortId property read bool org.freedesktop.NetworkManager.Device.Real property read uint org.freedesktop.NetworkManager.Device.State property read {D-Bus type "(uu)"} org.freedesktop.NetworkManager.Device.StateReason property read QString org.freedesktop.NetworkManager.Device.Udi signal void org.freedesktop.NetworkManager.Device.StateChanged(uint new_state, uint old_state, uint reason) method void org.freedesktop.NetworkManager.Device.Delete() method void org.freedesktop.NetworkManager.Device.Disconnect() method {D-Bus type "a{sa{sv}}"} org.freedesktop.NetworkManager.Device.GetAppliedConnection(uint flags, qulonglong& version_id) method void org.freedesktop.NetworkManager.Device.Reapply({D-Bus type "a{sa{sv}}"} connection, qulonglong version_id, uint flags)
Use qdbus to request information on network interfaces (complete… ok, You got it):
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/Devices/1 org.freedesktop.NetworkManager.Device.Interface lo andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/Devices/2 org.freedesktop.NetworkManager.Device.Interface eth0 andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/Devices/3 org.freedesktop.NetworkManager.Device.Interface eth1
- How to use command-line tool 'qdbus':
busctl is probably the best commandline utility:
try busctl tree
try busctl --user introspect with lot of Tab completion tries
e. g. busctl --user introspect org.freedesktop.Notifications /org/freedesktop/Notifications org.freedesktop.Notifications:
NAME TYPE SIGNATURE RESULT/VALUE FLAGS .CloseNotification method u - - .GetCapabilities method - as - .GetServerInformation method - ssss - .Notify method susssasa{sv}i u - .ActionInvoked signal us - - .NotificationClosed signal uu - -
- Thank goodness, no XML here!
This queer susssasa{sv}i designates method input parameters:
s — string (char * with zero)
u — uint32
s — string (char * with zero)
s — string (char * with zero)
s — string (char * with zero)
as — array of strings
a{sv} — dictionary with string indexes and variable object values
i — int32
It is possible to manage D-Bus from programs in high-level programming languages. For example, a library called pydbus allows doing it from Python.
1 andrewt@comp-core-i7-3615qm-0dbf32 ~ $ python3 2 Python 3.7.4 (default, Apr 17 2020, 12:15:50) 3 [GCC 8.4.1 20200305 (ALT p9 8.4.1-alt0.p9.1)] on linux 4 Type "help", "copyright", "credits" or "license" for more information. 5 >>> from pydbus import SessionBus 6 >>> 7 >>> bus = SessionBus() 8 >>> notifications = bus.get('org.freedesktop.Notifications') 9 >>> 10 >>> help(notifications) 11 >>> 12 >>> notifications.Notify('test', 0, 'dialog-information', "Hello World!", "pydbus works :)", [], {}, 5000)
Note how help() works!
- A Python program that lists running systemd units:
Systemd is providing excellent D-Dus interface. Manage systemd services from pydbus.
- Create a simple service:
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ systemctl edit --user --force --full xtermtop.service
- With the following content:
[Unit] Description=Xterm top [Service] ExecStart=xterm -e top
- See its' status:
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ systemctl --user status xtermtop.service ● xtermtop.service Loaded: loaded (/home/andrewt/.config/systemd/user/xtermtop.service; static; vendor preset: enabled) Active: inactive (dead)
- Start and stop it manually:
- Create a simple service:
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ systemctl --user start xtermtop.service andrewt@comp-core-i7-3615qm-0dbf32 ~ $ systemctl --user stop xtermtop.service
- A Python program that starts this service with the help of systemd:
A D-Bus service in Python.
server.py
1 # Python DBUS Test Server # runs until the Quit() method is called via DBUS 2 3 from gi.repository import GLib from pydbus import SessionBus 4 5 loop = GLib.MainLoop() 6 7 class MyDBUSService(object): 8 9 """ 10 <node> 11 <interface name='net.lew21.pydbus.ClientServerExample'> 12 <method name='Hello'> 13 <arg type='s' name='response' direction='out'/> 14 </method> <method name='EchoString'> 15 <arg type='s' name='a' direction='in'/> <arg type='s' name='response' direction='out'/> 16 </method> <method name='Quit'/> 17 </interface> 18 </node> 19 """ 20 21 def Hello(self): 22 """returns the string 'Hello, World!'""" 23 return "Hello, World!" 24 25 def EchoString(self, s): 26 """returns whatever is passed to it""" 27 return s 28 29 def Quit(self): 30 """removes this object from the DBUS connection and exits""" 31 loop.quit() 32 33 bus = SessionBus() 34 bus.publish("net.lew21.pydbus.ClientServerExample", MyDBUSService()) 35 loop.run()
Docstring here is valuable: it implements introspection
Note parameter sinatures (described here)
- Run server.py with python3:
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ python3 server.py
- See the registered service with 'd-feet':
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ d-feet
- Call methods of the service from 'd-feet':
- Call methods of the running server.py service with 'qdbus':
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus net.lew21.pydbus.ClientServerExample /net/lew21/pydbus/ClientServerExample net.lew21.pydbus.ClientServerExample.Hello Hello, World! andrewt@comp-core-i7-3615qm-0dbf32 ~ $ qdbus net.lew21.pydbus.ClientServerExample /net/lew21/pydbus/ClientServerExample net.lew21.pydbus.ClientServerExample.EchoString "QQ" QQ
- Call the same methods with 'dbus-send':
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ dbus-send --print-reply --session --dest=net.lew21.pydbus.ClientServerExample /net/lew21/pydbus/ClientServerExample net.lew21.pydbus.ClientServerExample.Hello method return time=1590411234.525504 sender=:1.174 -> destination=:1.193 serial=74 reply_serial=2 string "Hello, World!" andrewt@comp-core-i7-3615qm-0dbf32 ~ $ dbus-send --print-reply --session --dest=net.lew21.pydbus.ClientServerExample /net/lew21/pydbus/ClientServerExample net.lew21.pydbus.ClientServerExample.EchoString string:QQ method return time=1590411291.496958 sender=:1.174 -> destination=:1.194 serial=75 reply_serial=2 string "QQ"
- Call the 'Quit' method to stop the service:
andrewt@comp-core-i7-3615qm-0dbf32 ~ $ dbus-send --print-reply --session --dest=net.lew21.pydbus.ClientServerExample /net/lew21/pydbus/ClientServerExample net.lew21.pydbus.ClientServerExample.Quit method return time=1590411398.034527 sender=:1.174 -> destination=:1.195 serial=76 reply_serial=2