Providing services over D-Bus

Contents:

Introduction

The Qt3 bindings do not support autogeneration of service objects yet. In order to provide interfaces over D-Bus, an application has to implement the QDBusObjectBase interface and register an instance of the resulting class with the QDBusConnection.

A simple D-Bus client example

   #include <dbus/qdbusconnection.h>;
   #include <dbus/qdbusobject.h>;

   class QStringList;

   class TestService : public QDBusObjectBase
   {
   public:
       TestService(const QDBusConnection& connection);
       virtual ~TestService();

   protected:
       virtual bool handleMethodCall(const QDBusMessage& message);

   private:
       QDBusConnection m_connection;

   private:
       QStringList sortStrings(const QStringList& list);
   };
   #include <qstringlist.h>;

   #include <dbus/qdbuserror.h>;
   #include <dbus/qdbusmessage.h>;

   TestService::TestService(const QDBusConnection& connection) : m_connection(connection)
   {
       m_connection.registerObject("/ListSorter", this);
   }

   TestService::~TestService()
   {
       m_connection.unregisterObject("/ListSorter");
   }

   // return false to let D-Bus send a standard error message that the method is unknown

   bool TestService::handleMethod(const QDBusMessage& message)
   {
       if (message.interface() != "org.example.Sort") return false;

       if (message.member() == "Strings")
       {
           // check parameters

           if (message.count() != 1 || message[0].type() != QDBusData::List)
           {
               // method signature not what we expected

               QDBusError error = QDBusError::stdInvalidArgs(
                                "Expected one argument of type array of string");

               QDBusMessage reply = QDBusMessage::methodError(message, error);

               // send error

               m_connection.send(reply);

               // tell D-Bus we did handle the call

               return true;
           }

           // call implementation

           QStringList result = sortStrings(message[0].toQStringList());

           // prepare reply

           QDBusMessage reply = QDBusMessage::methodReply(message);

           reply << QDBusData::fromList(result);

           // send reply

           m_connection.send(reply);

           // tell D-Bus we did handle the call

           return true;
       }

       return false;
   }

   QStringList TestService::sortStrings(const QStringList& list)
   {
       QStringList result = list;

       result.sort();

       return result;
   }
   int main(int argc, char** argv)
   {
       QApplication app(argc, argv, false);

       QDBusConnection connection = QDBusConnection::sessionBus();
       if (!connection.isConnected())
           qFatal("Cannot connect to session bus");

       // try to get a specific service name
       if (!connection.requestName("org.example.SortService"))
       {
           qWarning("Requesting name 'org.example.SortService' failed. "
                    "Will only be addressable through unique name '%s'",
                    connection.uniqueName().local8Bit().data());
       }
       else
       {
           qDebug("Requesting name 'org.example.SortService' successfull");
       }

       TestService service(connection);

       return app.exec();
    }

Requesting service name

When an application connects to D-Bus it gets a unique name generated by the bus daemon.

However, an application providing service will often want to be reachable under a fixed name, like a webserver being reachable through a domain name independent from its actual IP address. See section Service names for details on service names.

In order to get such a specific name an application has to request it using QDBusConnection::requestName()

The example above request "org.example.SortService" but continues with the default unique name in the case some other application is currently owning that name.

Registering objects

To make service objects available to other applications on the same bus the application has to register the objects instances with the connection to the bus using QDBusConnection::registerObject()

Registering means to specify an object path where the object will be located, i.e. how it can be unambiguously be addressed in method calls. See section Object paths for details on object paths.

If the applications has introspectable objects it is recommended to register an introspectable root object, i.e. using "/" as the path, so other applications have a common place to start asking for introspection data.

In the example above a service object providing sorting services on lists is registered on the path "/ListSorter"

Service interfaces

D-Bus methods and signals of a service object a grouped into interfaces.

See section Interface names for details on interface naming.

An object can implement any number of interfaces, for example the interface for the functionality it wants to provide and a D-Bus standard interface like "org.freedesktop.DBus.Introspectable" for providing an XML description of all its interfaces.

The service object of the example above implements just one interface "org.example.Sort" and its handleMethodCall() explicitly checks all received messages and rejects any messsage not sent to this particular interface by returning false and thus telling the D-Bus layer to generate a standard error response.

Multiple interfaces can of course be directly implemented in one C++ class, however it might sometimes be wise to delegate calls for different interfaces to different implementations:

   class Interface1 : public QDBusObjectBase
   {
   public:
       Interface1(const QDBusConnection&);

   protected:
       virtual bool handleMethodCall(const QDBusMessage&);
   };

   class Interface2 : public QDBusObjectBase
   {
   public:
       Interface2(const QDBusConnection&);

   protected:
       virtual bool handleMethodCall(const QDBusMessage&);
   };

   class MultiInterfaceService : public QDBusObjectBase
   {
   public:
       MultiInterfaceService(const QDBusConnection&);

   protected:
       virtual bool handleMethodCall(const QDBusMessage&);

   private:
       QMap<QString, QDBusObjectBase*> m_interfaces;
   };

   MultiInterfaceService::MultiInterfaceService(const QDBusConnection& connection)
   {
       m_interfaces.insert("org.example.Interface1", new Interface1(connection));
       m_interfaces.insert("org.example.Interface2", new Interface2(connection));
   }

   bool MultiInterfaceService::handleMethodCall(const QDBusMessage& message)
   {
       // delegate call to its interface handler
       QDBusObjectBase* handler = m_interfaces[message.interface()];
       if (handler != 0)
           return delegateMethodCall->(message, handler);
       else
           return false; // no such interface
   }

Generated by  doxygen 1.6.2