summaryrefslogtreecommitdiff
path: root/gdbus
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2012-12-13 02:38:53 +0100
committerMarcel Holtmann <marcel@holtmann.org>2012-12-19 03:50:42 +0100
commitb30177d961387744a9aa36b05bcb102a5de47343 (patch)
tree1b5bee31f0360f458775b5fea0a933d3cee65711 /gdbus
parent8d5bb26e4356ac055691b2267e0f1ed2bb81df4c (diff)
gdbus: Add support for D-Bus client proxies
Diffstat (limited to 'gdbus')
-rw-r--r--gdbus/client.c333
-rw-r--r--gdbus/gdbus.h14
2 files changed, 343 insertions, 4 deletions
diff --git a/gdbus/client.c b/gdbus/client.c
index f561564c..5d19b19c 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 cd7ac25b..3eed52b8 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