diff --git a/spa/plugins/bluez5/dbus-manager.c b/spa/plugins/bluez5/dbus-manager.c deleted file mode 100644 index 99287b6bf..000000000 --- a/spa/plugins/bluez5/dbus-manager.c +++ /dev/null @@ -1,686 +0,0 @@ -/* Spa dbus - * - * Copyright © 2022 Pauli Virtanen - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "dbus-manager.h" - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT (&impl->log_topic) - -#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" - -struct object; - -struct impl -{ - struct spa_dbus_object_manager this; - - DBusConnection *conn; - - struct spa_log_topic log_topic; - struct spa_log *log; - - struct object *root; -}; - -struct object -{ - struct impl *impl; - struct spa_dbus_local_object alignas(max_align_t) this; -}; - -const struct spa_dbus_property *object_interface_get_property(struct object *o, - const struct spa_dbus_local_interface *iface, const char *name) -{ - const struct spa_dbus_property *prop; - - for (prop = iface->properties; prop && prop->name; ++prop) { - if (spa_streq(prop->name, name) && (prop->exists == NULL || prop->exists(&o->this))) - return prop; - } - - return NULL; -} - -const struct spa_dbus_local_interface *object_get_interface(struct object *o, const char *interface) -{ - const struct spa_dbus_local_interface *iface; - - for (iface = o->this.interfaces; iface && iface->name; ++iface) - if (spa_streq(interface, iface->name)) - return iface; - - return NULL; -} - -static DBusMessage *object_properties_get(struct object *o, DBusMessage *m) -{ - struct impl *impl = o->impl; - const char *interface, *name; - const struct spa_dbus_local_interface *iface; - const struct spa_dbus_property *prop; - DBusMessage *r; - DBusMessageIter i, v; - int res; - - if (!dbus_message_get_args(m, NULL, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, - "Invalid arguments"); - - iface = object_get_interface(o, interface); - if (!iface) - return dbus_message_new_error(m, DBUS_ERROR_UNKNOWN_INTERFACE, - "No such interface"); - - prop = object_interface_get_property(o, iface, name); - if (!prop) - return dbus_message_new_error(m, DBUS_ERROR_UNKNOWN_PROPERTY, - "No such property"); - - if (!prop->get) - return dbus_message_new_error(m, DBUS_ERROR_FAILED, - "Write-only property"); - - r = dbus_message_new_method_return(m); - if (r == NULL) - return NULL; - - dbus_message_iter_init_append(r, &i); - dbus_message_iter_open_container(&i, DBUS_TYPE_VARIANT, prop->signature, &v); - - if ((res = prop->get(&o->this, &v)) < 0) { - spa_log_debug(impl->log, "failed to get property %s value: %s", - prop->name, spa_strerror(res)); - dbus_message_unref(r); - return dbus_message_new_error(m, DBUS_ERROR_FAILED, - "Failed to get property"); - } - - dbus_message_iter_close_container(&i, &v); - - return r; -} - -static int object_append_properties(struct object *o, const struct spa_dbus_property *properties, DBusMessageIter *i) -{ - struct impl *impl = o->impl; - DBusMessageIter d; - const struct spa_dbus_property *prop; - - dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "{sv}", &d); - - for (prop = properties; prop && prop->name; ++prop) { - DBusMessageIter e, v; - int res; - - if (!(prop->exists == NULL || prop->exists(&o->this)) || !prop->get) - continue; - - dbus_message_iter_open_container(&d, DBUS_TYPE_DICT_ENTRY, NULL, &e); - dbus_message_iter_append_basic(&e, DBUS_TYPE_STRING, &prop->name); - dbus_message_iter_open_container(&e, DBUS_TYPE_VARIANT, prop->signature, &v); - - if ((res = prop->get(&o->this, &v)) < 0) { - spa_log_debug(impl->log, "failed to get property %s value: %s", - prop->name, spa_strerror(res)); - return res; - } - - dbus_message_iter_close_container(&e, &v); - dbus_message_iter_close_container(&d, &e); - } - - dbus_message_iter_close_container(i, &d); - - return 0; -} - -static DBusMessage *object_properties_get_all(struct object *o, DBusMessage *m) -{ - const char *interface; - const struct spa_dbus_local_interface *iface; - DBusMessageIter i; - DBusMessage *r; - - if (!dbus_message_get_args(m, NULL, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID)) - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, - "Invalid arguments"); - - iface = object_get_interface(o, interface); - if (!iface) - return dbus_message_new_error(m, DBUS_ERROR_UNKNOWN_INTERFACE, - "No such interface"); - - r = dbus_message_new_method_return(m); - if (r == NULL) - return NULL; - - dbus_message_iter_init_append(r, &i); - - if (object_append_properties(o, iface->properties, &i) < 0) { - dbus_message_unref(r); - return dbus_message_new_error(m, DBUS_ERROR_FAILED, - "Failed to get properties"); - } - - return r; -} - -static DBusMessage *object_properties_set(struct object *o, DBusMessage *m) -{ - struct impl *impl = o->impl; - const char *interface, *name; - const struct spa_dbus_local_interface *iface; - const struct spa_dbus_property *prop; - DBusMessageIter it, value; - char *value_signature; - bool valid_signature; - int res; - - if (!dbus_message_has_signature(m, "ssv")) - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, - "Invalid arguments"); - - dbus_message_iter_init(m, &it); - dbus_message_iter_get_basic(&it, &interface); - dbus_message_iter_next(&it); - dbus_message_iter_get_basic(&it, &name); - dbus_message_iter_next(&it); - - iface = object_get_interface(o, interface); - if (!iface) - return dbus_message_new_error(m, DBUS_ERROR_UNKNOWN_INTERFACE, - "No such interface"); - - prop = object_interface_get_property(o, iface, name); - if (!prop) - return dbus_message_new_error(m, DBUS_ERROR_UNKNOWN_PROPERTY, - "No such property"); - - if (prop->set == NULL) - return dbus_message_new_error(m, DBUS_ERROR_PROPERTY_READ_ONLY, - "Read-only property"); - - dbus_message_iter_recurse(&it, &value); - - value_signature = dbus_message_iter_get_signature(&value); - valid_signature = spa_streq(prop->signature, value_signature); - dbus_free(value_signature); - if (!valid_signature) - return dbus_message_new_error(m, DBUS_ERROR_INVALID_SIGNATURE, - "Invalid value signature"); - - if ((res = prop->set(&o->this, &value)) < 0) { - spa_log_debug(impl->log, "failed to set property %s value: %s", - prop->name, spa_strerror(res)); - return dbus_message_new_error(m, DBUS_ERROR_FAILED, - "Failed to set property"); - } - - return dbus_message_new_method_return(m); -} - -static DBusHandlerResult object_handler(DBusConnection *c, DBusMessage *m, void *userdata) -{ - struct object *o = userdata; - struct impl *impl = o->impl; - const char *path, *interface, *member; - DBusMessage *r = NULL; - - path = dbus_message_get_path(m); - interface = dbus_message_get_interface(m); - member = dbus_message_get_member(m); - - spa_log_debug(impl->log, "dbus: path=%s, interface=%s, member=%s", path, interface, member); - - if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "Get")) { - r = object_properties_get(o, m); - } else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "GetAll")) { - r = object_properties_get_all(o, m); - } else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "Set")) { - r = object_properties_set(o, m); - } else { - const struct spa_dbus_local_interface *iface = object_get_interface(o, interface); - bool called = false; - - if (iface) { - const struct spa_dbus_method *method; - - for (method = iface->methods; method && method->name; ++method) { - if (dbus_message_is_method_call(m, iface->name, method->name)) { - r = method->call(&o->this, m); - called = true; - break; - } - } - } - - if (!called) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - if (r != NULL && dbus_connection_send(impl->conn, r, NULL)) { - dbus_message_unref(r); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (r) { - dbus_message_unref(r); - } - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -static int object_signal_interfaces_added(struct object *o) -{ - struct impl *impl = o->impl; - struct object *root = impl->root; - const struct spa_dbus_local_interface *iface; - DBusMessage *s; - DBusMessageIter i, a; - - if (root == NULL) - root = o; /* we're root, impl still initializing */ - - s = dbus_message_new_signal(root->this.path, - DBUS_INTERFACE_OBJECT_MANAGER, - "InterfacesAdded"); - if (s == NULL) - return -ENOMEM; - - dbus_message_iter_init_append(s, &i); - dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &o->this.path); - - dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "{sa{sv}}", &a); - - for (iface = o->this.interfaces; iface && iface->name; ++iface) { - DBusMessageIter e; - int res; - - spa_log_debug(impl->log, "dbus: signal add interface path=%s interface=%s", - o->this.path, iface->name); - - dbus_message_iter_open_container(&a, DBUS_TYPE_DICT_ENTRY, NULL, &e); - dbus_message_iter_append_basic(&e, DBUS_TYPE_STRING, &iface->name); - - if ((res = object_append_properties(o, iface->properties, &e)) < 0) { - dbus_message_unref(s); - return res; - } - - dbus_message_iter_close_container(&a, &e); - } - - dbus_message_iter_close_container(&i, &a); - - dbus_connection_send(impl->conn, s, NULL); - dbus_message_unref(s); - - return 0; -} - -static int object_signal_interfaces_removed(struct object *o) -{ - struct impl *impl = o->impl; - struct object *root = impl->root; - const struct spa_dbus_local_interface *iface; - DBusMessage *s; - DBusMessageIter i, a; - - spa_assert(root); - - s = dbus_message_new_signal(root->this.path, - DBUS_INTERFACE_OBJECT_MANAGER, - "InterfacesRemoved"); - if (s == NULL) - return -ENOMEM; - - dbus_message_iter_init_append(s, &i); - dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &o->this.path); - - dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "s", &a); - - for (iface = o->this.interfaces; iface && iface->name; ++iface) { - spa_log_debug(impl->log, "dbus: signal remove interface path=%s interface=%s", - o->this.path, iface->name); - - dbus_message_iter_append_basic(&a, DBUS_TYPE_STRING, &iface->name); - } - - dbus_message_iter_close_container(&i, &a); - - dbus_connection_send(impl->conn, s, NULL); - dbus_message_unref(s); - - return 0; -} - -static int object_signal_properties_changed(struct object *o, - const struct spa_dbus_local_interface *iface, - const struct spa_dbus_property *properties) -{ - struct impl *impl = o->impl; - const struct spa_dbus_property *prop; - DBusMessage *s; - DBusMessageIter i, a; - int res; - - if (properties == NULL || properties->name == NULL) { - /* nothing was changed */ - return 0; - } - - s = dbus_message_new_signal(o->this.path, - DBUS_INTERFACE_PROPERTIES, - "PropertiesChanged"); - if (s == NULL) - return -ENOMEM; - - dbus_message_iter_init_append(s, &i); - dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &iface->name); - - if ((res = object_append_properties(o, properties, &i)) < 0) { - dbus_message_unref(s); - return res; - } - - dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "s", &a); - - for (prop = properties; prop && prop->name; ++prop) { - spa_log_debug(impl->log, "dbus: signal properties changed path=%s interface=%s property=%s", - o->this.path, iface->name, prop->name); - - if (prop->exists == NULL || prop->exists(&o->this)) - continue; - dbus_message_iter_append_basic(&a, DBUS_TYPE_STRING, &prop->name); - } - - dbus_message_iter_close_container(&i, &a); - - dbus_connection_send(impl->conn, s, NULL); - dbus_message_unref(s); - - return 0; -} - -static struct object *object_new(struct impl *impl, - const char *path, - const struct spa_dbus_local_interface *interfaces, - size_t object_size, - void *user_data) -{ - static const DBusObjectPathVTable vtable = { - .message_function = object_handler, - }; - const struct spa_dbus_local_interface *iface; - struct object *o; - - o = calloc(1, sizeof(struct object) - sizeof(struct spa_dbus_local_object) + object_size); - if (o == NULL) - return NULL; - - spa_log_debug(impl->log, "dbus: register path=%s", path); - - if (!dbus_connection_register_object_path(impl->conn, path, &vtable, o)) { - free(o); - errno = EIO; - return NULL; - } - - spa_list_append(&impl->this.object_list, &o->this.link); - o->impl = impl; - o->this.path = strdup(path); - o->this.interfaces = interfaces; - o->this.user_data = user_data; - - spa_assert(o->this.path); - - for (iface = o->this.interfaces; iface && iface->name; ++iface) { - if (iface->init) - iface->init(&o->this); - } - - object_signal_interfaces_added(o); - - return o; -} - -static void object_destroy(struct object *o) -{ - struct impl *impl = o->impl; - const struct spa_dbus_local_interface *iface; - - object_signal_interfaces_removed(o); - - spa_list_remove(&o->this.link); - - for (iface = o->this.interfaces; iface && iface->name; ++iface) { - if (iface->destroy) - iface->destroy(&o->this); - } - - spa_log_debug(impl->log, "dbus: unregister path=%s", o->this.path); - - dbus_connection_unregister_object_path(impl->conn, o->this.path); - - free(o); -} - -static struct object *object_find(struct impl *impl, const char *path) -{ - struct spa_dbus_local_object *obj; - - spa_list_for_each(obj, &impl->this.object_list, link) { - struct object *o = SPA_CONTAINER_OF(obj, struct object, this); - - if (spa_streq(o->this.path, path)) - return o; - } - - return NULL; -} - -static DBusMessage *root_get_managed_objects(struct spa_dbus_local_object *object, DBusMessage *m) -{ - struct impl *impl = object->user_data; - DBusMessage *r; - DBusMessageIter i, object_array; - struct spa_dbus_local_object *obj; - - if ((r = dbus_message_new_method_return(m)) == NULL) - return NULL; - - dbus_message_iter_init_append(r, &i); - dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "{oa{sa{sv}}}", &object_array); - - spa_list_for_each(obj, &impl->this.object_list, link) { - struct object *o = SPA_CONTAINER_OF(obj, struct object, this); - - const struct spa_dbus_local_interface *iface; - DBusMessageIter object_entry; - DBusMessageIter interface_array; - - dbus_message_iter_open_container(&object_array, DBUS_TYPE_DICT_ENTRY, NULL, &object_entry); - dbus_message_iter_append_basic(&object_entry, DBUS_TYPE_OBJECT_PATH, &o->this.path); - - dbus_message_iter_open_container(&object_entry, DBUS_TYPE_ARRAY, "{sa{sv}}", &interface_array); - - for (iface = o->this.interfaces; iface && iface->name; ++iface) { - DBusMessageIter interface_entry; - - dbus_message_iter_open_container(&interface_array, DBUS_TYPE_DICT_ENTRY, NULL, &interface_entry); - dbus_message_iter_append_basic(&interface_entry, DBUS_TYPE_STRING, &iface->name); - - if (object_append_properties(o, iface->properties, &interface_entry) < 0) { - dbus_message_unref(r); - return dbus_message_new_error(m, DBUS_ERROR_FAILED, - "Failed to get properties"); - } - - dbus_message_iter_close_container(&interface_array, &interface_entry); - } - - dbus_message_iter_close_container(&object_entry, &interface_array); - dbus_message_iter_close_container(&object_array, &object_entry); - } - - dbus_message_iter_close_container(&i, &object_array); - - return r; -} - -static const struct spa_dbus_method root_methods[] = { - { - .name = "GetManagedObjects", - .call = root_get_managed_objects, - }, - {NULL} -}; - -static const struct spa_dbus_local_interface root_interfaces[] = { - { - .name = DBUS_INTERFACE_OBJECT_MANAGER, - .methods = root_methods, - }, - {NULL} -}; - -struct spa_dbus_object_manager *spa_dbus_object_manager_new(DBusConnection *conn, const char *path, struct spa_log *log) -{ - struct impl *impl; - - impl = calloc(1, sizeof(struct impl)); - if (impl == NULL) - return NULL; - - impl->conn = conn; - impl->log = log; - - impl->log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.dbus"); - - spa_log_topic_init(impl->log, &impl->log_topic); - - spa_list_init(&impl->this.object_list); - - impl->root = object_new(impl, path, root_interfaces, sizeof(struct spa_dbus_local_object), impl); - if (impl->root == NULL) { - free(impl); - return NULL; - } - - impl->this.path = impl->root->this.path; - - dbus_connection_ref(impl->conn); - - return &impl->this; -} - -void spa_dbus_object_manager_destroy(struct spa_dbus_object_manager *this) -{ - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); - struct spa_dbus_local_object *obj; - struct spa_list tmp; - - spa_list_init(&tmp); - spa_list_remove(&impl->root->this.link); - spa_list_append(&tmp, &impl->root->this.link); - - spa_list_consume(obj, &impl->this.object_list, link) { - struct object *o = SPA_CONTAINER_OF(obj, struct object, this); - - object_destroy(o); - } - - object_destroy(impl->root); - - dbus_connection_unref(impl->conn); - free(impl); -} - -struct spa_dbus_local_object *spa_dbus_object_manager_register(struct spa_dbus_object_manager *this, - const char *path, - const struct spa_dbus_local_interface *interfaces, - size_t object_size, void *user_data) -{ - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); - struct object *o; - int root_len = strlen(this->path); - - if (!(spa_strstartswith(path, this->path) && path[root_len] == '/' && - path[root_len + 1] != '\0')) { - errno = EINVAL; - return NULL; - } - - o = object_new(impl, path, interfaces, object_size, user_data); - if (o == NULL) - return NULL; - - return &o->this; -} - -void spa_dbus_object_manager_unregister(struct spa_dbus_object_manager *this, - struct spa_dbus_local_object *object) -{ - struct object *o = SPA_CONTAINER_OF(object, struct object, this); - - object_destroy(o); -} - -struct spa_dbus_local_object *spa_dbus_object_manager_find(struct spa_dbus_object_manager *this, - const char *path) -{ - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); - struct object *o; - - o = object_find(impl, path); - if (o) - return &o->this; - - return NULL; -} - - -int spa_dbus_object_manager_properties_changed(struct spa_dbus_object_manager *this, - struct spa_dbus_local_object *object, - const struct spa_dbus_local_interface *interface, - const struct spa_dbus_property *properties) -{ - struct object *o = SPA_CONTAINER_OF(object, struct object, this); - - return object_signal_properties_changed(o, interface, properties); -} diff --git a/spa/plugins/bluez5/dbus-manager.h b/spa/plugins/bluez5/dbus-manager.h deleted file mode 100644 index 9b5acf843..000000000 --- a/spa/plugins/bluez5/dbus-manager.h +++ /dev/null @@ -1,188 +0,0 @@ -/* Spa dbus - * - * Copyright © 2022 Pauli Virtanen - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef SPA_DBUS_MANAGER_H_ -#define SPA_DBUS_MANAGER_H_ - -#include - -#include -#include - -struct spa_dbus_local_object; - -/** DBus object manager */ -struct spa_dbus_object_manager -{ - /** Root DBus object path */ - const char *path; - - /** List (non-mutable) of objects. */ - struct spa_list object_list; -}; - -/** DBus property specification a local DBus object. */ -struct spa_dbus_property -{ - const char *name; /**< Name of property */ - const char *signature; /**< DBus type signature of the value */ - - /** - * Hook to append bare DBus value to the iterator. - * If NULL, the property is considered to not be readable. - */ - int (*get)(struct spa_dbus_local_object *object, DBusMessageIter *value); - - /** - * Hook to get DBus value from the iterator, and apply it. - * If NULL, the property is considered read-only. - */ - int (*set)(struct spa_dbus_local_object *object, DBusMessageIter *value); - - /** - * Hook to check if the property currently exists. - * If NULL, the property always exists. - */ - bool (*exists)(struct spa_dbus_local_object *object); -}; - -/** DBus method specification for a local DBus object. */ -struct spa_dbus_method -{ - const char *name; /**< Name of method */ - - /** Hook to react and reply to DBus method call */ - DBusMessage *(*call)(struct spa_dbus_local_object *object, DBusMessage *m); -}; - -/** DBus interface specification for a local DBus object. */ -struct spa_dbus_local_interface -{ - /** Name of the DBus interface */ - const char *name; - - /** Array of properties, zero-terminated */ - const struct spa_dbus_property *properties; - - /** Array of methods, zero-terminated */ - const struct spa_dbus_method *methods; - - /** - * Hook called when initializing the object, before - * calling any other hooks. - */ - void (*init)(struct spa_dbus_local_object *object); - - /** - * Hook called once when interface is destroyed. - * No other hooks are called after this. - */ - void (*destroy)(struct spa_dbus_local_object *object); -}; - -/** - * DBus local object structure. - * - * One object struct exists for each registered object path. The same object - * struct may have multiple interfaces. The object structures are owned, - * allocated and freed by the object manager. - * - * A custom object struct can also be used, for example - * - * struct my_local_object { - * struct spa_dbus_local_object object; - * int my_extra_value; - * }; - * - * Its initialization and teardown can be done via the interface - * init/destroy hooks. Note that the hooks of all interfaces - * the object has are called on the same object struct. - * - * The struct size is specified in the call to - * spa_dbus_object_manager_register. - */ -struct spa_dbus_local_object -{ - struct spa_list link; /**< Link (non-mutable) to manager object list */ - const char *path; /**< DBus object path */ - - /** Zero-terminated array of the DBus interfaces of the objects */ - const struct spa_dbus_local_interface *interfaces; - - /** Pointer passed to spa_dbus_object_manager_register */ - void *user_data; -}; - -/** - * Create and register new DBus object manager at the given object path. - * - * Registers a DBus object with the object manager interface at the given path. - * - * \param conn DBus connection. - * \param path Object path to register the new manager at. - * \param log Logging output. - */ -struct spa_dbus_object_manager *spa_dbus_object_manager_new(DBusConnection *conn, const char *path, struct spa_log *log); - -/** Destroy and unregister the object manager and all objects owned by it. */ -void spa_dbus_object_manager_destroy(struct spa_dbus_object_manager *manager); - -/** - * Create and register a new DBus object under the object manager. - * - * The DBus object path must be a sub-path of the object manager path. - * - * \param manager Manager that owns the new object. - * \param path DBus object path. - * \param interfaces Zero-terminated array of interfaces for the new object. - * \param object_size Size of the object struct. Must be >= sizeof(struct spa_dbus_local_object). - * \param user_data User data pointer to set in the object. - */ -struct spa_dbus_local_object *spa_dbus_object_manager_register(struct spa_dbus_object_manager *manager, - const char *path, - const struct spa_dbus_local_interface *interfaces, - size_t object_size, void *user_data); - -/** Find previously registered local DBus object by object path */ -struct spa_dbus_local_object *spa_dbus_object_manager_find(struct spa_dbus_object_manager *manager, - const char *path); - -/** Unregister and destroy a previously registered local DBus object */ -void spa_dbus_object_manager_unregister(struct spa_dbus_object_manager *manager, - struct spa_dbus_local_object *object); - -/** - * Emit PropertiesChanged signal for a previously registered local DBus object. - * - * \param manager Object manager - * \param object The DBus object - * \param interface The interface to emit the signal for - * \param properties Zero-terminated array of properties that were changed - */ -int spa_dbus_object_manager_properties_changed(struct spa_dbus_object_manager *manager, - struct spa_dbus_local_object *object, - const struct spa_dbus_local_interface *interface, - const struct spa_dbus_property *properties); - -#endif diff --git a/spa/plugins/bluez5/dbus-monitor.c b/spa/plugins/bluez5/dbus-monitor.c deleted file mode 100644 index 6286d7b0b..000000000 --- a/spa/plugins/bluez5/dbus-monitor.c +++ /dev/null @@ -1,717 +0,0 @@ -/* Spa dbus - * - * Copyright © 2022 Pauli Virtanen - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include "dbus-monitor.h" - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT (&impl->log_topic) - -#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" - -struct impl -{ - struct spa_dbus_monitor this; - - DBusConnection *conn; - - struct spa_log_topic log_topic; - struct spa_log *log; - - struct spa_dbus_async_call get_managed_objects_call; - - unsigned int objects_listed:1; - - struct spa_list objects[]; -}; - -struct object -{ - unsigned int removed:1; - unsigned int updating:1; - struct spa_dbus_object alignas(max_align_t) this; -}; - -static void object_emit_update(struct object *o) -{ - if (o->this.interface->update) - o->this.interface->update(&o->this); -} - -static void object_emit_remove(struct object *o) -{ - if (o->this.interface->remove) - o->this.interface->remove(&o->this); -} - -static void object_emit_property(struct object *o, const char *key, DBusMessageIter *value) -{ - if (o->this.interface->property) - o->this.interface->property(&o->this, key, value); -} - -static struct object *object_new(struct impl *impl, const struct spa_dbus_interface *iface, - struct spa_list *object_list, const char *path) -{ - struct object *o; - - spa_assert(iface->object_size >= sizeof(struct spa_dbus_object)); - - o = calloc(1, sizeof(struct object) + iface->object_size - sizeof(struct spa_dbus_object)); - if (o == NULL) - return NULL; - - o->this.path = strdup(path); - o->this.interface = iface; - o->this.user_data = impl->this.user_data; - - spa_assert(o->this.path); - - spa_list_append(object_list, &o->this.link); - - return o; -} - -static void object_remove(struct object *o) -{ - if (o->removed) - return; - - o->updating = true; - o->removed = true; - object_emit_remove(o); - spa_list_remove(&o->this.link); -} - -static void object_destroy(struct object *o) -{ - object_remove(o); - free((void *)o->this.path); - free(o); -} - -static const struct spa_dbus_interface *interface_find(struct impl *impl, const char *interface, struct spa_list **objects) -{ - const struct spa_dbus_interface *iface; - size_t i; - - for (iface = impl->this.interfaces, i = 0; iface && iface->name; ++iface, ++i) { - if (spa_streq(iface->name, interface)) { - *objects = &impl->objects[i]; - return iface; - } - } - - return NULL; -} - -static struct object *object_find(struct spa_list *object_list, const char *path) -{ - struct spa_dbus_object *object; - - spa_list_for_each(object, object_list, link) { - struct object *o = SPA_CONTAINER_OF(object, struct object, this); - - if (spa_streq(o->this.path, path)) - return o; - } - - return NULL; -} - -static int object_update_props(struct impl *impl, - struct object *o, - DBusMessageIter *props_iter, - DBusMessageIter *invalidated_iter) -{ - o->updating = true; - - if (o->this.interface->property) { - while (invalidated_iter && dbus_message_iter_get_arg_type(invalidated_iter) != DBUS_TYPE_INVALID) { - const char *key; - - dbus_message_iter_get_basic(invalidated_iter, &key); - - object_emit_property(o, key, NULL); - if (o->removed) - goto removed; - - dbus_message_iter_next(invalidated_iter); - } - - while (props_iter && dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) { - DBusMessageIter entry, value; - const char *key; - - dbus_message_iter_recurse(props_iter, &entry); - dbus_message_iter_get_basic(&entry, &key); - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - - object_emit_property(o, key, &value); - if (o->removed) - goto removed; - - dbus_message_iter_next(props_iter); - } - } - - object_emit_update(o); - if (o->removed) - goto removed; - - o->updating = false; - return 0; - -removed: - object_destroy(o); - return 0; -} - -static void interface_added(struct impl *impl, - const char *object_path, - const char *interface_name, - DBusMessageIter *props_iter) -{ - struct object *o; - const struct spa_dbus_interface *iface; - struct spa_list *object_list; - - iface = interface_find(impl, interface_name, &object_list); - if (!iface) { - spa_log_trace(impl->log, "dbus: skip path=%s, interface=%s", - object_path, interface_name); - return; - } - - spa_log_debug(impl->log, "dbus: added path=%s, interface=%s", object_path, interface_name); - - o = object_find(object_list, object_path); - if (o == NULL) { - o = object_new(impl, iface, object_list, object_path); - if (o == NULL) { - spa_log_warn(impl->log, "can't create object: %m"); - return; - } - - } - - object_update_props(impl, o, props_iter, NULL); -} - -static void interfaces_added(struct impl *impl, DBusMessageIter *arg_iter) -{ - DBusMessageIter it[3]; - const char *object_path; - - dbus_message_iter_get_basic(arg_iter, &object_path); - dbus_message_iter_next(arg_iter); - dbus_message_iter_recurse(arg_iter, &it[0]); - - while (dbus_message_iter_get_arg_type(&it[0]) != DBUS_TYPE_INVALID) { - const char *interface_name; - - dbus_message_iter_recurse(&it[0], &it[1]); - dbus_message_iter_get_basic(&it[1], &interface_name); - dbus_message_iter_next(&it[1]); - dbus_message_iter_recurse(&it[1], &it[2]); - - interface_added(impl, object_path, interface_name, &it[2]); - - dbus_message_iter_next(&it[0]); - } -} - -static void interfaces_removed(struct impl *impl, DBusMessageIter *arg_iter) -{ - const char *object_path; - DBusMessageIter it; - - dbus_message_iter_get_basic(arg_iter, &object_path); - dbus_message_iter_next(arg_iter); - dbus_message_iter_recurse(arg_iter, &it); - - while (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_INVALID) { - const char *interface_name; - const struct spa_dbus_interface *iface; - struct spa_list *object_list; - - dbus_message_iter_get_basic(&it, &interface_name); - - iface = interface_find(impl, interface_name, &object_list); - if (iface) { - struct object *o; - - spa_log_debug(impl->log, "dbus: removed path=%s, interface=%s", - object_path, interface_name); - - o = object_find(object_list, object_path); - if (o) - object_destroy(o); - } else { - spa_log_trace(impl->log, "dbus: skip removed path=%s, interface=%s", - object_path, interface_name); - } - - dbus_message_iter_next(&it); - } -} - -static void get_managed_objects_reply(struct spa_dbus_async_call *call, DBusMessage *r) -{ - struct impl *impl = SPA_CONTAINER_OF(call, struct impl, get_managed_objects_call); - DBusMessageIter it[6]; - - if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) { - spa_log_warn(impl->log, "ObjectManager not available at path=%s", - impl->this.path); - return; - } - - if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { - spa_log_error(impl->log, "GetManagedObjects() at %s failed: %s", - impl->this.path, dbus_message_get_error_name(r)); - return; - } - - if (!dbus_message_iter_init(r, &it[0]) || - !dbus_message_has_signature(r, "a{oa{sa{sv}}}")) { - spa_log_error(impl->log, "Invalid reply signature for GetManagedObjects() at %s", - impl->this.path); - return; - } - - /* Add fake object representing the service itself */ - interface_added(impl, impl->this.path, SPA_DBUS_MONITOR_NAME_OWNER_INTERFACE, NULL); - - dbus_message_iter_recurse(&it[0], &it[1]); - - while (dbus_message_iter_get_arg_type(&it[1]) != DBUS_TYPE_INVALID) { - dbus_message_iter_recurse(&it[1], &it[2]); - - interfaces_added(impl, &it[2]); - - dbus_message_iter_next(&it[1]); - } - - impl->objects_listed = true; -} - -static int get_managed_objects(struct impl *impl) -{ - DBusMessage *m; - - if (impl->objects_listed || impl->get_managed_objects_call.pending) - return 0; - - m = dbus_message_new_method_call(impl->this.service, - impl->this.path, - DBUS_INTERFACE_OBJECT_MANAGER, - "GetManagedObjects"); - if (m == NULL) - return -ENOMEM; - - dbus_message_set_auto_start(m, false); - - return spa_dbus_async_call_send(&impl->get_managed_objects_call, impl->conn, m, - get_managed_objects_reply); -} - - -static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data) -{ - struct impl *impl = user_data; - DBusError err = DBUS_ERROR_INIT; - - if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { - const char *name, *old_owner, *new_owner; - bool has_old_owner, has_new_owner; - - spa_log_debug(impl->log, "dbus: name owner changed %s", dbus_message_get_path(m)); - - if (!dbus_message_get_args(m, &err, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old_owner, - DBUS_TYPE_STRING, &new_owner, - DBUS_TYPE_INVALID)) { - spa_log_error(impl->log, - "Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", - err.message); - goto done; - } - - if (!spa_streq(name, impl->this.service)) - goto done; - - has_old_owner = old_owner && *old_owner; - has_new_owner = new_owner && *new_owner; - - if (has_old_owner) - spa_log_debug(impl->log, "dbus: %s disappeared", impl->this.service); - - if (has_old_owner || has_new_owner) { - size_t i; - const struct spa_dbus_interface *iface; - - impl->objects_listed = false; - - for (iface = impl->this.interfaces, i = 0; iface && iface->name; ++iface, ++i) { - struct spa_dbus_object *object; - - spa_list_consume(object, &impl->objects[i], link) { - struct object *o = SPA_CONTAINER_OF(object, struct object, this); - - object_destroy(o); - } - } - } - - if (has_new_owner) { - spa_log_debug(impl->log, "dbus: %s appeared", impl->this.service); - get_managed_objects(impl); - } - } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded")) { - DBusMessageIter it; - - spa_log_debug(impl->log, "dbus: interfaces added on path=%s", dbus_message_get_path(m)); - - if (!impl->objects_listed) - goto done; - - if (!dbus_message_iter_init(m, &it) || !dbus_message_has_signature(m, "oa{sa{sv}}")) { - spa_log_error(impl->log, "Invalid signature found in InterfacesAdded"); - goto done; - } - - interfaces_added(impl, &it); - } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved")) { - DBusMessageIter it; - - spa_log_debug(impl->log, "dbus: interfaces removed on path=%s", dbus_message_get_path(m)); - - if (!impl->objects_listed) - goto done; - - if (!dbus_message_iter_init(m, &it) || !dbus_message_has_signature(m, "oas")) { - spa_log_error(impl->log, "Invalid signature found in InterfacesRemoved"); - goto done; - } - - interfaces_removed(impl, &it); - } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) { - DBusMessageIter args, props, invalidated; - const char *interface_name, *path; - const struct spa_dbus_interface *iface; - struct spa_list *object_list; - - if (!impl->objects_listed) - goto done; - - if (!dbus_message_iter_init(m, &args) || - !dbus_message_has_signature(m, "sa{sv}as")) { - spa_log_error(impl->log, "Invalid signature found in PropertiesChanged"); - goto done; - } - path = dbus_message_get_path(m); - - dbus_message_iter_get_basic(&args, &interface_name); - dbus_message_iter_next(&args); - dbus_message_iter_recurse(&args, &props); - dbus_message_iter_next(&args); - dbus_message_iter_recurse(&args, &invalidated); - - iface = interface_find(impl, interface_name, &object_list); - if (iface) { - struct object *o; - - o = object_find(object_list, path); - if (o == NULL) { - spa_log_debug(impl->log, "Properties changed in unknown object %s", - path); - goto done; - } - - spa_log_debug(impl->log, "dbus: properties changed in path=%s", path); - object_update_props(impl, o, &props, &invalidated); - } - } - -done: - dbus_error_free(&err); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static int add_filters(struct impl *impl) -{ - DBusError err = DBUS_ERROR_INIT; - char rule[1024]; - const struct spa_dbus_interface *iface; - - if (!dbus_connection_add_filter(impl->conn, filter_cb, impl, NULL)) { - spa_log_error(impl->log, "failed to add DBus filter"); - return -EIO; - } - - spa_scnprintf(rule, sizeof(rule), - "type='signal',sender='org.freedesktop.DBus'," - "interface='org.freedesktop.DBus',member='NameOwnerChanged'," - "arg0='%s'", - impl->this.service); - spa_log_trace(impl->log, "add match: %s", rule); - dbus_bus_add_match(impl->conn, rule, &err); - if (dbus_error_is_set(&err)) - goto fail; - - spa_scnprintf(rule, sizeof(rule), - "type='signal',sender='%s'," - "interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'", - impl->this.service); - spa_log_trace(impl->log, "add match: %s", rule); - dbus_bus_add_match(impl->conn, rule, &err); - if (dbus_error_is_set(&err)) - goto fail; - - spa_scnprintf(rule, sizeof(rule), - "type='signal',sender='%s'," - "interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'", - impl->this.service); - spa_log_trace(impl->log, "add match: %s", rule); - dbus_bus_add_match(impl->conn, rule, &err); - if (dbus_error_is_set(&err)) - goto fail; - - for (iface = impl->this.interfaces; iface && iface->name; ++iface) { - spa_scnprintf(rule, sizeof(rule), - "type='signal',sender='%s'," - "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'," - "arg0='%s'", - impl->this.service, iface->name); - spa_log_trace(impl->log, "add match: %s", rule); - dbus_bus_add_match(impl->conn, rule, &err); - if (dbus_error_is_set(&err)) - goto fail; - } - - dbus_error_free(&err); - return 0; - -fail: - dbus_connection_remove_filter(impl->conn, filter_cb, impl); - - spa_log_error(impl->log, "failed to add DBus match: %s", err.message); - dbus_error_free(&err); - return -EIO; -} - -struct spa_dbus_monitor *spa_dbus_monitor_new(DBusConnection *conn, - const char *service, - const char *path, - const struct spa_dbus_interface *interfaces, - struct spa_log *log, - void *user_data) -{ - struct impl *impl; - const struct spa_dbus_interface *iface; - size_t num_interfaces = 0, i; - int res; - - for (iface = interfaces; iface && iface->name; ++iface) { - spa_assert(iface->object_size >= sizeof(struct spa_dbus_object)); - ++num_interfaces; - } - - impl = calloc(1, sizeof(struct impl) + sizeof(struct spa_list) * num_interfaces); - if (impl == NULL) - return NULL; - - impl->conn = conn; - impl->this.service = strdup(service); - impl->this.path = strdup(path); - impl->this.interfaces = interfaces; - impl->this.user_data = user_data; - - impl->log = log; - - impl->log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.dbus"); - spa_log_topic_init(impl->log, &impl->log_topic); - - for (i = 0; i < num_interfaces; ++i) - spa_list_init(&impl->objects[i]); - - spa_assert(impl->this.service); - spa_assert(impl->this.path); - - if ((res = add_filters(impl)) < 0) { - free((void *)impl->this.service); - free((void *)impl->this.path); - free(impl); - errno = -res; - return NULL; - } - - dbus_connection_ref(conn); - - get_managed_objects(impl); - - return &impl->this; -} - -void spa_dbus_monitor_destroy(struct spa_dbus_monitor *this) -{ - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); - const struct spa_dbus_interface *iface; - struct spa_dbus_object *object; - size_t i; - - dbus_connection_remove_filter(impl->conn, filter_cb, impl); - - spa_dbus_async_call_cancel(&impl->get_managed_objects_call); - - for (iface = impl->this.interfaces, i = 0; iface && iface->name; ++iface, ++i) { - spa_list_consume(object, &impl->objects[i], link) { - struct object *o = SPA_CONTAINER_OF(object, struct object, this); - - object_destroy(o); - } - } - - dbus_connection_unref(impl->conn); - - free((void *)impl->this.service); - free((void *)impl->this.path); - free(impl); -} - -struct spa_dbus_object *spa_dbus_monitor_find(struct spa_dbus_monitor *this, const char *path, const char *interface) -{ - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); - struct object *o; - const struct spa_dbus_interface *iface; - struct spa_list *object_list; - - if (path == NULL) - return NULL; - - spa_assert(interface); - - iface = interface_find(impl, interface, &object_list); - if (!iface) - return NULL; - - o = object_find(object_list, path); - if (o) - return &o->this; - - return NULL; -} - -void spa_dbus_monitor_ignore_object(struct spa_dbus_monitor *this, struct spa_dbus_object *object) -{ - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); - struct object *o = SPA_CONTAINER_OF(object, struct object, this); - - spa_log_trace(impl->log, "ignore path=%s", o->this.path); - - if (o->updating) - object_remove(o); - else - object_destroy(o); -} - -struct spa_list *spa_dbus_monitor_object_list(struct spa_dbus_monitor *this, const char *interface) -{ - struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); - const struct spa_dbus_interface *iface; - struct spa_list *object_list; - - iface = interface_find(impl, interface, &object_list); - if (!iface) - return NULL; - - return object_list; -} - -static void async_call_reply(DBusPendingCall *pending, void *user_data) -{ - struct spa_dbus_async_call *call = user_data; - DBusMessage *r; - - spa_assert(pending == call->pending); - call->pending = NULL; - - r = dbus_pending_call_steal_reply(pending); - dbus_pending_call_unref(pending); - - spa_assert(call->reply != NULL); - - if (r == NULL) - return; - - call->reply(call, r); - dbus_message_unref(r); -} - -int spa_dbus_async_call_send(struct spa_dbus_async_call *call, - DBusConnection *conn, DBusMessage *msg, - void (*reply)(struct spa_dbus_async_call *call, DBusMessage *reply)) -{ - int res = -EIO; - DBusPendingCall *pending; - - spa_assert(msg); - spa_assert(conn); - spa_assert(call); - - if (call->pending) { - res = -EBUSY; - goto done; - } - - if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) - goto done; - - if (!dbus_pending_call_set_notify(pending, async_call_reply, call, NULL)) { - dbus_pending_call_cancel(pending); - dbus_pending_call_unref(pending); - goto done; - } - - call->reply = reply; - call->pending = pending; - res = 0; - -done: - dbus_message_unref(msg); - return res; -} diff --git a/spa/plugins/bluez5/dbus-monitor.h b/spa/plugins/bluez5/dbus-monitor.h deleted file mode 100644 index 04281bd57..000000000 --- a/spa/plugins/bluez5/dbus-monitor.h +++ /dev/null @@ -1,173 +0,0 @@ -/* Spa dbus - * - * Copyright © 2022 Pauli Virtanen - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef SPA_DBUS_MONITOR_H_ -#define SPA_DBUS_MONITOR_H_ - -#include - -#include -#include - -struct spa_dbus_object; - -/** - * DBus interface specification. - */ -struct spa_dbus_interface { - /** DBus interface name. */ - const char *name; - - /** Size of object struct. Must be at least sizeof(struct spa_dbus_object). */ - const size_t object_size; - - /** Property value updated. */ - void (*property) (struct spa_dbus_object *object, const char *key, DBusMessageIter *value); - - /** Interface at the object path added, or property updates complete. */ - void (*update) (struct spa_dbus_object *object); - - /** - * Interface at the object path removed. - * No other hooks will be called after this. - * The object will be deallocated after this, so any associated data, - * for example in a custom object struct, can be freed in this hook. - */ - void (*remove) (struct spa_dbus_object *object); -}; - -/** - * DBus object instance, for one interface. - * - * A custom object struct can be also used for each interface, specified - * as - * - * struct my_dbus_object { - * struct spa_dbus_object object; - * int some_extra_value; - * } - * - * The struct will be zero-initialized when first allocated. The object - * instances are owned by spa_dbus_monitor and allocated and freed by it. - */ -struct spa_dbus_object -{ - struct spa_list link; /**< Link in interface's object list */ - const char *path; /**< DBus object path */ - const struct spa_dbus_interface *interface; /**< The interface of the object */ - void *user_data; /**< Pointer passed in spa_dbus_monitor_new */ -}; - -/** DBus object monitor */ -struct spa_dbus_monitor -{ - const char *service; /**< Service name */ - const char *path; /**< Object path */ - const struct spa_dbus_interface *interfaces; /**< Monitored interfaces - * (zero-terminated) */ - void *user_data; /**< Pointer passed in spa_dbus_monitor_new */ -}; - -/** - * Non-DBus interface type representing the monitored service itself. - * Can be used to track NameOwner events. - */ -#define SPA_DBUS_MONITOR_NAME_OWNER_INTERFACE "org.freedesktop.pipewire.spa.dbus.monitor.owner" - -/** - * Create new object monitor. - * - * \param conn DBus connection - * \param service DBus service name to monitor - * \param path Object path to monitor - * \param interfaces Zero-terminated array of interfaces to monitor. Corresponding objects - * will be created. - * \param log Log to output to - * \param user_data User data to set in object instances. - */ -struct spa_dbus_monitor *spa_dbus_monitor_new(DBusConnection *conn, - const char *service, - const char *path, - const struct spa_dbus_interface *interfaces, - struct spa_log *log, - void *user_data); - -/** - * Destroy object monitor and all interface objects owned by it. - */ -void spa_dbus_monitor_destroy(struct spa_dbus_monitor *monitor); - -/** Find interface object by name */ -struct spa_dbus_object *spa_dbus_monitor_find(struct spa_dbus_monitor *monitor, - const char *path, const char *interface); - -/** Destroy an object, and don't receive further updates concerning it. */ -void spa_dbus_monitor_ignore_object(struct spa_dbus_monitor *monitor, - struct spa_dbus_object *object); - -/** - * Get the object list for the named interface. - * - * The returned list is not mutable. - * - * \param monitor The DBus monitor. - * \param interface Interface name. Must be one of those monitored. - */ -struct spa_list *spa_dbus_monitor_object_list(struct spa_dbus_monitor *monitor, - const char *interface); - - -struct spa_dbus_async_call -{ - DBusPendingCall *pending; - void (*reply)(struct spa_dbus_async_call *call, DBusMessage *reply); -}; - -/** - * Make an asynchronous DBus call. - * - * On successful return, call contains the pending call handle. - * The same address is passed to the reply function, and can be used - * to locate the address container object. The reply function shall not - * unref the reply. - * - * The msg passed in is stolen, and unref'd also on errors. - * - * \returns 0 on success, < 0 on error. - */ -int spa_dbus_async_call_send(struct spa_dbus_async_call *call, - DBusConnection *conn, DBusMessage *msg, - void (*reply)(struct spa_dbus_async_call *call, DBusMessage *reply)); - -/** Convenience for pending call cancel + unref */ -static inline void spa_dbus_async_call_cancel(struct spa_dbus_async_call *call) -{ - if (call->pending) { - dbus_pending_call_cancel(call->pending); - dbus_pending_call_unref(call->pending); - spa_zero(*call); - } -} - -#endif