Archive for December 2, 2010
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:
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.