From 37244aab41298699898f5e71114051b77b45a5d7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 13 Dec 2012 02:38:53 +0100 Subject: gdbus: Add support for D-Bus client proxies --- gdbus/client.c | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- gdbus/gdbus.h | 14 +++ 2 files changed, 343 insertions(+), 4 deletions(-) diff --git a/gdbus/client.c b/gdbus/client.c index f561564..5d19b19 100644 --- a/gdbus/client.c +++ b/gdbus/client.c @@ -44,8 +44,280 @@ struct GDBusClient { void *disconn_data; GDBusMessageFunction signal_func; void *signal_data; + GDBusProxyFunction proxy_added; + GDBusProxyFunction proxy_removed; + void *proxy_data; + GList *proxy_list; }; +struct GDBusProxy { + gint ref_count; + GDBusClient *client; + char *obj_path; + char *interface; +}; + +static GDBusProxy *proxy_new(GDBusClient *client, const char *path, + const char *interface) +{ + GDBusProxy *proxy; + + proxy = g_try_new0(GDBusProxy, 1); + if (proxy == NULL) + return NULL; + + proxy->client = client; + proxy->obj_path = g_strdup(path); + proxy->interface = g_strdup(interface); + + return g_dbus_proxy_ref(proxy); +} + +static void proxy_free(gpointer data) +{ + GDBusProxy *proxy = data; + + if (proxy->client) { + GDBusClient *client = proxy->client; + + if (client->proxy_removed) + client->proxy_removed(proxy, client->proxy_data); + + proxy->client = NULL; + } + + g_dbus_proxy_unref(proxy); +} + +static void proxy_remove(GDBusClient *client, const char *path, + const char *interface) +{ + GList *list; + + for (list = g_list_first(client->proxy_list); list; + list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + + if (g_str_equal(proxy->interface, interface) == TRUE && + g_str_equal(proxy->obj_path, path) == TRUE) { + client->proxy_list = + g_list_delete_link(client->proxy_list, list); + proxy_free(proxy); + break; + } + } +} + +GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy) +{ + if (proxy == NULL) + return NULL; + + g_atomic_int_inc(&proxy->ref_count); + + return proxy; +} + +void g_dbus_proxy_unref(GDBusProxy *proxy) +{ + if (proxy == NULL) + return; + + if (g_atomic_int_dec_and_test(&proxy->ref_count) == FALSE) + return; + + g_free(proxy->obj_path); + g_free(proxy->interface); + + g_free(proxy); +} + +const char *g_dbus_proxy_get_path(GDBusProxy *proxy) +{ + if (proxy == NULL) + return NULL; + + return proxy->obj_path; +} + +const char *g_dbus_proxy_get_interface(GDBusProxy *proxy) +{ + if (proxy == NULL) + return NULL; + + return proxy->interface; +} + +static void parse_properties(GDBusClient *client, const char *path, + const char *interface, DBusMessageIter *iter) +{ + GDBusProxy *proxy; + + if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE) + return; + + if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) + return; + + proxy = proxy_new(client, path, interface); + + if (client->proxy_added) + client->proxy_added(proxy, client->proxy_data); + + client->proxy_list = g_list_append(client->proxy_list, proxy); +} + +static void parse_interfaces(GDBusClient *client, const char *path, + DBusMessageIter *iter) +{ + DBusMessageIter dict; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *interface; + + dbus_message_iter_recurse(&dict, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + break; + + dbus_message_iter_get_basic(&entry, &interface); + dbus_message_iter_next(&entry); + + dbus_message_iter_recurse(&entry, &value); + parse_properties(client, path, interface, &value); + + dbus_message_iter_next(&dict); + } +} + +static void interfaces_added(GDBusClient *client, DBusMessage *msg) +{ + DBusMessageIter iter; + const char *path; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return; + + dbus_message_iter_get_basic(&iter, &path); + dbus_message_iter_next(&iter); + + parse_interfaces(client, path, &iter); +} + +static void interfaces_removed(GDBusClient *client, DBusMessage *msg) +{ + DBusMessageIter iter, entry; + const char *path; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return; + + dbus_message_iter_get_basic(&iter, &path); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(&iter, &entry); + + while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { + const char *interface; + + dbus_message_iter_get_basic(&entry, &interface); + proxy_remove(client, path, interface); + dbus_message_iter_next(&entry); + } +} + +static void parse_managed_objects(GDBusClient *client, DBusMessage *msg) +{ + DBusMessageIter iter, dict; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(&iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry; + const char *path; + + dbus_message_iter_recurse(&dict, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != + DBUS_TYPE_OBJECT_PATH) + break; + + dbus_message_iter_get_basic(&entry, &path); + dbus_message_iter_next(&entry); + + parse_interfaces(client, path, &entry); + + dbus_message_iter_next(&dict); + } +} + +static void get_managed_objects_reply(DBusPendingCall *call, void *user_data) +{ + GDBusClient *client = user_data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, reply) == TRUE) { + dbus_error_free(&error); + goto done; + } + + parse_managed_objects(client, reply); + +done: + dbus_message_unref(reply); + + dbus_pending_call_unref(client->pending_call); + client->pending_call = NULL; +} + +static void get_managed_objects(GDBusClient *client) +{ + DBusMessage *msg; + + msg = dbus_message_new_method_call("org.bluez", "/", + DBUS_INTERFACE_DBUS ".ObjectManager", + "GetManagedObjects"); + if (msg == NULL) + return; + + dbus_message_append_args(msg, DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(client->dbus_conn, msg, + &client->pending_call, -1) == FALSE) { + dbus_message_unref(msg); + return; + } + + dbus_pending_call_set_notify(client->pending_call, + get_managed_objects_reply, client, NULL); + + dbus_message_unref(msg); +} + static void modify_match_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); @@ -67,7 +339,7 @@ static gboolean modify_match(DBusConnection *conn, const char *member, msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, member); - if (!msg) + if (msg == NULL) return FALSE; dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule, @@ -115,6 +387,8 @@ done: dbus_pending_call_unref(client->pending_call); client->pending_call = NULL; + + get_managed_objects(client); } static void get_name_owner(GDBusClient *client, const char *name) @@ -123,7 +397,7 @@ static void get_name_owner(GDBusClient *client, const char *name) msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); - if (!msg) + if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, @@ -180,14 +454,18 @@ static DBusHandlerResult message_filter(DBusConnection *connection, if (client->disconn_func) client->disconn_func(client->dbus_conn, client->disconn_data); + g_free(client->unique_name); client->unique_name = NULL; } else if (*old == '\0') { + g_free(client->unique_name); + client->unique_name = g_strdup(new); + if (client->connect_func) client->connect_func(client->dbus_conn, client->connect_data); - g_free(client->unique_name); - client->unique_name = g_strdup(new); + + get_managed_objects(client); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -197,6 +475,37 @@ static DBusHandlerResult message_filter(DBusConnection *connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(sender, client->unique_name) == TRUE) { + const char *path; + + path = dbus_message_get_path(message); + + if (g_str_equal(path, "/") == TRUE) { + const char *interface, *member; + + interface = dbus_message_get_interface(message); + + if (g_str_equal(interface, DBUS_INTERFACE_DBUS + ".ObjectManager") == FALSE) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + member = dbus_message_get_member(message); + + if (g_str_equal(member, "InterfacesAdded") == TRUE) { + interfaces_added(client, message); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (g_str_equal(member, "InterfacesRemoved") == TRUE) { + interfaces_removed(client, message); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (g_str_has_prefix(path, client->base_path) == FALSE) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + if (client->signal_func) client->signal_func(client->dbus_conn, message, client->signal_data); @@ -288,6 +597,8 @@ void g_dbus_client_unref(GDBusClient *client) dbus_connection_remove_filter(client->dbus_conn, message_filter, client); + g_list_free_full(client->proxy_list, proxy_free); + if (client->disconn_func) client->disconn_func(client->dbus_conn, client->disconn_data); @@ -335,3 +646,17 @@ gboolean g_dbus_client_set_signal_watch(GDBusClient *client, return TRUE; } + +gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, + GDBusProxyFunction added, + GDBusProxyFunction removed, void *user_data) +{ + if (client == NULL) + return FALSE; + + client->proxy_added = added; + client->proxy_removed = removed; + client->proxy_data = user_data; + + return TRUE; +} diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h index cd7ac25..3eed52b 100644 --- a/gdbus/gdbus.h +++ b/gdbus/gdbus.h @@ -268,6 +268,14 @@ gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, gboolean g_dbus_attach_object_manager(DBusConnection *connection); gboolean g_dbus_detach_object_manager(DBusConnection *connection); +typedef struct GDBusProxy GDBusProxy; + +GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy); +void g_dbus_proxy_unref(GDBusProxy *proxy); + +const char *g_dbus_proxy_get_path(GDBusProxy *proxy); +const char *g_dbus_proxy_get_interface(GDBusProxy *proxy); + typedef struct GDBusClient GDBusClient; GDBusClient *g_dbus_client_new(DBusConnection *connection, @@ -283,6 +291,12 @@ gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data); +typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data); + +gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, + GDBusProxyFunction added, + GDBusProxyFunction removed, void *user_data); + #ifdef __cplusplus } #endif -- cgit 1.2.3-korg