Most of the current Linux installations rely on the inter process communication framework D-Bus. D-Bbus can be used to gather quite some information about the system – however the usage can be a bit troublesome. This howto sheds some light on the usage of D-Bus by the example of querying the NetworkManagaer interface.
Background
D-BUS enables tools and programs to talk to each other. For example tools like NetworkManager, systemd or firewalld all provide methods and information via D-Bus to query their information and change their configuration or trigger some specific behavior. And of course all these operations can also be performed on the command line. This can be handy in case you want to include it in some bash scripts or for example in your monitoring setup. It also helps understanding the basic principles behind D-Bus in case you want to use it in more complex scripts and programs.
First steps: qdbus
For this example I use qdbus which is shipped with Qt. There are corresponding tools like gdbus and others available in case you don’t want to install qt on your machine for whatever reason.
When you first launch qdbus it shows you a list of strange names which roughly remind you of the apps currently running on your desktop/user session. The point is that you are asking your own user environment – but in case of NetworkManager or other system tools you need to query the system D-Bus:
$ qdbus --system ... org.freedesktop.NetworkManager ...
This outputs show a list of all available services, or better said, interfaces. You can connect to these and can get a list of the objects the have:
$ qdbus --system org.freedesktop.NetworkManager ... /org /org/freedesktop /org/freedesktop/NetworkManager /org/freedesktop/NetworkManager/AccessPoint /org/freedesktop/NetworkManager/AccessPoint/0 ...
Each object has a path which identifies, well, the path to the object. That’s how you call it and everything which is connected to it.
Querying objects
Now that we have a list of objects, we can check which members belong to an object. Members can be actions which can be triggered, or information about a current state, signals, etc. – when we have access to the members things get interesting. In this case we query the object NetworkManager itself, not one of its sub-objects:
$ qdbus --system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager ... method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface, QString propname) method QVariantMap org.freedesktop.DBus.Properties.GetAll(QString interface) ... property read QList<QDBusObjectPath> org.freedesktop.NetworkManager.ActiveConnections ...
The output shows a list of various members. In the above given code snippet I highlighted the methods to get information – and a property which is called org.freedesktop.NetworkManager.ActiveConnections
. Guess what, that property holds the information of the current active connections (there can be more than one!) of the NetworkManager. And we can ask this information (using the --literal
because otherwise the output is not possible):
$ qdbus --system --literal org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.DBus.Properties.Get org.freedesktop.NetworkManager ActiveConnections [Variant: [Argument: ao {[ObjectPath: /org/freedesktop/NetworkManager/ActiveConnection/0]}]]
Please note that as arguments we gave not the entire property as a whole, but we separated at the last dot. Formally we asked for the content of the property ActiveConnections
at the interface org.freedesktop.NetworkManager
. The interface and the property are merged in the output, but the query always needs to have them separated by a space. I’m not sure why…
But well, now we know that our active connection is actually a NetworkManager object with the path given above. We can again query that object to get a list of all members:
$ qdbus --system --literal org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/ActiveConnection/0 ... method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface, QString propname) ... property read QDBusObjectPath org.freedesktop.NetworkManager.Connection.Active.Ip4Config ...
There is again a member to get properties – and the interesting property again is an object path:
$ qdbus --system --literal org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/ActiveConnection/0 org.freedesktop.DBus.Properties.Get org.freedesktop.NetworkManager.Connection.Active Ip4Config [Variant: [ObjectPath: /org/freedesktop/NetworkManager/IP4Config/1]]
We query again that given object path and see rather promising members:
$ qdbus --system --literal org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/IP4Config/1 property read QDBusRawType::aau org.freedesktop.NetworkManager.IP4Config.Addresses property read QStringList org.freedesktop.NetworkManager.IP4Config.Domains property read QString org.freedesktop.NetworkManager.IP4Config.Gateway ...
And indeed: if we now query these members, we get for example the current Gateway:
$ qdbus --system --literal org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/IP4Config/1 org.freedesktop.DBus.Properties.Get org.freedesktop.NetworkManager.IP4Config Gateway [Variant(QString): "192.168.178.1"]
That’s it. Now you know the gateway I have configured right now. If you do not want to query each member individually, you can simply call all given members of an interface:
$ qdbus --system --literal org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/IP4Config/1 org.freedesktop.DBus.Properties.GetAll org.freedesktop.NetworkManager.IP4Config|sed 's/, /\n/g' [Argument: a{sv} {"Gateway" = [Variant(QString): "192.168.178.1"] "Addresses" = [Variant: [Argument: aau {[Argument: au {565356736 24 28485824}]}]] "Routes" = [Variant: [Argument: aau {}]] "Nameservers" = [Variant: [Argument: au {28485824}]] "Domains" = [Variant(QStringList): {"example.com"}] "Searches" = [Variant(QStringList): {}] "WinsServers" = [Variant: [Argument: au {}]]}]
As you see the ipv4 addresses are encoded in reverse decimal notation. I am sure there is reason for that. A good one. Surely. But well, that’s just a stupid encoding problem, nothing else. In the end, the queries worked: the current gateway was successfully identified via D-Bus.
Methods: calling panic mode in firewalld
As mentioned above there are also methods which influence the behavior of an application. One simple example I came across is to kill all networking by calling the firewalld panic mode. For that you need the interface org.fedoraproject.FirewallD1
, the object /org/fedoraproject/FirewallD1
and the method org.fedoraproject.FirewallD1.enablePanicMode
:
$ qdbus --system --literal org.fedoraproject.FirewallD1 /org/fedoraproject/FirewallD1 org.fedoraproject.FirewallD1.enablePanicMode []
And your internet connection is gone. It comes back by disabling the panic mode again:
$ qdbus --system --literal org.fedoraproject.FirewallD1 /org/fedoraproject/FirewallD1 org.fedoraproject.FirewallD1.disablePanicMode []
Rights
You should also be aware that there is a rights management embedded in D-Bus – not every user is allowed to do anything. For example, as a normal user you cannot simply query all configured chains. If you call the following method:
$ qdbus --system --literal org.fedoraproject.FirewallD1 /org/fedoraproject/FirewallD1 org.fedoraproject.FirewallD1.direct.getAllChains [Argument: a(sss) {}]
you are greeted with a password dialog before the command is executed.
Summary
D-Bus is used for inter process communication and thus can help when various programs are supposed to work together. It can also used on the shell to query information or to call specific methods as long as they are provided via the D-Bus interface. That might come in handy – some applications have rather strange ways to provide data or procedures via their user interfaces, and D-Bus offers a very generic way to interact without the need to respect any user interfaces.
Nice examples!
Btw, correct capitalization for D-Bus is D-Bus ๐
Ah, good to know ๐