D-BUS Tips 3.0: Performing asynchronous/synchronous method invocations and dbus-binding-tool

To easy perform a synchronous remote method invocation you have just to use GLib bindings  for DBus Proxy API (DBusGProxy), that is our first example. The second example shows how to use generated code by dbus-binding-tool to implement remote calls faster. Examples bellow are implemented accessing org.freedesktop.NetworkManager interface of NetworkManager API.

Example 1


int main()
{
	g_type_init();

	GError *error = NULL;
	DBusGConnection *conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);

	if (error != NULL) {
		g_error("D-BUS Connection error: %s", error->message);
		g_error_free(error);
	}

	if (!conn) {
		g_error("D-BUS connection cannot be created");
		return EXIT_FAILURE;
	}

	DBusGProxy *proxy = dbus_g_proxy_new_for_name(conn,
			"org.freedesktop.NetworkManager",
			"/org/freedesktop/NetworkManager",
			"org.freedesktop.NetworkManager");

	if (!proxy) {
		g_error("Cannot create proxy");
		return EXIT_FAILURE;
	}

	g_message("Calling NetworkManager.sate synchronously");
	guint state;
	GError *error = NULL;
	if (!dbus_g_proxy_call(proxy, "state", &error, G_TYPE_INVALID,
			G_TYPE_UINT, &state, G_TYPE_INVALID)) {
		if (error->domain == DBUS_GERROR && error->code
					== DBUS_GERROR_REMOTE_EXCEPTION) {
			g_error("Caught remote method exception %s: %s",
					dbus_g_error_get_name(error),
					error->message);
		} else {
			g_error("D-BUS: %s", error->message);
		}
	}
	print_network_manager_state(state);

	g_object_unref(proxy);
	dbus_g_connection_unref(conn);
	return EXIT_SUCCESS;
}

The code is self explaining. Be careful to use the right type parameters, otherwise the call will not work, at D-Bus tutorial page you can find GLib – DBus type mapping. If you want to use more complex types outside this mapping, GLib may show you the message “No marshaller for signature of …” and you will need to register a custom marshaller to this type, but this is a subject for future posts.

Example 2

A faster way to call a remote method is to generate client stub. First you need to get XML API of object interface, you can use d-feet tool to call DBus Introspectable interface and get that.

instrospect

Then execute the following command passing network-manager-api.xml file:


dbus-binding-tool --mode=glib-client network-manager-api.xml >  network-manager-client-stub.h

And you will simply get the code ready to execute D-BUS calls and they are properly typed according to method signatures. The example show you an asynchronous call using the stub:

#include "network-manager-client-stub.h"

void network_state_callback(DBusGProxy *proxy, guint state, GError *error, gpointer userdata)
{
	print_network_manager_state(state);
	g_main_loop_quit(loop);
}

static GMainLoop *loop;
int main()
{
	loop = g_main_loop_new(NULL, FALSE);
	g_type_init();

	GError *error = NULL;
	DBusGConnection *conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);

	if (error != NULL) {
		g_error("D-BUS Connection error: %s", error->message);
		g_error_free(error);
	}

	if (!conn) {
		g_error("D-BUS connection cannot be created");
		return EXIT_FAILURE;
	}

	DBusGProxy *proxy = dbus_g_proxy_new_for_name(conn,
			"org.freedesktop.NetworkManager",
			"/org/freedesktop/NetworkManager",
			"org.freedesktop.NetworkManager");

	if (!proxy) {
		g_error("Cannot create proxy");
		return EXIT_FAILURE;
	}

	g_message("Calling NetworkManager.sate asynchronously");
	org_freedesktop_NetworkManager_state_async(proxy, network_state_callback , NULL);

	g_message("Waiting D-BUS callback");
	g_main_loop_run(loop);

	g_message("Exiting glib mainloop");

	g_main_loop_unref(loop);
	g_object_unref(proxy);
	dbus_g_connection_unref(conn);
	return EXIT_SUCCESS;
}

See full code example in method-invocation.c at dbus-glib-sample project.

D-BUS Tips 2.0: Listening to D-BUS signals in C with D-Bus Glib bindings

I will present two ways to handle signals from D-BUS, the first consists in use a connection filter to register callback function that should be invoked when the corresponding match occurs, the second uses a proxy.

First, let us choose the signal we want to listen. To see current active D-BUS services on system (I am using Ubuntu 10.04), you can open a debugger tool like d-feet and see something like that:

debugger

Let us catch ‘StateChange’ signal from NetworkManager Interface (API definition link), in our test case to dispatch this signal, you should connect or disconnect your active network connection after run the code sample.

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

DBusHandlerResult signal_filter(DBusConnection *connection, DBusMessage *msg,
		void *user_data)
{
	if (dbus_message_is_signal(msg, "org.freedesktop.NetworkManager",
			"StateChange")) {
		read_network_manager_state_change(msg);
	}
	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}


int main()
{
	GMainLoop *loop = g_main_loop_new(NULL, FALSE);
	DBusError error;

	dbus_error_init(&error);
	DBusConnection *conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);

	if (dbus_error_is_set(&error)) {
		g_error("Cannot get System BUS connection: %s", error.message);
		dbus_error_free(&error);
		return EXIT_FAILURE;
	}
	dbus_connection_setup_with_g_main(conn, NULL);

	char *rule = "type='signal',interface='org.freedesktop.NetworkManager'";
	g_message("Signal match rule: %s", rule);
	dbus_bus_add_match(conn, rule, &error);

	if (dbus_error_is_set(&error)) {
		g_error("Cannot add D-BUS match rule, cause: %s", error.message);
		dbus_error_free(&error);
		return EXIT_FAILURE;
	}

	g_message("Listening to D-BUS signals using a connection filter");
	dbus_connection_add_filter(conn, signal_filter, NULL, NULL);

	g_main_loop_run(loop);

	return EXIT_SUCCESS; 
}

The filter function return DBUS_HANDLER_RESULT_HANDLED, other handlers will not receive this message, but in example code we want the other BUS listeners catch the network connection signal.

Do not forget to call dbus_connection_setup_with_g_main function since the connection was created from dbus_bus_get function, otherwise the GLib mainloop and D-BUS callbacks will not work properly and will you be very sad, belive me.

To read the message you can use D-BUS Message API, it is part of D-Bus low-level public API.

void read_network_manager_state_change(DBusMessage *msg)
{
	DBusError error;
	dbus_error_init(&error);

	guint32 state = 0;

	if (!dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &state,
			DBUS_TYPE_INVALID)) {
		g_error("Cannot read NetworkManager state change message, cause: %s", error.message);
		dbus_error_free(&error);
		return;
	}
	print_network_manager_state(state);
}

The second example show how to receive the same signal, but using a proxy from remote object.

#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

void state_changed_callback(DBusGProxy *proxy, guint32 state,
		gpointer user_data)
{
	print_network_manager_state(state);
}

int main()
{
	GMainLoop *loop = g_main_loop_new(NULL, FALSE);

	g_type_init();

	GError *error = NULL;
	DBusGConnection *conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);

	if (error != NULL) {
		g_error("D-BUS Connection error: %s", error->message);
		g_error_free(error);
	}

	if (!conn) {
		g_error("D-BUS connection cannot be created");
		return EXIT_FAILURE;
	}

	DBusGProxy *proxy = dbus_g_proxy_new_for_name(conn,
			"org.freedesktop.NetworkManager",
			"/org/freedesktop/NetworkManager",
			"org.freedesktop.NetworkManager");

	if (!proxy) {
		g_error("Cannot create proxy");
		return EXIT_FAILURE;
	}

	dbus_g_proxy_add_signal(proxy, "StateChange", G_TYPE_UINT,
			G_TYPE_INVALID);

	dbus_g_proxy_connect_signal(proxy, "StateChange",
			G_CALLBACK(state_changed_callback), NULL, NULL);

	g_message("Waiting D-BUS proxy callback for signal");
	g_main_loop_run(loop);

	return EXIT_SUCCESS;
}

Next post I will talk about asynchronous/synchronous calls.

Download full example code at gitorious dbus-glib-sample project.

D-BUS Tips 1.0: Getting in D-BUS world

This is the first of a sequence of posts related to D-BUS communication. I guess this can help you to find an easy and painless way to work with this technology since official documentation is under development.

D-Bus is a system for interprocess communication (IPC), and it is a good way to integrate operating system/desktop applications on linux, the windows port is under development. You can find details about “what is D-BUS?” at tutorial page.

Next post will describe how to listen to application signals.