Hi Szymon,
On 11/29/2017 09:16 AM, Szymon Janc wrote:
---
Makefile.am | 2 +
ell/dbus-client.c | 595 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/dbus-client.h | 87 ++++++++
ell/ell.h | 1 +
4 files changed, 685 insertions(+)
create mode 100644 ell/dbus-client.c
create mode 100644 ell/dbus-client.h
diff --git a/Makefile.am b/Makefile.am
index a95bac4..19b957e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,6 +32,7 @@ pkginclude_HEADERS = ell/ell.h \
ell/genl.h \
ell/dbus.h \
ell/dbus-service.h \
+ ell/dbus-client.h \
ell/hwdb.h \
ell/cipher.h \
ell/random.h \
@@ -74,6 +75,7 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/dbus-message.c \
ell/dbus-util.c \
ell/dbus-service.c \
+ ell/dbus-client.c \
ell/dbus-name-cache.c \
ell/dbus-filter.c \
ell/gvariant-private.h \
diff --git a/ell/dbus-client.c b/ell/dbus-client.c
new file mode 100644
index 0000000..e6b6ff3
--- /dev/null
+++ b/ell/dbus-client.c
@@ -0,0 +1,595 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2017 Codecoup. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ell.h"
+#include "private.h"
+
+struct l_dbus_client {
+ struct l_dbus *dbus;
+ unsigned int watch;
+ unsigned int added_watch;
+ unsigned int removed_watch;
+ char *service;
+ char *path;
+ uint32_t objects_call;
+
+ l_dbus_watch_func_t connect_cb;
+ void *connect_cb_data;
+ l_dbus_destroy_func_t connect_cb_data_destroy;
+
+ l_dbus_watch_func_t disconnect_cb;
+ void *disconnect_cb_data;
+ l_dbus_destroy_func_t disconnect_cb_data_destroy;
+
+ l_dbus_client_ready_func_t ready_cb;
+ void *ready_cb_data;
+ l_dbus_destroy_func_t ready_cb_data_destroy; > +
+ l_dbus_client_proxy_func_t proxy_added_cb;
+ l_dbus_client_proxy_func_t proxy_removed_cb;
+ l_dbus_client_property_function_t properties_changed_cb;
+ void *proxy_cb_data;
+ l_dbus_destroy_func_t proxy_cb_data_destroy;
+
+ struct l_queue *proxies;
+};
+
+struct proxy_property {
+ char *name;
+ struct l_dbus_message *msg;
+};
+
+struct l_dbus_proxy {
+ int refcount;
+ struct l_dbus_client *client;
+ char *interface;
+ char *path;
+ uint32_t properties_watch;
+ bool ready;
+
+ struct l_queue *properties;
+};
+
+LIB_EXPORT const char *l_dbus_proxy_get_path(struct l_dbus_proxy *proxy)
+{
+ if (unlikely(!proxy))
+ return NULL;
+
+ return proxy->path;
+}
+
+LIB_EXPORT const char *l_dbus_proxy_get_interface(struct l_dbus_proxy *proxy)
+{
+ if (unlikely(!proxy))
+ return NULL;
+
+ return proxy->interface;
+}
+
+static bool find_property_match(const void *a, const void *b)
property_match_by_name?
+{
+ const struct proxy_property *prop = a;
+ const char *name = b;
+
+ return !strcmp(prop->name, name);
+}
+
+static struct proxy_property *find_property(struct l_dbus_proxy *proxy,
+ const char *name)
+{
+ return l_queue_find(proxy->properties, find_property_match, name);
+}
+
+static struct proxy_property *get_property(struct l_dbus_proxy *proxy,
+ const char *name)
+{
+ struct proxy_property *prop;
+
+ prop = find_property(proxy, name);
+ if (prop)
+ return prop;
+
+ prop = l_new(struct proxy_property, 1);
+ prop->name = l_strdup(name);
+
+ l_queue_push_tail(proxy->properties, prop);
+
+ return prop;
+}
+
+LIB_EXPORT bool l_dbus_proxy_get_property(struct l_dbus_proxy *proxy,
+ const char *name,
+ const char *signature, ...)
+{
+ struct proxy_property *prop;
+ va_list args;
+ bool res;
+
+ if (unlikely(!proxy))
+ return false;
+
+ prop = find_property(proxy, name);
+ if (!prop)
+ return false;
+
+ va_start(args, signature);
+ res = l_dbus_message_get_arguments_valist(prop->msg, signature, args);
+ va_end(args);
+
+ return res;
+}
+
+static void property_free(void *data)
+{
+ struct proxy_property *prop = data;
+
+ if (prop->msg)
+ l_dbus_message_unref(prop->msg);
+
+ l_free(prop->name);
+ l_free(prop);
+}
+
+static struct l_dbus_proxy *l_dbus_proxy_ref(struct l_dbus_proxy *proxy)
+{
+ if (unlikely(!proxy))
+ return NULL;
+
+ __sync_fetch_and_add(&proxy->refcount, 1);
+
+ return proxy;
+}
+
+static void l_dbus_proxy_unref(struct l_dbus_proxy *proxy)
+{
+ if (unlikely(!proxy))
+ return;
+
+ if (__sync_sub_and_fetch(&proxy->refcount, 1))
+ return;
+
+ if (proxy->properties_watch)
+ l_dbus_remove_signal_watch(proxy->client->dbus,
+ proxy->properties_watch);
+
+ l_queue_destroy(proxy->properties, property_free);
+ l_free(proxy->interface);
+ l_free(proxy->path);
+ l_free(proxy);
+}
+
+static void proxy_update_property(struct l_dbus_proxy *proxy,
+ const char *name,
+ struct l_dbus_message_iter *property)
+{
+ struct l_dbus_message_builder *builder;
+ struct proxy_property *prop = get_property(proxy, name);
+
+ l_dbus_message_unref(prop->msg);
+
+ if (!property) {
+ prop->msg = NULL;
+ goto done;
+ }
+
+ prop->msg = l_dbus_message_new_signal(proxy->client->dbus, proxy->path,
+ proxy->interface, name);
So for every property we build a dummy message and put the contents of
the property into the message, right? E.g. If Powered = true was
signaled, we build a signal with member="Powered" and a single byte as
contents.
I'm okay with this being the first naive solution, but this seems quite
wasteful. Can we store the serialized property data without the
enclosing message? E.g. by using GVariant serializer and storing the
raw data?
+ if (!prop->msg)
+ return;
+
+ builder = l_dbus_message_builder_new(prop->msg);
+ l_dbus_message_builder_append_from_iter(builder, property);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+done:
+ if (proxy->client->properties_changed_cb && proxy->ready)
How does proxy->ready get set for manually created proxies? Or is a
proxy supposed to be always obtained by the proxy_added callback? If
so, why export the proxy_new constructor?
+ proxy->client->properties_changed_cb(proxy, name,
prop->msg,
+ proxy->client->proxy_cb_data_destroy);
+}
+
+static void proxy_invalidate_properties(struct l_dbus_proxy *proxy,
+ struct l_dbus_message_iter* props)
+{
+ const char *name;
+
+ while (l_dbus_message_iter_next_entry(props, &name))
+ proxy_update_property(proxy, name, NULL);
+}
+
+static void proxy_update_properties(struct l_dbus_proxy *proxy,
+ struct l_dbus_message_iter* props)
+{
+ struct l_dbus_message_iter variant;
+ const char *name;
+
+ while (l_dbus_message_iter_next_entry(props, &name, &variant))
+ proxy_update_property(proxy, name, &variant);
+}
+
+static void properties_changed_callback(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_proxy *proxy = user_data;
+ const char *interface;
+ struct l_dbus_message_iter changed;
+ struct l_dbus_message_iter invalidated;
+
+ if (!l_dbus_message_get_arguments(message, "sa{sv}as", &interface,
+ &changed, &invalidated)) {
+ return;
+ }
No need for {}
+
+ proxy_update_properties(proxy, &changed);
+ proxy_invalidate_properties(proxy, &invalidated);
+}
+
+LIB_EXPORT struct l_dbus_proxy *l_dbus_proxy_new(struct l_dbus_client *client,
+ const char *path, const char *interface)
+{
+ struct l_dbus_proxy *proxy;
+
+ proxy = l_new(struct l_dbus_proxy, 1);
+
+ proxy->properties_watch = l_dbus_add_signal_watch(client->dbus,
+ client->service, path,
+ L_DBUS_INTERFACE_PROPERTIES,
+ "PropertiesChanged",
+ L_DBUS_MATCH_ARGUMENT(0),
+ interface, L_DBUS_MATCH_NONE,
+ properties_changed_callback,
+ proxy);
+ if (!proxy->properties_watch) {
+ l_free(proxy);
+ return NULL;
+ }
+
+ proxy->refcount = 1;
+ proxy->client = client;
+ proxy->interface = l_strdup(interface);
+ proxy->path = l_strdup(path);
+ proxy->properties = l_queue_new();
+
+ l_queue_push_tail(client->proxies, proxy);
+
+ return proxy;
+}
+
+static bool is_ignorable(const char *interface)
+{
+ static const struct {
+ const char *interface;
+ } interfaces_to_ignore[] = {
+ { L_DBUS_INTERFACE_OBJECT_MANAGER },
+ { L_DBUS_INTERFACE_INTROSPECTABLE },
+ { L_DBUS_INTERFACE_PROPERTIES },
+ };
+ size_t i;
+
+ for (i = 0; i < L_ARRAY_SIZE(interfaces_to_ignore); i++)
+ if (!strcmp(interfaces_to_ignore[i].interface, interface))
+ return true;
+
+ return false;
+}
+
+struct find_proxy_data
+{
+ const char *path;
+ const char *interface;
+};
+
+static bool find_proxy_match(const void *a, const void *b)
+{
+ const struct l_dbus_proxy *proxy = a;
+ const struct find_proxy_data *data = b;
+
+ return !strcmp(proxy->interface, data->interface) &&
+ !strcmp(proxy->path, data->path);
+}
+
+static struct l_dbus_proxy *find_proxy(struct l_dbus_client *client,
+ const char *path, const char *interface)
+{
+ struct find_proxy_data data;
+
+ data.path = path;
+ data.interface = interface;
+
+ return l_queue_find(client->proxies, find_proxy_match, &data);
It might be more compact to rewrite this using l_queue_get_entries. No
need to define struct find_proxy_data, etc.
+}
+
+static void parse_interface(struct l_dbus_client *client, const char *path,
+ const char *interface,
+ struct l_dbus_message_iter *properties)
+{
+ struct l_dbus_proxy *proxy;
+ bool proxy_added = false;
+
+ if (is_ignorable(interface))
+ return;
+
+ proxy = find_proxy(client, path, interface);
+ if (!proxy) {
+ proxy = l_dbus_proxy_new(client, path, interface);
+ proxy_added = true;
+ }
+
+ if (!proxy)
+ return;
+
+ proxy_update_properties(proxy, properties);
+
+ if (proxy_added && client->proxy_added_cb) {
+ proxy->ready = true;
Why does setting ready depend on proxy_added_cb being valid?
+ client->proxy_added_cb(proxy, client->proxy_cb_data);
+ }
+}
+
+static void parse_object(struct l_dbus_client *client, const char *path,
+ struct l_dbus_message_iter *object)
+{
+ const char *interface;
+ struct l_dbus_message_iter properties;
+
+ if (!path)
+ return;
+
+ while (l_dbus_message_iter_next_entry(object, &interface, &properties))
+ parse_interface(client, path, interface, &properties);
+}
+
+static void interfaces_added_callback(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_client *client = user_data;
+ struct l_dbus_message_iter object;
+ const char *path;
+
+ l_dbus_message_get_arguments(message, "oa{sa{sv}}", &path, &object);
Might want an error check here
+
+ parse_object(client, path, &object);
+}
+
+static void interfaces_removed_callback(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_client *client = user_data;
+ struct l_dbus_message_iter interfaces;
+ const char *interface;
+ const char *path;
+
+ l_dbus_message_get_arguments(message, "oas", &path, &interfaces);
+
+ while (l_dbus_message_iter_next_entry(&interfaces, &interface)) {
+ struct l_dbus_proxy *proxy;
+
+ proxy = find_proxy(client, path, interface);
+ if (!proxy)
+ continue;
+
+ l_queue_remove(proxy->client->proxies, proxy);
+
+ if (client->proxy_removed_cb)
+ client->proxy_removed_cb(proxy, client->proxy_cb_data);
+
+ l_dbus_proxy_unref(proxy);
+ }
+}
+
+static void get_managed_objects_reply(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_client *client = user_data;
+ struct l_dbus_message_iter objects;
+ struct l_dbus_message_iter object;
+ const char *path;
+
+ client->objects_call = 0;
+
+ if (l_dbus_message_is_error(message)) {
+ return;
+ }
No need for {}
+
+ l_dbus_message_get_arguments(message, "a{oa{sa{sv}}}", &objects);
+
+ while (l_dbus_message_iter_next_entry(&objects, &path, &object))
+ parse_object(client, path, &object);
+
+ client->added_watch = l_dbus_add_signal_watch(client->dbus,
+ client->service, "/",
+ L_DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesAdded",
+ L_DBUS_MATCH_NONE,
+ interfaces_added_callback,
+ client);
+
+ client->removed_watch = l_dbus_add_signal_watch(client->dbus,
+ client->service, "/",
+ L_DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesRemoved",
+ L_DBUS_MATCH_NONE,
+ interfaces_removed_callback,
+ client);
+
+ if (client->ready_cb)
+ client->ready_cb(client, client->ready_cb_data);
+}
+
+static void service_appeared_callback(struct l_dbus *dbus, void *user_data)
+{
+ struct l_dbus_client *client = user_data;
+
+ /* TODO should we allow to set different root? */
+ client->objects_call = l_dbus_method_call(dbus, client->service, "/",
Should "/" be client->path instead? Note that ell only exports
ObjectManager on the root path. It doesn't export it for any child
paths. However, BlueZ might act differently.
+ L_DBUS_INTERFACE_OBJECT_MANAGER,
+ "GetManagedObjects", NULL,
+ get_managed_objects_reply,
+ client, NULL);
+
+ if (client->connect_cb)
+ client->connect_cb(client->dbus, client->connect_cb_data);
+}
+
+static void service_disappeared_callback(struct l_dbus *dbus, void *user_data)
+{
+ struct l_dbus_client *client = user_data;
+
+ if (client->disconnect_cb)
+ client->disconnect_cb(client->dbus, client->disconnect_cb_data);
+
+
No double empty lines please
+ l_queue_clear(client->proxies,
+ (l_queue_destroy_func_t)l_dbus_proxy_unref);
+}
+
+LIB_EXPORT struct l_dbus_client *l_dbus_client_new(struct l_dbus *dbus,
+ const char *service, const char *path)
+{
+ struct l_dbus_client *client;
+
+ client = l_new(struct l_dbus_client, 1);
+
+ client->dbus = dbus;
+
+ client->watch = l_dbus_add_service_watch(dbus, service,
+ service_appeared_callback,
+ service_disappeared_callback,
+ client, NULL);
+
+ if (!client->watch) {
+ l_free(client);
+ return NULL;
+ }
+
+ client->service = l_strdup(service);
+ client->path = l_strdup(path);
+ client->proxies = l_queue_new();
+
+ return client;
+}
+
+LIB_EXPORT void l_dbus_client_destroy(struct l_dbus_client *client)
+{
+ if (unlikely(!client))
+ return;
+
+ if (client->watch)
+ l_dbus_remove_signal_watch(client->dbus, client->watch);
+
+ if (client->added_watch)
+ l_dbus_remove_signal_watch(client->dbus, client->added_watch);
+
+ if (client->removed_watch)
+ l_dbus_remove_signal_watch(client->dbus, client->removed_watch);
+
+ if (client->connect_cb_data_destroy)
+ client->connect_cb_data_destroy(client->connect_cb_data);
+
+ if (client->disconnect_cb_data_destroy)
+ client->disconnect_cb_data_destroy(client->disconnect_cb_data);
+
+ if (client->ready_cb_data_destroy)
+ client->ready_cb_data_destroy(client->ready_cb_data);
+
+ if (client->proxy_cb_data_destroy)
+ client->proxy_cb_data_destroy(client->proxy_cb_data);
+
+ if (client->objects_call)
+ l_dbus_cancel(client->dbus, client->objects_call)
+;
+ l_queue_destroy(client->proxies,
+ (l_queue_destroy_func_t)l_dbus_proxy_unref);
+
+ l_free(client->service);
+ l_free(client->path);
+ l_free(client);
+}
+
+LIB_EXPORT bool l_dbus_client_set_connect_handler(struct l_dbus_client *client,
+ l_dbus_watch_func_t function,
+ void *user_data,
+ l_dbus_destroy_func_t destroy)
+{
+ if (unlikely(!client))
+ return false;
+
+ client->connect_cb = function;
+ client->connect_cb_data = user_data;
+ client->connect_cb_data_destroy = destroy;
+
+ return true;
+}
+
+LIB_EXPORT bool l_dbus_client_set_disconnect_handler(struct l_dbus_client *client,
+ l_dbus_watch_func_t function,
+ void *user_data,
+ l_dbus_destroy_func_t destroy)
+{
+ if (unlikely(!client))
+ return false;
+
+ client->disconnect_cb = function;
+ client->disconnect_cb_data = user_data;
+ client->disconnect_cb_data_destroy = destroy;
+
+ return true;
+}
+
+LIB_EXPORT bool l_dbus_client_set_ready_handler(struct l_dbus_client *client,
+ l_dbus_client_ready_func_t function,
+ void *user_data,
+ l_dbus_destroy_func_t destroy)
+{
+ if (unlikely(!client))
+ return false;
+
+ client->ready_cb = function;
+ client->ready_cb_data = user_data;
+ client->ready_cb_data_destroy = destroy;
+
+ return true;
+}
+
+LIB_EXPORT bool l_dbus_client_set_proxy_handlers(struct l_dbus_client *client,
+ l_dbus_client_proxy_func_t proxy_added,
+ l_dbus_client_proxy_func_t proxy_removed,
+ l_dbus_client_property_function_t property_changed,
+ void *user_data,
+ l_dbus_destroy_func_t destroy)
+{
+ if (unlikely(!client))
+ return false;
+
+ client->proxy_added_cb = proxy_added;
+ client->proxy_removed_cb = proxy_removed;
+ client->properties_changed_cb = property_changed;
+ client->proxy_cb_data = user_data;
+ client->proxy_cb_data_destroy = destroy;
+
+ return true;
+}
diff --git a/ell/dbus-client.h b/ell/dbus-client.h
new file mode 100644
index 0000000..b32f4a3
--- /dev/null
+++ b/ell/dbus-client.h
@@ -0,0 +1,87 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2017 Codecoup. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __ELL_DBUS_CLIENT_H
+#define __ELL_DBUS_CLIENT_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct l_dbus;
+struct l_dbus_message;
+struct l_dbus_client;
+struct l_dbus_proxy;
+
+typedef void (*l_dbus_client_ready_func_t)(struct l_dbus_client *client,
+ void *user_data);
+typedef void (*l_dbus_client_proxy_func_t) (struct l_dbus_proxy *proxy,
+ void *user_data);
+typedef void (*l_dbus_client_property_function_t) (struct l_dbus_proxy *proxy,
+ const char *name,
+ struct l_dbus_message *msg,
+ void *user_data);
+
+struct l_dbus_client *l_dbus_client_new(struct l_dbus *dbus,
+ const char *service, const char *path);
+void l_dbus_client_destroy(struct l_dbus_client *client);
+
+bool l_dbus_client_set_connect_handler(struct l_dbus_client *client,
+ l_dbus_watch_func_t function,
+ void *user_data,
+ l_dbus_destroy_func_t destroy);
+
+bool l_dbus_client_set_disconnect_handler(struct l_dbus_client *client,
+ l_dbus_watch_func_t function,
+ void *user_data,
+ l_dbus_destroy_func_t destroy);
+
+bool l_dbus_client_set_ready_handler(struct l_dbus_client *client,
+ l_dbus_client_ready_func_t function,
+ void *user_data,
+ l_dbus_destroy_func_t destroy);
+
+bool l_dbus_client_set_proxy_handlers(struct l_dbus_client *client,
+ l_dbus_client_proxy_func_t proxy_added,
+ l_dbus_client_proxy_func_t proxy_removed,
+ l_dbus_client_property_function_t property_changed,
+ void *user_data,
+ l_dbus_destroy_func_t destroy);
+
+struct l_dbus_proxy *l_dbus_proxy_new(struct l_dbus_client *client,
+ const char *path,
+ const char *interface);
+
+const char *l_dbus_proxy_get_path(struct l_dbus_proxy *proxy);
+
+const char *l_dbus_proxy_get_interface(struct l_dbus_proxy *proxy);
+
+bool l_dbus_proxy_get_property(struct l_dbus_proxy *proxy, const char *name,
+ const char *signature, ...);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_DBUS_CLIENT_H */
diff --git a/ell/ell.h b/ell/ell.h
index 2efd1c4..4083ee5 100644
--- a/ell/ell.h
+++ b/ell/ell.h
@@ -51,3 +51,4 @@
#include <ell/genl.h>
#include <ell/dbus.h>
#include <ell/dbus-service.h>
+#include <ell/dbus-client.h>
Regards,
-Denis