Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-108822

QDBus: unable to connect the InterfacesAdded signal of org.freedesktop.DBus.ObjectManager on the system bus (unless using QDBUS_DEBUG)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P2: Important
    • 6.2.8, 6.4.3, 6.5.0, 6.6.0
    • 6.4.1
    • D-Bus
    • None
    • I first encountered that issue on a yocto-based, custom linux distro but it's reproducible on Ubuntu 22.10 both with X11 and Wayland.
    • Linux/Wayland, Linux/X11, Linux/Yocto
    • d619a952c (dev), 157c2d46a (6.5), d82f68f12 (tqtc/lts-6.2), 2230c1344 (6.4)

    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?

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            thiago Thiago Macieira
            brgl Bartosz Golaszewski
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes