Details
Description
Let's consider the following python code:
provider.py:
#!/usr/bin/env python3 # SPDX-License-Identifier: WTFPL import pydbus import pydbus_manager import sys from gi.repository import GLib from functools import partial class Provider: """ <node> <interface name='org.foobar.Provider1'> <property name='FooBar' type='s' access='read'/> </interface> </node> """ @property def FooBar(self): return "foobar foobar" def add_interface(manager): print("Adding object") manager.register_object("/org/foobar/Provider1/FooBar", Provider(), None) return GLib.SOURCE_REMOVE if __name__ == "__main__": loop = GLib.MainLoop() bus = ( pydbus.SystemBus() if len(sys.argv) == 2 and sys.argv[1] == "system" else pydbus.SessionBus() ) manager = pydbus_manager.Manager(bus, "org.foobar.Provider1") print("Registered the object manager") GLib.timeout_add_seconds(5, partial(add_interface, manager)) print("Scheduled an interface event in 5 seconds") loop.run()
receiver.py:
#!/usr/bin/env python3 # SPDX-License-Identifier: WTFPL import pydbus import sys from gi.repository import GLib def on_interfaces_added(*args): print("InterfacesAdded: args: {}".format(args)) if __name__ == "__main__": loop = GLib.MainLoop() bus = ( pydbus.SystemBus() if len(sys.argv) == 2 and sys.argv[1] == "system" else pydbus.SessionBus() ) intf = bus.get("org.foobar.Provider1", "/org/foobar/Provider1") intf.InterfacesAdded.connect(on_interfaces_added) print("Connected to InterfacesAdded signal") loop.run()
The /org/foobar/Provider1 object implements the org.freedesktop.DBus.ObjectManager interface and is visible in qdbus like this:
$ qdbus org.foobar.Provider1 /org/foobar/Provider1 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() signal void org.freedesktop.DBus.ObjectManager.InterfacesAdded(QDBusObjectPath object_path, {D-Bus type "a{sa{sv}}"} interfaces_and_properties) signal void org.freedesktop.DBus.ObjectManager.InterfacesRemoved(QDBusObjectPath object_path, QStringList interfaces) method {D-Bus type "a{oa{sa{sv}}}"} org.freedesktop.DBus.ObjectManager.GetManagedObjects()
The above python examples work just fine - if the receiver is run within 5 seconds of starting the provider, it receives the signal:
$ ./python-receiver/receiver.py Connected to InterfacesAdded signal InterfacesAdded: args: ('/org/foobar/Provider1/FooBar', {'org.freedesktop.DBus.Properties': {}, 'org.freedesktop.DBus.Introspectable': {}, 'org.freedesktop.DBus.Peer': {}, 'org.foobar.Provider1': {'FooBar': 'foobar foobar'}})
Let's now implement a receiver in Qt6:
receiver.hpp:
/* SPDX-License-Identifier: WTFPL */ #include <QObject> #include <QtDBus/QDBusObjectPath> #include <QMap> #include <QVariant> #include <QString> typedef QMap<QString, QDBusVariant> QDBusVariantMap; Q_DECLARE_METATYPE(QDBusVariantMap); typedef QMap<QString, QDBusVariantMap> QDBusVariantMapMap; Q_DECLARE_METATYPE(QDBusVariantMapMap); class Receiver : public QObject { Q_OBJECT public: Receiver(QObject *parent = nullptr); virtual ~Receiver(); private slots: void InterfacesAdded(QDBusObjectPath object_path, QDBusVariantMapMap interfaces_and_properties); };
main.cpp:
// SPDX-License-Identifier: WTFPL #include <QtGlobal> #include <QDebug> #include <QtWidgets/QApplication> #include <QtDBus/QDBusInterface> #include <QtDBus/QDBusMetaType> #include "receiver.hpp" Receiver::Receiver(QObject *parent) : QObject(parent) { } Receiver::~Receiver() { } void Receiver::InterfacesAdded(QDBusObjectPath object_path, QDBusVariantMapMap interfaces_and_properties) { Q_UNUSED(interfaces_and_properties); qInfo() << "InterfacesAdded: " << object_path.path(); } int main(int argc, char **argv) { QApplication app(argc, argv); auto bus = (argc == 2 && QString(argv[1]) == "system") ? QDBusConnection::systemBus() : QDBusConnection::sessionBus(); Receiver receiver; qDBusRegisterMetaType<QDBusVariantMap>(); qDBusRegisterMetaType<QDBusVariantMapMap>(); qInfo() << "Before connection"; qInfo() << "Is connected " << bus.isConnected(); qInfo() << "Trying to connect"; auto ret = bus.connect( "org.foobar.Provider1", "/org/foobar/Provider1", "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &receiver, SLOT(InterfacesAdded(QDBusObjectPath, QDBusVariantMapMap)) ); qInfo() << "connect() result: " << ret; if (!ret) qInfo() << "lastError: " << bus.lastError(); return app.exec(); }
Runs fine as well:
$ ./receiver Before connection Is connected true Trying to connect connect() result: true InterfacesAdded: "/org/foobar/Provider1/FooBar"
The above examples are using the session bus. Now let's use the following config under /etc/dbus-1/system.d/org.foobar.Provider1.conf:
<!-- SPDX-License-Identifier: WTFPL --> <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <policy user="root"> <allow own="org.foobar.Provider1"/> <allow send_destination="org.foobar.Provider1"/> <allow receive_sender="org.foobar.Provider1"/> </policy> </busconfig>
And run the examples with the "system" argument. Python still works just fine. But running the same C++ code results in the following:
$ sudo ./receiver system QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root' Before connection Is connected true Trying to connect connect() result: false lastError: QDBusError("", "")
As you can see, the connection fails, bus.connect() returns false, the signal is never received but bus.lastError() doesn't indicate any error code.
Ok, let's try to debug this with QDBUS_DEBUG=1:
$ sudo QDBUS_DEBUG=1 ./qt6-receiver/receiver system QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root' QDBusConnectionPrivate(0x7fab3c004f70) : connected successfully QDBusConnectionPrivate(0x7fab3c004f70) got message (signal): QDBusMessage(type=Signal, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="NameAcquired", signature="s", contents=(":1.165") ) QDBusConnectionPrivate(0x7fab3c004f70) delivery is suspended Before connection Is connected true Trying to connect QDBusConnectionPrivate(0x7fab3c004f70) Adding rule: "type='signal',sender='org.foobar.Provider1',path='/org/foobar/Provider1',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'" QDBusConnectionPrivate(0x7fab3c004f70) sending message: QDBusMessage(type=MethodCall, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="GetNameOwner", signature="", contents=("org.foobar.Provider1") ) QDBusConnectionPrivate(0x7fab3c004f70) got message reply: QDBusMessage(type=MethodReturn, service="org.freedesktop.DBus", signature="s", contents=(":1.164") ) QDBusConnectionPrivate(0x7fab3c004f70) Watching service "org.foobar.Provider1" for owner changes (current owner: ":1.164" ) connect() result: true QDBusConnectionPrivate(0x7fab3c00fa40) : connected successfully QDBusConnectionPrivate(0x7fab3c00fa40) got message (signal): QDBusMessage(type=Signal, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="NameAcquired", signature="s", contents=(":1.4") ) QDBusConnectionPrivate(0x7fab3c00fa40) delivery is suspended QDBusConnectionPrivate(0x7fab3c00fa40) Adding rule: "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.a11y.Bus',arg1=''" QDBusConnectionPrivate(0x7fab3c00fa40) sending message: QDBusMessage(type=MethodCall, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="NameHasOwner", signature="", contents=("org.a11y.Bus") ) QDBusConnectionPrivate(0x7fab3c00fa40) got message reply: QDBusMessage(type=MethodReturn, service="org.freedesktop.DBus", signature="b", contents=(false) ) QDBusConnectionPrivate(0x7fab3c015220) : connected successfully QDBusConnectionPrivate(0x7fab3c015220) got message (signal): QDBusMessage(type=Signal, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="NameAcquired", signature="s", contents=(":1.34") ) QDBusConnectionPrivate(0x7fab3c004f70) dequeueing message QDBusMessage(type=Signal, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="NameAcquired", signature="s", contents=(":1.165") ) QDBusConnectionPrivate(0x7fab3c00fa40) dequeueing message QDBusMessage(type=Signal, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="NameAcquired", signature="s", contents=(":1.4") ) QDBusConnectionPrivate(0x7fab3c004f70) got message (signal): QDBusMessage(type=Signal, service=":1.164", path="/org/foobar/Provider1", interface="org.freedesktop.DBus.ObjectManager", member="InterfacesAdded", signature="oa{sa{sv}}", contents=([ObjectPath: /org/foobar/Provider1/FooBar], [Argument: a{sa{sv}} {"org.freedesktop.DBus.Properties" = [Argument: a{sv} {}], "org.freedesktop.DBus.Introspectable" = [Argument: a{sv} {}], "org.freedesktop.DBus.Peer" = [Argument: a{sv} {}], "org.foobar.Provider1" = [Argument: a{sv} {"FooBar" = [Variant(QString): "foobar foobar"]}]}]) ) InterfacesAdded: "/org/foobar/Provider1/FooBar" QDBusConnectionPrivate(0x7fab3c004f70) got message (signal): QDBusMessage(type=Signal, service="org.freedesktop.DBus", path="/org/freedesktop/DBus", interface="org.freedesktop.DBus", member="NameOwnerChanged", signature="sss", contents=("org.foobar.Provider1", ":1.164", "") ) QDBusConnectionPrivate(0x7fab3c004f70) Updating name "org.foobar.Provider1" from ":1.164" to ""
Ha! Now it works.
The examples I posted are available here. I suspect it's some timing issue that's hidden when enabling debug?