Gio – 2.0: Migrating to GDBus
Description: Reference for Gio-2.0: Migrating to GDBus

#### Migrating to GDBus

## Conceptual differences

The central concepts of D-Bus are modelled in a very similar way in dbus-glib and GDBus. Both have objects representing connections, proxies and method invocations. But there are some important differences:

- dbus-glib uses the libdbus reference implementation, GDBus doesn’t. Instead, it relies on GIO streams as transport layer, and has its own implementation for the D-Bus connection setup and authentication. Apart from using streams as transport, avoiding libdbus also lets GDBus avoid some thorny multithreading issues.
- dbus-glib uses the GObject type system for method arguments and return values, including a homegrown container specialization mechanism. GDBus relies on the GVariant type system which is explicitly designed to match D-Bus types.
- dbus-glib models only D-Bus interfaces and does not provide any types for objects. GDBus models both D-Bus interfaces (via the GDBusInterface, GDBusProxy and GDBusInterfaceSkeleton types) and objects (via the GDBusObject, GDBusObjectSkeleton and GDBusObjectProxy types).
- GDBus includes native support for the org.freedesktop.DBus.Properties (via the GDBusProxy type) and org.freedesktop.DBus.ObjectManager D-Bus interfaces, dbus-glib doesn’t.
- The typical way to export an object in dbus-glib involves generating glue code from XML introspection data using dbus-binding-tool. GDBus provides a similar tool called gdbus-codegen that can also generate Docbook D-Bus interface documentation.
- dbus-glib doesn’t provide any convenience API for owning and watching bus names, GDBus provides the `g_bus_own_name()` and `g_bus_watch_name()` family of convenience functions.
- GDBus provides API to parse, generate and work with Introspection XML, dbus-glib doesn’t.
- GTestDBus provides API to create isolated unit tests

## API comparison

dbus-glib | GDBus
--- | ---
`DBusGConnection` | `GDBusConnection`
`DBusGProxy` | `GDBusProxy`, `GDBusInterface` - also see `GDBusObjectProxy`
`DBusGObject` | `GDBusInterfaceSkeleton`, `GDBusInterface` - also see `GDBusObjectSkeleton`
`DBusGMethodInvocation` | `GDBusMethodInvocation`
`dbus_g_bus_get()` | `g_bus_get_sync()`, also see `g_bus_get()`
`dbus_g_proxy_new_for_name()` | `g_dbus_proxy_new_sync()` and `g_dbus_proxy_new_for_bus_sync()`, also see `g_dbus_proxy_new()`
`dbus_g_proxy_add_signal()` | not needed, use the generic “g-signal”
`dbus_g_proxy_connect_signal()` | use `g_signal_connect()` with “g-signal”
`dbus_g_connection_register_g_object()` | `g_dbus_connection_register_object()` - also see `g_dbus_object_manager_server_export()`
`dbus_g_connection_unregister_g_object()` | `g_dbus_connection_unregister_object()` - also see `g_dbus_object_manager_server_unexport()`
`dbus_g_object_type_install_info()` | introspection data is installed while registering an object, see `g_dbus_connection_register_object()`
`dbus_g_proxy_begin_call()` | `g_dbus_proxy_call()`
`dbus_g_proxy_end_call()` | `g_dbus_proxy_call_finish()`
`dbus_g_proxy_call()` | `g_dbus_proxy_call_sync()`
`dbus_g_error_domain_register()` | `g_dbus_error_register_error_domain()`
`dbus_g_error_has_name()` | no direct equivalent, see `g_dbus_error_get_remote_error()`
`dbus_g_method_return()` | `g_dbus_method_invocation_return_value()`
`dbus_g_method_return_error()` | `g_dbus_method_invocation_return_error()` and variants
`dbus_g_method_get_sender()` | `g_dbus_method_invocation_get_sender()`

## Owning bus names

Using dbus-glib, you typically call RequestName manually to own a name, like in the following excerpt:

    error = NULL;
    res = dbus_g_proxy_call (system_bus_proxy,
                             "RequestName",
                             &error,
                             G_TYPE_STRING, NAME_TO_CLAIM,
                             G_TYPE_UINT,   DBUS_NAME_FLAG_ALLOW_REPLACEMENT,
                             G_TYPE_INVALID,
                             G_TYPE_UINT,   &result,
                             G_TYPE_INVALID);
    if (!res)
      {
        if (error != NULL)
          {
            g_warning ("Failed to acquire %s: %s",
                       NAME_TO_CLAIM, error->message);
            g_error_free (error);
          }
        else
          {
            g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
          }
        goto out;
      }

    if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
      {
        if (error != NULL)
          {
            g_warning ("Failed to acquire %s: %s",
                       NAME_TO_CLAIM, error->message);
            g_error_free (error);
          }
        else
          {
            g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
          }
        exit (1);
      }

    dbus_g_proxy_add_signal (system_bus_proxy, "NameLost",
                             G_TYPE_STRING, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal (system_bus_proxy, "NameLost",
                                 G_CALLBACK (on_name_lost), NULL, NULL);

    /* further setup ... */

While you can do things this way with GDBus too, using `g_dbus_proxy_call_sync()`, it is much nicer to use the high-level API for this:

    static void
    on_name_acquired (GDBusConnection *connection,
                      const gchar     *name,
                      gpointer         user_data)
    {
      /* further setup ... */
    }

    /* ... */

      owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
                                 NAME_TO_CLAIM,
                                 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
                                 on_bus_acquired,
                                 on_name_acquired,
                                 on_name_lost,
                                 NULL,
                                 NULL);

      g_main_loop_run (loop);

      g_bus_unown_name (owner_id);

Note that `g_bus_own_name()` works asynchronously and requires you to enter your mainloop to await the `on_name_acquired()` callback. Also note that in order to avoid race conditions (e.g. when your service is activated by a method call), you have to export your manager object before acquiring the name. The `on_bus_acquired()` callback is the right place to do such preparations.

## Creating proxies for well-known names

dbus-glib lets you create proxy objects for well-known names, like the following example:

    proxy = dbus_g_proxy_new_for_name (system_bus_connection,
                                       "org.freedesktop.Accounts",
                                       "/org/freedesktop/Accounts",
                                       "org.freedesktop.Accounts");

For a DBusGProxy constructed like this, method calls will be sent to the current owner of the name, and that owner can change over time.

The same can be achieved with GDBusProxy:

    error = NULL;
    proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
                                           G_DBUS_PROXY_FLAGS_NONE,
                                           NULL, /* GDBusInterfaceInfo */
                                           "org.freedesktop.Accounts",
                                           "/org/freedesktop/Accounts",
                                           "org.freedesktop.Accounts",
                                           NULL, /* GCancellable */
                                           &error);

For an added layer of safety, you can specify what D-Bus interface the proxy is expected to conform to by using the GDBusInterfaceInfo type. Additionally, GDBusProxy loads, caches and tracks changes to the D-Bus properties on the remote object. It also sets up match rules so D-Bus signals from the remote object are delivered locally.

The GDBusProxy type normally isn’t used directly - instead proxies subclassing GDBusProxy generated by `gdbus-codegen` is used, see the section called “Using gdbus-codegen”.

## Generating code and docs

### Using gdbus-codegen

dbus-glib comes with dbus-binding-tool, which can produce somewhat nice client- and server-side wrappers for a D-Bus interface. With GDBus, gdbus-codegen is used and like its counterpart, it also takes D-Bus Introspection XML as input:

#### Example D-Bus Introspection XML

    <node>
      <!-- org.gtk.GDBus.Example.ObjectManager.Animal:
           @short_description: Example docs generated by gdbus-codegen
           @since: 2.30

           This D-Bus interface is used to describe a simple animal.
        -->
      <interface name="org.gtk.GDBus.Example.ObjectManager.Animal">
        <!-- Mood: The mood of the animal.
             @since: 2.30

             Known values for this property include
             <literal>Happy</literal> and <literal>Sad</literal>. Use the
             org.gtk.GDBus.Example.ObjectManager.Animal.Poke() method to
             change this property.

             This property influences how often the animal jumps up and
             down, see the
             #org.gtk.GDBus.Example.ObjectManager.Animal::Jumped signal
             for more details.
        -->
        <property name="Mood" type="s" access="read"/>

        <!--
            Poke:
            @make_sad: Whether to make the animal sad.
            @make_happy: Whether to make the animal happy.
            @since: 2.30

            Method used to changing the mood of the animal. See also the
            #org.gtk.GDBus.Example.ObjectManager.Animal:Mood property.
          -->
        <method name="Poke">
          <arg direction="in" type="b" name="make_sad"/>
          <arg direction="in" type="b" name="make_happy"/>
        </method>

        <!--
            Jumped:
            @height: Height, in meters, that the animal jumped.
            @since: 2.30

            Emitted when the animal decides to jump.
          -->
        <signal name="Jumped">
          <arg type="d" name="height"/>
        </signal>

        <!--
            Foo:
            Property with no <quote>since</quote> annotation (should inherit the 2.30 from its containing interface).
          -->
        <property name="Foo" type="s" access="read"/>

        <!--
            Bar:
            @since: 2.36
            Property with a later <quote>since</quote> annotation.
          -->
        <property name="Bar" type="s" access="read"/>
      </interface>

      <!-- org.gtk.GDBus.Example.ObjectManager.Cat:
           @short_description: More example docs generated by gdbus-codegen

           This D-Bus interface is used to describe a cat. Right now there
           are no properties, methods or signals associated with this
           interface so it is essentially a <ulink
           url="http://en.wikipedia.org/wiki/Marker_interface_pattern">Marker
           Interface</ulink>.

           Note that D-Bus objects implementing this interface also
           implement the #org.gtk.GDBus.Example.ObjectManager.Animal
           interface.
        -->
      <interface name="org.gtk.GDBus.Example.ObjectManager.Cat">
      </interface>
    </node>

If this XML is processed like this

    gdbus-codegen --interface-prefix org.gtk.GDBus.Example.ObjectManager. \
                  --generate-c-code generated-code                        \
                  --c-namespace Example                       \
                  --c-generate-object-manager                 \
                  --generate-docbook generated-docs                       \
                  gdbus-example-objectmanager.xml

then two files generated-code.h and generated-code.c are generated. Additionally, two XML files generated-docs-org.gtk.GDBus.Example.ObjectManager.Animal and generated-docs-org.gtk.GDBus.Example.ObjectManager.Cat with Docbook XML are generated.

While the contents of `generated-code.h` and `generated-code.c` are best described by the `gdbus-codegen` manual page, here’s a brief example of how this generated code can be used:

    #include "gdbus-object-manager-example/objectmanager-gen.h"

    /* ---------------------------------------------------------------------------------------------------- */

    static GDBusObjectManagerServer *manager = NULL;

    static gboolean
    on_animal_poke (ExampleAnimal          *animal,
                    GDBusMethodInvocation  *invocation,
                    gboolean                make_sad,
                    gboolean                make_happy,
                    gpointer                user_data)
    {
      if ((make_sad && make_happy) || (!make_sad && !make_happy))
        {
          g_dbus_method_invocation_return_dbus_error (invocation,
                                                      "org.gtk.GDBus.Examples.ObjectManager.Error.Failed",
                                                      "Exactly one of make_sad or make_happy must be TRUE");
          goto out;
        }

      if (make_sad)
        {
          if (g_strcmp0 (example_animal_get_mood (animal), "Sad") == 0)
            {
              g_dbus_method_invocation_return_dbus_error (invocation,
                                                          "org.gtk.GDBus.Examples.ObjectManager.Error.SadAnimalIsSad",
                                                          "Sad animal is already sad");
              goto out;
            }

          example_animal_set_mood (animal, "Sad");
          example_animal_complete_poke (animal, invocation);
          goto out;
        }

      if (make_happy)
        {
          if (g_strcmp0 (example_animal_get_mood (animal), "Happy") == 0)
            {
              g_dbus_method_invocation_return_dbus_error (invocation,
                                                          "org.gtk.GDBus.Examples.ObjectManager.Error.HappyAnimalIsHappy",
                                                          "Happy animal is already happy");
              goto out;
            }

          example_animal_set_mood (animal, "Happy");
          example_animal_complete_poke (animal, invocation);
          goto out;
        }

      g_assert_not_reached ();

     out:
      return G_DBUS_METHOD_INVOCATION_HANDLED;
    }

    static void
    on_bus_acquired (GDBusConnection *connection,
                     const gchar     *name,
                     gpointer         user_data)
    {
      ExampleObjectSkeleton *object;
      guint n;

      g_print ("Acquired a message bus connection\n");

      /* Create a new org.freedesktop.DBus.ObjectManager rooted at /example/Animals */
      manager = g_dbus_object_manager_server_new ("/example/Animals");

      for (n = 0; n < 10; n++)
        {
          gchar *s;
          ExampleAnimal *animal;

          /* Create a new D-Bus object at the path /example/Animals/N where N is 000..009 */
          s = g_strdup_printf ("/example/Animals/%03d", n);
          object = example_object_skeleton_new (s);
          g_free (s);

          /* Make the newly created object export the interface
           * org.gtk.GDBus.Example.ObjectManager.Animal (note
           * that @object takes its own reference to @animal).
           */
          animal = example_animal_skeleton_new ();
          example_animal_set_mood (animal, "Happy");
          example_object_skeleton_set_animal (object, animal);
          g_object_unref (animal);

          /* Cats are odd animals - so some of our objects implement the
           * org.gtk.GDBus.Example.ObjectManager.Cat interface in addition
           * to the .Animal interface
           */
          if (n % 2 == 1)
            {
              ExampleCat *cat;
              cat = example_cat_skeleton_new ();
              example_object_skeleton_set_cat (object, cat);
              g_object_unref (cat);
            }

          /* Handle Poke() D-Bus method invocations on the .Animal interface */
          g_signal_connect (animal,
                            "handle-poke",
                            G_CALLBACK (on_animal_poke),
                            NULL); /* user_data */

          /* Export the object (@manager takes its own reference to @object) */
          g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (object));
          g_object_unref (object);
        }

      /* Export all objects */
      g_dbus_object_manager_server_set_connection (manager, connection);
    }

    static void
    on_name_acquired (GDBusConnection *connection,
                      const gchar     *name,
                      gpointer         user_data)
    {
      g_print ("Acquired the name %s\n", name);
    }

    static void
    on_name_lost (GDBusConnection *connection,
                  const gchar     *name,
                  gpointer         user_data)
    {
      g_print ("Lost the name %s\n", name);
    }

    gint
    main (gint argc, gchar *argv[])
    {
      GMainLoop *loop;
      guint id;

      loop = g_main_loop_new (NULL, FALSE);

      id = g_bus_own_name (G_BUS_TYPE_SESSION,
                           "org.gtk.GDBus.Examples.ObjectManager",
                           G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
                           G_BUS_NAME_OWNER_FLAGS_REPLACE,
                           on_bus_acquired,
                           on_name_acquired,
                           on_name_lost,
                           loop,
                           NULL);

      g_main_loop_run (loop);

      g_bus_unown_name (id);
      g_main_loop_unref (loop);

      return 0;
    }

This, on the other hand, is a client-side application using generated code:

    #include "gdbus-object-manager-example/objectmanager-gen.h"

    /* ---------------------------------------------------------------------------------------------------- */

    static void
    print_objects (GDBusObjectManager *manager)
    {
      GList *objects;
      GList *l;

      g_print ("Object manager at %s\n", g_dbus_object_manager_get_object_path (manager));
      objects = g_dbus_object_manager_get_objects (manager);
      for (l = objects; l != NULL; l = l->next)
        {
          ExampleObject *object = EXAMPLE_OBJECT (l->data);
          GList *interfaces;
          GList *ll;
          g_print (" - Object at %s\n", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));

          interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object));
          for (ll = interfaces; ll != NULL; ll = ll->next)
            {
              GDBusInterface *interface = G_DBUS_INTERFACE (ll->data);
              g_print ("   - Interface %s\n", g_dbus_interface_get_info (interface)->name);

              /* Note that @interface is really a GDBusProxy instance - and additionally also
               * an ExampleAnimal or ExampleCat instance - either of these can be used to
               * invoke methods on the remote object. For example, the generated function
               *
               *  void example_animal_call_poke_sync (ExampleAnimal  *proxy,
               *                                      gboolean        make_sad,
               *                                      gboolean        make_happy,
               *                                      GCancellable   *cancellable,
               *                                      GError        **error);
               *
               * can be used to call the Poke() D-Bus method on the .Animal interface.
               * Additionally, the generated function
               *
               *  const gchar *example_animal_get_mood (ExampleAnimal *object);
               *
               * can be used to get the value of the :Mood property.
               */
            }
          g_list_free_full (interfaces, g_object_unref);
        }
      g_list_free_full (objects, g_object_unref);
    }

    static void
    on_object_added (GDBusObjectManager *manager,
                     GDBusObject        *object,
                     gpointer            user_data)
    {
      gchar *owner;
      owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
      g_print ("Added object at %s (owner %s)\n", g_dbus_object_get_object_path (object), owner);
      g_free (owner);
    }

    static void
    on_object_removed (GDBusObjectManager *manager,
                       GDBusObject        *object,
                       gpointer            user_data)
    {
      gchar *owner;
      owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
      g_print ("Removed object at %s (owner %s)\n", g_dbus_object_get_object_path (object), owner);
      g_free (owner);
    }

    static void
    on_notify_name_owner (GObject    *object,
                          GParamSpec *pspec,
                          gpointer    user_data)
    {
      GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object);
      gchar *name_owner;

      name_owner = g_dbus_object_manager_client_get_name_owner (manager);
      g_print ("name-owner: %s\n", name_owner);
      g_free (name_owner);
    }

    static void
    on_interface_proxy_properties_changed (GDBusObjectManagerClient *manager,
                                           GDBusObjectProxy         *object_proxy,
                                           GDBusProxy               *interface_proxy,
                                           GVariant                 *changed_properties,
                                           const gchar *const       *invalidated_properties,
                                           gpointer                  user_data)
    {
      GVariantIter iter;
      const gchar *key;
      GVariant *value;
      gchar *s;

      g_print ("Properties Changed on %s:\n", g_dbus_object_get_object_path (G_DBUS_OBJECT (object_proxy)));
      g_variant_iter_init (&iter, changed_properties);
      while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
        {
          s = g_variant_print (value, TRUE);
          g_print ("  %s -> %s\n", key, s);
          g_variant_unref (value);
          g_free (s);
        }
    }

    gint
    main (gint argc, gchar *argv[])
    {
      GDBusObjectManager *manager;
      GMainLoop *loop;
      GError *error;
      gchar *name_owner;

      manager = NULL;
      loop = NULL;

      loop = g_main_loop_new (NULL, FALSE);

      error = NULL;
      manager = example_object_manager_client_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                                                G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
                                                                "org.gtk.GDBus.Examples.ObjectManager",
                                                                "/example/Animals",
                                                                NULL, /* GCancellable */
                                                                &error);
      if (manager == NULL)
        {
          g_printerr ("Error getting object manager client: %s", error->message);
          g_error_free (error);
          goto out;
        }

      name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
      g_print ("name-owner: %s\n", name_owner);
      g_free (name_owner);

      print_objects (manager);

      g_signal_connect (manager,
                        "notify::name-owner",
                        G_CALLBACK (on_notify_name_owner),
                        NULL);
      g_signal_connect (manager,
                        "object-added",
                        G_CALLBACK (on_object_added),
                        NULL);
      g_signal_connect (manager,
                        "object-removed",
                        G_CALLBACK (on_object_removed),
                        NULL);
      g_signal_connect (manager,
                        "interface-proxy-properties-changed",
                        G_CALLBACK (on_interface_proxy_properties_changed),
                        NULL);

      g_main_loop_run (loop);

     out:
      if (manager != NULL)
        g_object_unref (manager);
      if (loop != NULL)
        g_main_loop_unref (loop);

      return 0;
    }