From c8d819a5adbe32e14d7f03a252bca6f7df01d795 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 16 Jun 2009 19:03:22 +0300 Subject: dbus-protocol: Connection handling for local connections. --- src/pulsecore/core.h | 9 ++++++ src/pulsecore/dbus-common.c | 73 +++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/dbus-common.h | 39 ++++++++++++++++++++++++ src/pulsecore/dbus-util.c | 21 +++++++++++++ src/pulsecore/dbus-util.h | 1 + 5 files changed, 143 insertions(+) create mode 100644 src/pulsecore/dbus-common.c create mode 100644 src/pulsecore/dbus-common.h (limited to 'src/pulsecore') diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index c6794445..f93652e2 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -42,6 +42,13 @@ typedef struct pa_core pa_core; #include #include +typedef enum pa_server_type { + PA_SERVER_TYPE_UNSET, + PA_SERVER_TYPE_USER, + PA_SERVER_TYPE_SYSTEM, + PA_SERVER_TYPE_NONE +} pa_server_type_t; + typedef enum pa_core_state { PA_CORE_STARTUP, PA_CORE_RUNNING, @@ -152,6 +159,8 @@ struct pa_core { pa_resample_method_t resample_method; int realtime_priority; + pa_server_type_t server_type; + /* hooks */ pa_hook hooks[PA_CORE_HOOK_MAX]; }; diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c new file mode 100644 index 00000000..05931e0a --- /dev/null +++ b/src/pulsecore/dbus-common.c @@ -0,0 +1,73 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio 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. + + PulseAudio 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "dbus-common.h" + +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { + char *address = NULL; + char *runtime_path = NULL; + char *escaped_path = NULL; + + switch (server_type) { + case PA_SERVER_TYPE_USER: + if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) { + pa_log("pa_runtime_path() failed."); + break; + } + + if (!(escaped_path = dbus_address_escape_value(runtime_path))) { + pa_log("dbus_address_escape_value() failed."); + break; + } + + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_SYSTEM: + if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) { + pa_log("dbus_address_escape_value() failed."); + break; + } + + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_NONE: + address = pa_xnew0(char, 1); + break; + + default: + pa_assert_not_reached(); + } + + pa_xfree(runtime_path); + pa_xfree(escaped_path); + + return address; +} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h new file mode 100644 index 00000000..26bd05d4 --- /dev/null +++ b/src/pulsecore/dbus-common.h @@ -0,0 +1,39 @@ +#ifndef foodbuscommonhfoo +#define foodbuscommonhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio 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. + + PulseAudio 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#define PA_DBUS_DEFAULT_PORT 24883 +#define PA_DBUS_SOCKET_NAME "dbus_socket" + +#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME + +/* Returns the default address of the server type in the escaped form. For + * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the + * string. This function may fail in some rare cases, in which case NULL is + * returned. */ +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); + +#endif diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index ece36def..d8bd0e0a 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -276,6 +276,27 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus return pconn; } +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) { + pa_dbus_wrap_connection *pconn; + + pa_assert(m); + pa_assert(conn); + + pconn = pa_xnew(pa_dbus_wrap_connection, 1); + pconn->mainloop = m; + pconn->connection = dbus_connection_ref(conn); + + dbus_connection_set_exit_on_disconnect(conn, FALSE); + dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); + dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL); + dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL); + dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL); + + pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn); + + return pconn; +} + void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) { pa_assert(c); diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 55cda7a0..cd08485d 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -31,6 +31,7 @@ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, DBusConnection *conn); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); -- cgit From 123c6a3c6ffc9903c0855e38445fc3b6588311ce Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 19 Jun 2009 10:28:08 +0300 Subject: dbus-common: Implement infrastructure for registering D-Bus objects on all client connections and for receiving method calls from clients. --- src/pulsecore/dbus-common.c | 430 +++++++++++++++++++++++++++++++++++++++++ src/pulsecore/dbus-common.h | 34 ++++ src/pulsecore/dbus-objs/core.c | 120 ++++++++++++ src/pulsecore/dbus-objs/core.h | 39 ++++ 4 files changed, 623 insertions(+) create mode 100644 src/pulsecore/dbus-objs/core.c create mode 100644 src/pulsecore/dbus-objs/core.h (limited to 'src/pulsecore') diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c index 05931e0a..350add82 100644 --- a/src/pulsecore/dbus-common.c +++ b/src/pulsecore/dbus-common.c @@ -25,10 +25,35 @@ #include +#include + #include +#include +#include +#include #include "dbus-common.h" +struct dbus_state { + pa_core *core; + pa_hashmap *objects; /* Object path -> struct object_entry */ + pa_idxset *connections; /* DBusConnections */ +}; + +struct object_entry { + char *path; + pa_hashmap *interfaces; /* Interface name -> struct interface_entry */ + char *introspection; +}; + +struct interface_entry { + char *name; + char **methods; + char *introspection_snippet; + DBusObjectPathMessageFunction receive; + void *userdata; +}; + char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { char *address = NULL; char *runtime_path = NULL; @@ -71,3 +96,408 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { return address; } + +static void update_introspection(struct object_entry *oe) { + pa_strbuf *buf; + void *state = NULL; + struct interface_entry *iface_entry = NULL; + + pa_assert(oe); + + buf = pa_strbuf_new(); + pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + pa_strbuf_puts(buf, ""); + + while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) + pa_strbuf_puts(buf, iface_entry->introspection_snippet); + + pa_strbuf_puts(buf, " " + " " + " " + " " + " "); + + pa_strbuf_puts(buf, ""); + + pa_xfree(oe->introspection); + oe->introspection = pa_strbuf_tostring_free(buf); +} + +static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) { + const char *interface; + struct interface_entry *iface_entry; + void *state = NULL; + + pa_assert(obj_entry); + pa_assert(msg); + + if ((interface = dbus_message_get_interface(msg))) + return pa_hashmap_get(obj_entry->interfaces, interface); + + /* NULL interface, we'll have to search for an interface that contains the + * method. */ + + while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *method; + char **pos = iface_entry->methods; + + while ((method = *pos++)) { + if (!strcmp(dbus_message_get_member(msg), method)) + return iface_entry; + } + } + + return NULL; +} + +static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + struct dbus_state *dbus_state = user_data; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + DBusMessage *reply = NULL; + + pa_assert(connection); + pa_assert(message); + pa_assert(dbus_state); + pa_assert(dbus_state->objects); + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + pa_log_debug("%s.%s handled.", obj_entry->path, "Introspect"); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (!(iface_entry = find_interface(obj_entry, message))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + return iface_entry->receive(connection, message, iface_entry->userdata); + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusObjectPathVTable vtable = { + .unregister_function = NULL, + .message_function = handle_message_cb, + .dbus_internal_pad1 = NULL, + .dbus_internal_pad2 = NULL, + .dbus_internal_pad3 = NULL, + .dbus_internal_pad4 = NULL +}; + +static void register_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { + DBusConnection *conn; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(obj_entry); + + if (!dbus_state->connections) + return; + + while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { + if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) + pa_log_debug("dbus_connection_register_object_path() failed."); + } +} + +static char **copy_methods(const char * const *methods) { + unsigned n = 0; + char **copy; + unsigned i; + + while (methods[n++]) + ; + + copy = pa_xnew0(char *, n); + + for (i = 0; i < n - 1; ++i) + copy[i] = pa_xstrdup(methods[i]); + + return copy; +} + +int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) { + struct dbus_state *dbus_state; + pa_hashmap *objects; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + pa_bool_t state_created = FALSE; + pa_bool_t object_map_created = FALSE; + pa_bool_t obj_entry_created = FALSE; + + pa_assert(c); + pa_assert(path); + pa_assert(introspection_snippet); + pa_assert(receive_cb); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { + dbus_state = pa_xnew0(struct dbus_state, 1); + dbus_state->core = c; + pa_hashmap_put(c->shared, "dbus-state", dbus_state); + state_created = TRUE; + } + + if (!(objects = dbus_state->objects)) { + objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + dbus_state->objects = objects; + object_map_created = TRUE; + } + + if (!(obj_entry = pa_hashmap_get(objects, path))) { + obj_entry = pa_xnew(struct object_entry, 1); + obj_entry->path = pa_xstrdup(path); + obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + obj_entry->introspection = NULL; + pa_hashmap_put(objects, path, obj_entry); + obj_entry_created = TRUE; + } + + if (pa_hashmap_get(obj_entry->interfaces, interface) != NULL) + goto fail; /* The interface was already registered. */ + + iface_entry = pa_xnew(struct interface_entry, 1); + iface_entry->name = pa_xstrdup(interface); + iface_entry->methods = copy_methods(methods); + iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); + iface_entry->receive = receive_cb; + iface_entry->userdata = userdata; + pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry); + + update_introspection(obj_entry); + + if (obj_entry_created) + register_object(dbus_state, obj_entry); + + return 0; + +fail: + if (obj_entry_created) { + pa_hashmap_remove(objects, path); + pa_xfree(obj_entry); + } + + if (object_map_created) { + dbus_state->objects = NULL; + pa_hashmap_free(objects, NULL, NULL); + } + + if (state_created) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return -1; +} + +static void unregister_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { + DBusConnection *conn; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(obj_entry); + + if (!dbus_state->connections) + return; + + while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { + if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) + pa_log_debug("dbus_connection_unregister_object_path() failed."); + } +} + +static void free_methods(char **methods) { + char **pos = methods; + + while (*pos++) + pa_xfree(*pos); + + pa_xfree(methods); +} + +int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { + struct dbus_state *dbus_state; + pa_hashmap *objects; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + + pa_assert(c); + pa_assert(path); + pa_assert(interface); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) + return -1; + + if (!(objects = dbus_state->objects)) + return -1; + + if (!(obj_entry = pa_hashmap_get(objects, path))) + return -1; + + if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface))) + return -1; + + update_introspection(obj_entry); + + pa_xfree(iface_entry->name); + free_methods(iface_entry->methods); + pa_xfree(iface_entry->introspection_snippet); + pa_xfree(iface_entry); + + if (pa_hashmap_isempty(obj_entry->interfaces)) { + unregister_object(dbus_state, obj_entry); + + pa_hashmap_remove(objects, path); + pa_xfree(obj_entry->path); + pa_hashmap_free(obj_entry->interfaces, NULL, NULL); + pa_xfree(obj_entry->introspection); + pa_xfree(obj_entry); + } + + if (pa_hashmap_isempty(objects)) { + dbus_state->objects = NULL; + pa_hashmap_free(objects, NULL, NULL); + } + + if (!dbus_state->objects && !dbus_state->connections) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return 0; +} + +static void register_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(conn); + + if (!dbus_state->objects) + return; + + while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { + if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) + pa_log_debug("dbus_connection_register_object_path() failed."); + } +} + +int pa_dbus_register_connection(pa_core *c, DBusConnection *conn) { + struct dbus_state *dbus_state; + pa_idxset *connections; + pa_bool_t state_created = FALSE; + pa_bool_t connection_set_created = FALSE; + + pa_assert(c); + pa_assert(conn); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { + dbus_state = pa_xnew0(struct dbus_state, 1); + dbus_state->core = c; + pa_hashmap_put(c->shared, "dbus-state", dbus_state); + state_created = TRUE; + } + + if (!(connections = dbus_state->connections)) { + connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + dbus_state->connections = connections; + connection_set_created = TRUE; + } + + if (pa_idxset_get_by_data(connections, conn, NULL)) + goto fail; /* The connection was already registered. */ + + register_all_objects(dbus_state, conn); + + pa_idxset_put(connections, dbus_connection_ref(conn), NULL); + + return 0; + +fail: + if (connection_set_created) { + dbus_state->connections = NULL; + pa_idxset_free(connections, NULL, NULL); + } + + if (state_created) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return -1; +} + +static void unregister_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(conn); + + if (!dbus_state->objects) + return; + + while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { + if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) + pa_log_debug("dus_connection_unregister_object_path() failed."); + } +} + +int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn) { + struct dbus_state *dbus_state; + pa_idxset *connections; + + pa_assert(c); + pa_assert(conn); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) + return -1; + + if (!(connections = dbus_state->connections)) + return -1; + + if (!pa_idxset_remove_by_data(connections, conn, NULL)) + return -1; + + unregister_all_objects(dbus_state, conn); + + dbus_connection_unref(conn); + + if (pa_idxset_isempty(connections)) { + dbus_state->connections = NULL; + pa_idxset_free(connections, NULL, NULL); + } + + if (!dbus_state->objects && !dbus_state->connections) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return 0; +} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h index 26bd05d4..23c7c221 100644 --- a/src/pulsecore/dbus-common.h +++ b/src/pulsecore/dbus-common.h @@ -22,6 +22,8 @@ USA. ***/ +#include + #include #include @@ -30,10 +32,42 @@ #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME +/* NOTE: These functions may only be called from the main thread. */ + /* Returns the default address of the server type in the escaped form. For * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the * string. This function may fail in some rare cases, in which case NULL is * returned. */ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); +/* Registers the given interface to the given object path. This is additive: it + * doesn't matter whether or not the object has already been registered; if it + * is, then its interface set is just extended. + * + * Introspection requests are handled automatically. For that to work, the + * caller gives an XML snippet containing the interface introspection element. + * All interface snippets are automatically combined to provide the final + * introspection string. + * + * The introspection snippet contains the interface name and the methods, but + * since this function doesn't do XML parsing, the interface name and the set + * of method names have to be supplied separately. If the interface doesn't + * contain any methods, NULL may be given as the methods parameter, otherwise + * the methods parameter must be a NULL-terminated array of strings. + * + * Fails and returns a negative number if the object already has the interface + * registered. */ +int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata); + +/* Returns a negative number if the given object doesn't have the given + * interface registered. */ +int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface); + +/* Fails and returns a negative number if the connection is already + * registered. */ +int pa_dbus_register_connection(pa_core *c, DBusConnection *conn); + +/* Returns a negative number if the connection wasn't registered. */ +int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn); + #endif diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c new file mode 100644 index 00000000..f59c478a --- /dev/null +++ b/src/pulsecore/dbus-objs/core.c @@ -0,0 +1,120 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio 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. + + PulseAudio 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include + +#include "core.h" + +#define OBJECT_NAME "/org/pulseaudio/core" +#define INTERFACE_NAME "org.pulseaudio.Core" + +struct pa_dbusobj_core { + pa_core *core; +}; + +static const char *introspection_snippet = + " " + " " + " " + " " + " "; + +static const char *methods[] = { + "Test", + NULL +}; + +static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusMessage *reply = NULL; + const char *reply_message = "Hello!"; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!(reply = dbus_message_new_method_return(msg))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + pa_dbusobj_core *c = user_data; + + pa_assert(connection); + pa_assert(message); + pa_assert(c); + + if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test")) + return handle_test(connection, message, c); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { + pa_dbusobj_core *c; + + pa_assert(core); + + c = pa_xnew(pa_dbusobj_core, 1); + c->core = core; + + pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c); + + return c; +} + +void pa_dbusobj_core_free(pa_dbusobj_core *c) { + pa_assert(c); + + pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME); + + pa_xfree(c); +} diff --git a/src/pulsecore/dbus-objs/core.h b/src/pulsecore/dbus-objs/core.h new file mode 100644 index 00000000..8e59cc3d --- /dev/null +++ b/src/pulsecore/dbus-objs/core.h @@ -0,0 +1,39 @@ +#ifndef foodbusobjscorehfoo +#define foodbusobjscorehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio 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. + + PulseAudio 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus object at path /org/pulseaudio/core. + * The implemented interface is org.pulseaudio.Core. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Core interface + * documentation. + */ + +#include + +typedef struct pa_dbusobj_core pa_dbusobj_core; + +pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core); +void pa_dbusobj_core_free(pa_dbusobj_core *c); + +#endif -- cgit From 5c7952e4fa5e560f64255ef173c3e6570bee433a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 3 Jul 2009 02:49:07 +0300 Subject: dbus: Implement the Name property of the core object. --- src/pulsecore/dbus-common.c | 215 +++++++++++++++++---- src/pulsecore/dbus-common.h | 28 ++- src/pulsecore/dbus-objs/core.c | 414 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 587 insertions(+), 70 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c index 350add82..6562cda2 100644 --- a/src/pulsecore/dbus-common.c +++ b/src/pulsecore/dbus-common.c @@ -48,6 +48,7 @@ struct object_entry { struct interface_entry { char *name; + char **properties; char **methods; char *introspection_snippet; DBusObjectPathMessageFunction receive; @@ -106,48 +107,152 @@ static void update_introspection(struct object_entry *oe) { buf = pa_strbuf_new(); pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); - pa_strbuf_puts(buf, ""); + pa_strbuf_puts(buf, "\n"); while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) pa_strbuf_puts(buf, iface_entry->introspection_snippet); - pa_strbuf_puts(buf, " " - " " - " " - " " - " "); - - pa_strbuf_puts(buf, ""); + pa_strbuf_puts(buf, " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); + + pa_strbuf_puts(buf, "\n"); pa_xfree(oe->introspection); oe->introspection = pa_strbuf_tostring_free(buf); } -static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) { - const char *interface; - struct interface_entry *iface_entry; +enum find_result_t { + SUCCESS, + NO_SUCH_PROPERTY, + NO_SUCH_METHOD, + INVALID_MESSAGE_ARGUMENTS +}; + +static enum find_result_t find_interface_by_property(struct object_entry *obj_entry, const char *property, struct interface_entry **entry) { void *state = NULL; pa_assert(obj_entry); - pa_assert(msg); + pa_assert(property); + pa_assert(entry); + + while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *iface_property; + char **pos = (*entry)->properties; + + while ((iface_property = *pos++)) { + if (pa_streq(iface_property, property)) + return SUCCESS; + } + } + + return NO_SUCH_PROPERTY; +} - if ((interface = dbus_message_get_interface(msg))) - return pa_hashmap_get(obj_entry->interfaces, interface); +static enum find_result_t find_interface_by_method(struct object_entry *obj_entry, const char *method, struct interface_entry **entry) { + void *state = NULL; - /* NULL interface, we'll have to search for an interface that contains the - * method. */ + pa_assert(obj_entry); + pa_assert(method); + pa_assert(entry); - while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { - char *method; - char **pos = iface_entry->methods; + while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *iface_method; + char **pos = (*entry)->methods; - while ((method = *pos++)) { - if (!strcmp(dbus_message_get_member(msg), method)) - return iface_entry; + while ((iface_method = *pos++)) { + if (pa_streq(iface_method, method)) + return SUCCESS; } } - return NULL; + return NO_SUCH_METHOD; +} + +static enum find_result_t find_interface_from_properties_call(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { + const char *interface; + const char *property; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(entry); + + if (dbus_message_has_member(msg, "GetAll")) { + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + } else { + pa_assert_se((*entry = pa_hashmap_first(obj_entry->interfaces))); + return SUCCESS; + } + } else { + pa_assert(dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set")); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + } else + return find_interface_by_property(obj_entry, property, entry); + } +} + +static enum find_result_t find_interface(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { + const char *interface; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(entry); + + *entry = NULL; + + if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) + return find_interface_from_properties_call(obj_entry, msg, entry); + + else if ((interface = dbus_message_get_interface(msg))) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + + } else { /* The method call doesn't contain an interface. */ + if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { + if (find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry) == SUCCESS) + return SUCCESS; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + else + /* Assume this is a .Properties call. */ + return find_interface_from_properties_call(obj_entry, msg, entry); + + } else /* This is not a .Properties call. */ + return find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry); + } } static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -166,7 +271,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); - if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { if (!(reply = dbus_message_new_method_return(message))) goto oom; @@ -183,10 +289,38 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa return DBUS_HANDLER_RESULT_HANDLED; } - if (!(iface_entry = find_interface(obj_entry, message))) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + switch (find_interface(obj_entry, message, &iface_entry)) { + case SUCCESS: + return iface_entry->receive(connection, message, iface_entry->userdata); + + case NO_SUCH_PROPERTY: + if (!(reply = dbus_message_new_error(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property"))) + goto fail; - return iface_entry->receive(connection, message, iface_entry->userdata); + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + + case NO_SUCH_METHOD: + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + case INVALID_MESSAGE_ARGUMENTS: + if (!(reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) + goto fail; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + + default: + pa_assert_not_reached(); + } fail: if (reply) @@ -226,23 +360,30 @@ static void register_object(struct dbus_state *dbus_state, struct object_entry * } } -static char **copy_methods(const char * const *methods) { +static char **copy_strarray(const char * const *array) { unsigned n = 0; char **copy; unsigned i; - while (methods[n++]) + while (array[n++]) ; copy = pa_xnew0(char *, n); for (i = 0; i < n - 1; ++i) - copy[i] = pa_xstrdup(methods[i]); + copy[i] = pa_xstrdup(array[i]); return copy; } -int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) { +int pa_dbus_add_interface(pa_core *c, + const char* path, + const char* interface, + const char * const *properties, + const char * const *methods, + const char* introspection_snippet, + DBusObjectPathMessageFunction receive_cb, + void *userdata) { struct dbus_state *dbus_state; pa_hashmap *objects; struct object_entry *obj_entry; @@ -283,7 +424,8 @@ int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, c iface_entry = pa_xnew(struct interface_entry, 1); iface_entry->name = pa_xstrdup(interface); - iface_entry->methods = copy_methods(methods); + iface_entry->properties = copy_strarray(properties); + iface_entry->methods = copy_strarray(methods); iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); iface_entry->receive = receive_cb; iface_entry->userdata = userdata; @@ -331,13 +473,13 @@ static void unregister_object(struct dbus_state *dbus_state, struct object_entry } } -static void free_methods(char **methods) { - char **pos = methods; +static void free_strarray(char **array) { + char **pos = array; while (*pos++) pa_xfree(*pos); - pa_xfree(methods); + pa_xfree(array); } int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { @@ -365,7 +507,8 @@ int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface update_introspection(obj_entry); pa_xfree(iface_entry->name); - free_methods(iface_entry->methods); + free_strarray(iface_entry->properties); + free_strarray(iface_entry->methods); pa_xfree(iface_entry->introspection_snippet); pa_xfree(iface_entry); diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h index 23c7c221..4354c4ea 100644 --- a/src/pulsecore/dbus-common.h +++ b/src/pulsecore/dbus-common.h @@ -28,10 +28,12 @@ #include #define PA_DBUS_DEFAULT_PORT 24883 -#define PA_DBUS_SOCKET_NAME "dbus_socket" +#define PA_DBUS_SOCKET_NAME "dbus-socket" #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" + /* NOTE: These functions may only be called from the main thread. */ /* Returns the default address of the server type in the escaped form. For @@ -47,17 +49,27 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); * Introspection requests are handled automatically. For that to work, the * caller gives an XML snippet containing the interface introspection element. * All interface snippets are automatically combined to provide the final - * introspection string. + * introspection string for the object. * - * The introspection snippet contains the interface name and the methods, but - * since this function doesn't do XML parsing, the interface name and the set - * of method names have to be supplied separately. If the interface doesn't - * contain any methods, NULL may be given as the methods parameter, otherwise - * the methods parameter must be a NULL-terminated array of strings. + * The introspection snippet contains the interface name, the property names + * and the method namess, but since this function doesn't do XML parsing, the + * information needs to be given separately. Property and method names are + * given as a NULL-terminated array of strings. The interface name is used for + * message routing, and so are the property and method names too in case the + * client doesn't tell which interface he's trying to access; in absence of + * interface information from the client, the correct interface is searched + * based on the property or method name. * * Fails and returns a negative number if the object already has the interface * registered. */ -int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata); +int pa_dbus_add_interface(pa_core *c, + const char* path, + const char* interface, + const char * const *properties, + const char * const *methods, + const char* introspection_snippet, + DBusObjectPathMessageFunction receive_cb, + void *userdata); /* Returns a negative number if the given object doesn't have the given * interface registered. */ diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c index f59c478a..18bbf789 100644 --- a/src/pulsecore/dbus-objs/core.c +++ b/src/pulsecore/dbus-objs/core.c @@ -27,62 +27,414 @@ #include +#include #include #include #include "core.h" -#define OBJECT_NAME "/org/pulseaudio/core" -#define INTERFACE_NAME "org.pulseaudio.Core" +#define OBJECT_PATH "/org/pulseaudio1" +#define INTERFACE_CORE "org.PulseAudio.Core1" struct pa_dbusobj_core { pa_core *core; }; static const char *introspection_snippet = - " " - " " - " " - " " - " "; + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"; + +/* If you need to modify this list, note that handle_get_all() uses hard-coded + * indexes to point to these strings, so make sure the indexes don't go wrong + * there. */ +static const char *properties[] = { + "InterfaceRevision", + "Name", + "Version", + "Username", + "Hostname", + "DefaultChannels", + "DefaultSampleFormat", + "DefaultSampleRate", + "Sinks", + "FallbackSink", + "Sources", + "FallbackSource", + "PlaybackStreams", + "RecordStreams", + "Samples", + "Modules", + "Clients", + "Extensions", + NULL +}; static const char *methods[] = { - "Test", + "GetCardByName", + "GetSinkByName", + "GetSourceByName", + "GetSampleByName", + "UploadSample", + "LoadSampleFromFile", + "AddLazySample", + "AddLazySamplesFromDirectory", + "LoadModule", + "Exit", NULL }; -static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { +static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; DBusMessage *reply = NULL; - const char *reply_message = "Hello!"; + const char *server_name = PACKAGE_NAME; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; pa_assert(conn); pa_assert(msg); pa_assert(c); - if (!(reply = dbus_message_new_method_return(msg))) - goto oom; + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: + if (reply) + dbus_message_unref(reply); + + return r; +} - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID)) - goto fail; +static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (*interface && !pa_streq(interface, INTERFACE_CORE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (pa_streq(property, "Name")) { + r = handle_get_name(conn, msg, c); + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: + if (reply) + dbus_message_unref(reply); + + return r; +} - dbus_message_unref(reply); +static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - return DBUS_HANDLER_RESULT_HANDLED; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); -fail: + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (*interface && !pa_streq(interface, INTERFACE_CORE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (pa_streq(property, "Name")) { + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return r; +} + +static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + DBusMessage *reply = NULL; + char *interface = NULL; + char const *server_name = PACKAGE_NAME; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter variant_iter; -oom: + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */ + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return r; } static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -92,8 +444,17 @@ static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *mes pa_assert(message); pa_assert(c); - if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test")) - return handle_test(connection, message, c); + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get"))) + return handle_get(connection, message, c); + + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set"))) + return handle_set(connection, message, c); + + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll"))) + return handle_get_all(connection, message, c); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -106,7 +467,8 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { c = pa_xnew(pa_dbusobj_core, 1); c->core = core; - pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c); + pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c); + return c; } @@ -114,7 +476,7 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { void pa_dbusobj_core_free(pa_dbusobj_core *c) { pa_assert(c); - pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME); + pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE); pa_xfree(c); } -- cgit From 9347e90fed732dac619bb88f6518c344e7436447 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 21 Jul 2009 00:02:27 +0300 Subject: Finish the Core dbus interface. --- src/pulsecore/core-util.c | 22 + src/pulsecore/core-util.h | 7 + src/pulsecore/dbus-common.c | 646 ---------------------------- src/pulsecore/dbus-common.h | 85 ---- src/pulsecore/dbus-objs/core.c | 482 --------------------- src/pulsecore/dbus-objs/core.h | 39 -- src/pulsecore/dbus-util.c | 333 +++++++++++++++ src/pulsecore/dbus-util.h | 29 ++ src/pulsecore/namereg.c | 63 +-- src/pulsecore/protocol-dbus.c | 930 +++++++++++++++++++++++++++++++++++++++++ src/pulsecore/protocol-dbus.h | 158 +++++++ 11 files changed, 1514 insertions(+), 1280 deletions(-) delete mode 100644 src/pulsecore/dbus-common.c delete mode 100644 src/pulsecore/dbus-common.h delete mode 100644 src/pulsecore/dbus-objs/core.c delete mode 100644 src/pulsecore/dbus-objs/core.h create mode 100644 src/pulsecore/protocol-dbus.c create mode 100644 src/pulsecore/protocol-dbus.h (limited to 'src/pulsecore') diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 04e7eb24..ebddf363 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2656,6 +2656,28 @@ char *pa_replace(const char*s, const char*a, const char *b) { return pa_strbuf_tostring_free(sb); } +char *pa_escape(const char *p, const char *chars) { + const char *s; + const char *c; + pa_strbuf *buf = pa_strbuf_new(); + + for (s = p; *s; ++s) { + if (*s == '\\') + pa_strbuf_putc(buf, '\\'); + else if (chars) { + for (c = chars; *c; ++c) { + if (*s == *c) { + pa_strbuf_putc(buf, '\\'); + break; + } + } + } + pa_strbuf_putc(buf, *s); + } + + return pa_strbuf_tostring_free(buf); +} + char *pa_unescape(char *p) { char *s, *d; pa_bool_t escaped = FALSE; diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 96a0480a..aaa51bd6 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -220,6 +220,13 @@ unsigned pa_ncpus(void); char *pa_replace(const char*s, const char*a, const char *b); +/* Escapes p by inserting backslashes in front of backslashes. chars is a + * regular (ie. NULL-terminated) string containing additional characters that + * should be escaped. chars can be NULL. The caller has to free the returned + * string. */ +char *pa_escape(const char *p, const char *chars); + +/* Does regular backslash unescaping. Returns the argument p. */ char *pa_unescape(char *p); char *pa_realpath(const char *path); diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c deleted file mode 100644 index 6562cda2..00000000 --- a/src/pulsecore/dbus-common.c +++ /dev/null @@ -1,646 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio 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. - - PulseAudio 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 - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#include -#include -#include -#include - -#include "dbus-common.h" - -struct dbus_state { - pa_core *core; - pa_hashmap *objects; /* Object path -> struct object_entry */ - pa_idxset *connections; /* DBusConnections */ -}; - -struct object_entry { - char *path; - pa_hashmap *interfaces; /* Interface name -> struct interface_entry */ - char *introspection; -}; - -struct interface_entry { - char *name; - char **properties; - char **methods; - char *introspection_snippet; - DBusObjectPathMessageFunction receive; - void *userdata; -}; - -char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { - char *address = NULL; - char *runtime_path = NULL; - char *escaped_path = NULL; - - switch (server_type) { - case PA_SERVER_TYPE_USER: - if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) { - pa_log("pa_runtime_path() failed."); - break; - } - - if (!(escaped_path = dbus_address_escape_value(runtime_path))) { - pa_log("dbus_address_escape_value() failed."); - break; - } - - address = pa_sprintf_malloc("unix:path=%s", escaped_path); - break; - - case PA_SERVER_TYPE_SYSTEM: - if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) { - pa_log("dbus_address_escape_value() failed."); - break; - } - - address = pa_sprintf_malloc("unix:path=%s", escaped_path); - break; - - case PA_SERVER_TYPE_NONE: - address = pa_xnew0(char, 1); - break; - - default: - pa_assert_not_reached(); - } - - pa_xfree(runtime_path); - pa_xfree(escaped_path); - - return address; -} - -static void update_introspection(struct object_entry *oe) { - pa_strbuf *buf; - void *state = NULL; - struct interface_entry *iface_entry = NULL; - - pa_assert(oe); - - buf = pa_strbuf_new(); - pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); - pa_strbuf_puts(buf, "\n"); - - while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) - pa_strbuf_puts(buf, iface_entry->introspection_snippet); - - pa_strbuf_puts(buf, " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n"); - - pa_strbuf_puts(buf, "\n"); - - pa_xfree(oe->introspection); - oe->introspection = pa_strbuf_tostring_free(buf); -} - -enum find_result_t { - SUCCESS, - NO_SUCH_PROPERTY, - NO_SUCH_METHOD, - INVALID_MESSAGE_ARGUMENTS -}; - -static enum find_result_t find_interface_by_property(struct object_entry *obj_entry, const char *property, struct interface_entry **entry) { - void *state = NULL; - - pa_assert(obj_entry); - pa_assert(property); - pa_assert(entry); - - while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { - char *iface_property; - char **pos = (*entry)->properties; - - while ((iface_property = *pos++)) { - if (pa_streq(iface_property, property)) - return SUCCESS; - } - } - - return NO_SUCH_PROPERTY; -} - -static enum find_result_t find_interface_by_method(struct object_entry *obj_entry, const char *method, struct interface_entry **entry) { - void *state = NULL; - - pa_assert(obj_entry); - pa_assert(method); - pa_assert(entry); - - while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { - char *iface_method; - char **pos = (*entry)->methods; - - while ((iface_method = *pos++)) { - if (pa_streq(iface_method, method)) - return SUCCESS; - } - } - - return NO_SUCH_METHOD; -} - -static enum find_result_t find_interface_from_properties_call(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { - const char *interface; - const char *property; - - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(entry); - - if (dbus_message_has_member(msg, "GetAll")) { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; - - if (*interface) { - if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) - return SUCCESS; - else - return NO_SUCH_METHOD; - } else { - pa_assert_se((*entry = pa_hashmap_first(obj_entry->interfaces))); - return SUCCESS; - } - } else { - pa_assert(dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set")); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; - - if (*interface) { - if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) - return SUCCESS; - else - return NO_SUCH_METHOD; - } else - return find_interface_by_property(obj_entry, property, entry); - } -} - -static enum find_result_t find_interface(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { - const char *interface; - - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(entry); - - *entry = NULL; - - if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) - return find_interface_from_properties_call(obj_entry, msg, entry); - - else if ((interface = dbus_message_get_interface(msg))) { - if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) - return SUCCESS; - else - return NO_SUCH_METHOD; - - } else { /* The method call doesn't contain an interface. */ - if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { - if (find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry) == SUCCESS) - return SUCCESS; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ - else - /* Assume this is a .Properties call. */ - return find_interface_from_properties_call(obj_entry, msg, entry); - - } else /* This is not a .Properties call. */ - return find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry); - } -} - -static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { - struct dbus_state *dbus_state = user_data; - struct object_entry *obj_entry; - struct interface_entry *iface_entry; - DBusMessage *reply = NULL; - - pa_assert(connection); - pa_assert(message); - pa_assert(dbus_state); - pa_assert(dbus_state->objects); - - if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); - - if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)) - goto fail; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - pa_log_debug("%s.%s handled.", obj_entry->path, "Introspect"); - - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - switch (find_interface(obj_entry, message, &iface_entry)) { - case SUCCESS: - return iface_entry->receive(connection, message, iface_entry->userdata); - - case NO_SUCH_PROPERTY: - if (!(reply = dbus_message_new_error(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property"))) - goto fail; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; - - case NO_SUCH_METHOD: - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - case INVALID_MESSAGE_ARGUMENTS: - if (!(reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) - goto fail; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; - - default: - pa_assert_not_reached(); - } - -fail: - if (reply) - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -static DBusObjectPathVTable vtable = { - .unregister_function = NULL, - .message_function = handle_message_cb, - .dbus_internal_pad1 = NULL, - .dbus_internal_pad2 = NULL, - .dbus_internal_pad3 = NULL, - .dbus_internal_pad4 = NULL -}; - -static void register_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { - DBusConnection *conn; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(obj_entry); - - if (!dbus_state->connections) - return; - - while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { - if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) - pa_log_debug("dbus_connection_register_object_path() failed."); - } -} - -static char **copy_strarray(const char * const *array) { - unsigned n = 0; - char **copy; - unsigned i; - - while (array[n++]) - ; - - copy = pa_xnew0(char *, n); - - for (i = 0; i < n - 1; ++i) - copy[i] = pa_xstrdup(array[i]); - - return copy; -} - -int pa_dbus_add_interface(pa_core *c, - const char* path, - const char* interface, - const char * const *properties, - const char * const *methods, - const char* introspection_snippet, - DBusObjectPathMessageFunction receive_cb, - void *userdata) { - struct dbus_state *dbus_state; - pa_hashmap *objects; - struct object_entry *obj_entry; - struct interface_entry *iface_entry; - pa_bool_t state_created = FALSE; - pa_bool_t object_map_created = FALSE; - pa_bool_t obj_entry_created = FALSE; - - pa_assert(c); - pa_assert(path); - pa_assert(introspection_snippet); - pa_assert(receive_cb); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { - dbus_state = pa_xnew0(struct dbus_state, 1); - dbus_state->core = c; - pa_hashmap_put(c->shared, "dbus-state", dbus_state); - state_created = TRUE; - } - - if (!(objects = dbus_state->objects)) { - objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - dbus_state->objects = objects; - object_map_created = TRUE; - } - - if (!(obj_entry = pa_hashmap_get(objects, path))) { - obj_entry = pa_xnew(struct object_entry, 1); - obj_entry->path = pa_xstrdup(path); - obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - obj_entry->introspection = NULL; - pa_hashmap_put(objects, path, obj_entry); - obj_entry_created = TRUE; - } - - if (pa_hashmap_get(obj_entry->interfaces, interface) != NULL) - goto fail; /* The interface was already registered. */ - - iface_entry = pa_xnew(struct interface_entry, 1); - iface_entry->name = pa_xstrdup(interface); - iface_entry->properties = copy_strarray(properties); - iface_entry->methods = copy_strarray(methods); - iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); - iface_entry->receive = receive_cb; - iface_entry->userdata = userdata; - pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry); - - update_introspection(obj_entry); - - if (obj_entry_created) - register_object(dbus_state, obj_entry); - - return 0; - -fail: - if (obj_entry_created) { - pa_hashmap_remove(objects, path); - pa_xfree(obj_entry); - } - - if (object_map_created) { - dbus_state->objects = NULL; - pa_hashmap_free(objects, NULL, NULL); - } - - if (state_created) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return -1; -} - -static void unregister_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { - DBusConnection *conn; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(obj_entry); - - if (!dbus_state->connections) - return; - - while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { - if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) - pa_log_debug("dbus_connection_unregister_object_path() failed."); - } -} - -static void free_strarray(char **array) { - char **pos = array; - - while (*pos++) - pa_xfree(*pos); - - pa_xfree(array); -} - -int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { - struct dbus_state *dbus_state; - pa_hashmap *objects; - struct object_entry *obj_entry; - struct interface_entry *iface_entry; - - pa_assert(c); - pa_assert(path); - pa_assert(interface); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) - return -1; - - if (!(objects = dbus_state->objects)) - return -1; - - if (!(obj_entry = pa_hashmap_get(objects, path))) - return -1; - - if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface))) - return -1; - - update_introspection(obj_entry); - - pa_xfree(iface_entry->name); - free_strarray(iface_entry->properties); - free_strarray(iface_entry->methods); - pa_xfree(iface_entry->introspection_snippet); - pa_xfree(iface_entry); - - if (pa_hashmap_isempty(obj_entry->interfaces)) { - unregister_object(dbus_state, obj_entry); - - pa_hashmap_remove(objects, path); - pa_xfree(obj_entry->path); - pa_hashmap_free(obj_entry->interfaces, NULL, NULL); - pa_xfree(obj_entry->introspection); - pa_xfree(obj_entry); - } - - if (pa_hashmap_isempty(objects)) { - dbus_state->objects = NULL; - pa_hashmap_free(objects, NULL, NULL); - } - - if (!dbus_state->objects && !dbus_state->connections) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return 0; -} - -static void register_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { - struct object_entry *obj_entry; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(conn); - - if (!dbus_state->objects) - return; - - while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { - if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) - pa_log_debug("dbus_connection_register_object_path() failed."); - } -} - -int pa_dbus_register_connection(pa_core *c, DBusConnection *conn) { - struct dbus_state *dbus_state; - pa_idxset *connections; - pa_bool_t state_created = FALSE; - pa_bool_t connection_set_created = FALSE; - - pa_assert(c); - pa_assert(conn); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { - dbus_state = pa_xnew0(struct dbus_state, 1); - dbus_state->core = c; - pa_hashmap_put(c->shared, "dbus-state", dbus_state); - state_created = TRUE; - } - - if (!(connections = dbus_state->connections)) { - connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - dbus_state->connections = connections; - connection_set_created = TRUE; - } - - if (pa_idxset_get_by_data(connections, conn, NULL)) - goto fail; /* The connection was already registered. */ - - register_all_objects(dbus_state, conn); - - pa_idxset_put(connections, dbus_connection_ref(conn), NULL); - - return 0; - -fail: - if (connection_set_created) { - dbus_state->connections = NULL; - pa_idxset_free(connections, NULL, NULL); - } - - if (state_created) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return -1; -} - -static void unregister_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { - struct object_entry *obj_entry; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(conn); - - if (!dbus_state->objects) - return; - - while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { - if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) - pa_log_debug("dus_connection_unregister_object_path() failed."); - } -} - -int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn) { - struct dbus_state *dbus_state; - pa_idxset *connections; - - pa_assert(c); - pa_assert(conn); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) - return -1; - - if (!(connections = dbus_state->connections)) - return -1; - - if (!pa_idxset_remove_by_data(connections, conn, NULL)) - return -1; - - unregister_all_objects(dbus_state, conn); - - dbus_connection_unref(conn); - - if (pa_idxset_isempty(connections)) { - dbus_state->connections = NULL; - pa_idxset_free(connections, NULL, NULL); - } - - if (!dbus_state->objects && !dbus_state->connections) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return 0; -} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h deleted file mode 100644 index 4354c4ea..00000000 --- a/src/pulsecore/dbus-common.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef foodbuscommonhfoo -#define foodbuscommonhfoo - -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio 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. - - PulseAudio 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 - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include -#include - -#define PA_DBUS_DEFAULT_PORT 24883 -#define PA_DBUS_SOCKET_NAME "dbus-socket" - -#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME - -#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" - -/* NOTE: These functions may only be called from the main thread. */ - -/* Returns the default address of the server type in the escaped form. For - * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the - * string. This function may fail in some rare cases, in which case NULL is - * returned. */ -char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); - -/* Registers the given interface to the given object path. This is additive: it - * doesn't matter whether or not the object has already been registered; if it - * is, then its interface set is just extended. - * - * Introspection requests are handled automatically. For that to work, the - * caller gives an XML snippet containing the interface introspection element. - * All interface snippets are automatically combined to provide the final - * introspection string for the object. - * - * The introspection snippet contains the interface name, the property names - * and the method namess, but since this function doesn't do XML parsing, the - * information needs to be given separately. Property and method names are - * given as a NULL-terminated array of strings. The interface name is used for - * message routing, and so are the property and method names too in case the - * client doesn't tell which interface he's trying to access; in absence of - * interface information from the client, the correct interface is searched - * based on the property or method name. - * - * Fails and returns a negative number if the object already has the interface - * registered. */ -int pa_dbus_add_interface(pa_core *c, - const char* path, - const char* interface, - const char * const *properties, - const char * const *methods, - const char* introspection_snippet, - DBusObjectPathMessageFunction receive_cb, - void *userdata); - -/* Returns a negative number if the given object doesn't have the given - * interface registered. */ -int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface); - -/* Fails and returns a negative number if the connection is already - * registered. */ -int pa_dbus_register_connection(pa_core *c, DBusConnection *conn); - -/* Returns a negative number if the connection wasn't registered. */ -int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn); - -#endif diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c deleted file mode 100644 index 18bbf789..00000000 --- a/src/pulsecore/dbus-objs/core.c +++ /dev/null @@ -1,482 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio 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. - - PulseAudio 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 - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#include -#include -#include - -#include "core.h" - -#define OBJECT_PATH "/org/pulseaudio1" -#define INTERFACE_CORE "org.PulseAudio.Core1" - -struct pa_dbusobj_core { - pa_core *core; -}; - -static const char *introspection_snippet = - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n"; - -/* If you need to modify this list, note that handle_get_all() uses hard-coded - * indexes to point to these strings, so make sure the indexes don't go wrong - * there. */ -static const char *properties[] = { - "InterfaceRevision", - "Name", - "Version", - "Username", - "Hostname", - "DefaultChannels", - "DefaultSampleFormat", - "DefaultSampleRate", - "Sinks", - "FallbackSink", - "Sources", - "FallbackSource", - "PlaybackStreams", - "RecordStreams", - "Samples", - "Modules", - "Clients", - "Extensions", - NULL -}; - -static const char *methods[] = { - "GetCardByName", - "GetSinkByName", - "GetSourceByName", - "GetSampleByName", - "UploadSample", - "LoadSampleFromFile", - "AddLazySample", - "AddLazySamplesFromDirectory", - "LoadModule", - "Exit", - NULL -}; - -static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - DBusMessage *reply = NULL; - const char *server_name = PACKAGE_NAME; - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!(reply = dbus_message_new_method_return(msg))) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - dbus_message_iter_init_append(reply, &msg_iter); - if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - const char* interface; - const char* property; - DBusMessage *reply = NULL; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (*interface && !pa_streq(interface, INTERFACE_CORE)) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - - if (pa_streq(property, "Name")) { - r = handle_get_name(conn, msg, c); - goto finish; - } - - if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - const char* interface; - const char* property; - DBusMessage *reply = NULL; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (*interface && !pa_streq(interface, INTERFACE_CORE)) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - - if (pa_streq(property, "Name")) { - if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - - if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - DBusMessage *reply = NULL; - char *interface = NULL; - char const *server_name = PACKAGE_NAME; - DBusMessageIter msg_iter; - DBusMessageIter dict_iter; - DBusMessageIter dict_entry_iter; - DBusMessageIter variant_iter; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (!(reply = dbus_message_new_method_return(msg))) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - dbus_message_iter_init_append(reply, &msg_iter); - if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */ - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { - pa_dbusobj_core *c = user_data; - - pa_assert(connection); - pa_assert(message); - pa_assert(c); - - if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get"))) - return handle_get(connection, message, c); - - if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set"))) - return handle_set(connection, message, c); - - if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll"))) - return handle_get_all(connection, message, c); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { - pa_dbusobj_core *c; - - pa_assert(core); - - c = pa_xnew(pa_dbusobj_core, 1); - c->core = core; - - pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c); - - - return c; -} - -void pa_dbusobj_core_free(pa_dbusobj_core *c) { - pa_assert(c); - - pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE); - - pa_xfree(c); -} diff --git a/src/pulsecore/dbus-objs/core.h b/src/pulsecore/dbus-objs/core.h deleted file mode 100644 index 8e59cc3d..00000000 --- a/src/pulsecore/dbus-objs/core.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef foodbusobjscorehfoo -#define foodbusobjscorehfoo - -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio 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. - - PulseAudio 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 - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* This object implements the D-Bus object at path /org/pulseaudio/core. - * The implemented interface is org.pulseaudio.Core. - * - * See http://pulseaudio.org/wiki/DBusInterface for the Core interface - * documentation. - */ - -#include - -typedef struct pa_dbusobj_core pa_dbusobj_core; - -pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core); -void pa_dbusobj_core_free(pa_dbusobj_core *c); - -#endif diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index e047dc31..cfc3e8cb 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -444,3 +445,335 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) { pa_dbus_pending_free(i); } } + +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message) { + DBusMessage *reply = NULL; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(name); + pa_assert(message); + + pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message))); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + + dbus_message_unref(reply); +} + +void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) { + DBusMessage *reply = NULL; + + pa_assert(c); + pa_assert(in_reply_to); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) { + DBusMessage *reply = NULL; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +static const char *signature_from_basic_type(int type) { + switch (type) { + case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING; + case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING; + case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING; + case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING; + case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING; + case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING; + case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING; + case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING; + case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING; + case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING; + case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING; + case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING; + default: pa_assert_not_reached(); + } +} + +void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter)); + pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data)); + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter)); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +/* Note: returns sizeof(char*) for strings, object paths and signatures. */ +static unsigned basic_type_size(int type) { + switch (type) { + case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t); + case DBUS_TYPE_BYTE: return 1; + case DBUS_TYPE_INT16: return sizeof(dbus_int16_t); + case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t); + case DBUS_TYPE_INT32: return sizeof(dbus_int32_t); + case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t); + case DBUS_TYPE_INT64: return sizeof(dbus_int64_t); + case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t); + case DBUS_TYPE_DOUBLE: return sizeof(double); + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: return sizeof(char*); + default: pa_assert_not_reached(); + } +} + +void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) { + DBusMessageIter array_iter; + unsigned i; + unsigned item_size; + + pa_assert(iter); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + item_size = basic_type_size(item_type); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter)); + + for (i = 0; i < n; ++i) + pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size])); + + pa_assert_se(dbus_message_iter_close_container(iter, &array_iter)); +}; + +void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) { + DBusMessageIter variant_iter; + + pa_assert(iter); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter)); + pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data)); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); +} + +void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) { + DBusMessageIter variant_iter; + char *array_signature; + + pa_assert(iter); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type)); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter)); + pa_dbus_append_basic_array(&variant_iter, item_type, array, n); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); + + pa_xfree(array_signature); +} + +void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) { + DBusMessageIter dict_entry_iter; + + pa_assert(dict_iter); + pa_assert(key); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_dbus_append_basic_variant(&dict_entry_iter, type, data); + pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); +} + +void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) { + DBusMessageIter dict_entry_iter; + + pa_assert(dict_iter); + pa_assert(key); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n); + pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); +} + +int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + + pa_assert(c); + pa_assert(msg); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return -1; + } + + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); + return -1; + } + + dbus_message_iter_recurse(&msg_iter, &variant_iter); + + if (dbus_message_iter_get_arg_type(&variant_iter) != type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Variant has wrong contained type."); + return -1; + } + + dbus_message_iter_get_basic(&variant_iter, data); + + return 0; +} + +int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { + pa_assert(c); + pa_assert(msg); + pa_assert(iter); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + if (dbus_message_iter_get_arg_type(iter) != type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments."); + return -1; + } + + dbus_message_iter_get_basic(iter, data); + + return 0; +} + +int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { + DBusMessageIter array_iter; + int signed_n; + + pa_assert(c); + pa_assert(msg); + pa_assert(iter); + pa_assert(dbus_type_is_fixed(item_type)); + pa_assert(array); + pa_assert(n); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + return -1; + } + + if (dbus_message_iter_get_element_type(iter) != item_type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type."); + return -1; + } + + dbus_message_iter_recurse(iter, &array_iter); + + dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); + + pa_assert(signed_n >= 0); + + *n = signed_n; + + return 0; +} + +pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { + DBusMessageIter dict_iter; + pa_proplist *proplist = NULL; + const char *key; + const uint8_t *value; + int value_length; + + pa_assert(c); + pa_assert(msg); + pa_assert(iter); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + return NULL; + } + + if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + return NULL; + } + + proplist = pa_proplist_new(); + + dbus_message_iter_recurse(iter, &dict_iter); + + while (dbus_message_iter_has_next(&dict_iter)) { + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + goto fail; + } + + dbus_message_iter_get_basic(&dict_iter, &key); + + if (strlen(key) <= 0 || !pa_ascii_valid(key)) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); + goto fail; + } + + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. An array was expected."); + goto fail; + } + + if (dbus_message_iter_get_element_type(&dict_iter) != DBUS_TYPE_BYTE) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type. A byte was expected."); + goto fail; + } + + dbus_message_iter_get_fixed_array(&dict_iter, &value, &value_length); + + pa_assert(value_length >= 0); + + pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); + } + + return proplist; + +fail: + if (proplist) + pa_proplist_free(proplist); + + return NULL; +} diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 97328735..1a8aeac9 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -26,6 +26,7 @@ #include #include +#include /* A wrap connection is not shared or refcounted, it is available in client side */ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; @@ -61,4 +62,32 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); /* Free up a list of pa_dbus_pending_call objects */ void pa_dbus_free_pending_list(pa_dbus_pending **p); +/* Sends an error message as the reply to the given message. */ +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message); + +void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); +void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); +void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); +void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); + +void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); +void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); +void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); +void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); +void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); + +/* Helper function for extracting the value argument of a Set call for a + * property with a basic type. If the message is invalid, an error reply is + * sent and a negative number is returned. */ +int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); + +/* If the arguments can't be read from the iterator, an error reply is sent and + * a negative number is returned. */ +int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); +int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); + +/* Returns a new proplist, that the caller has to free. If the proplist can't + * be read from the iterator, an error reply is sent and NULL is returned. */ +pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); + #endif diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index 9df2f583..046c87b2 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -149,21 +149,48 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0); + if (type == PA_NAMEREG_SINK && !c->default_sink) + pa_namereg_set_default_sink(c, data); + else if (type == PA_NAMEREG_SOURCE && !c->default_source) + pa_namereg_set_default_source(c, data); + return e->name; } void pa_namereg_unregister(pa_core *c, const char *name) { struct namereg_entry *e; + uint32_t idx; pa_assert(c); pa_assert(name); pa_assert_se(e = pa_hashmap_remove(c->namereg, name)); - if (c->default_sink == e->data) - pa_namereg_set_default_sink(c, NULL); - else if (c->default_source == e->data) - pa_namereg_set_default_source(c, NULL); + if (c->default_sink == e->data) { + pa_sink *new_default = pa_idxset_first(c->sinks, &idx); + + if (new_default == e->data) + new_default = pa_idxset_next(c->sinks, &idx); + + pa_namereg_set_default_sink(c, new_default); + + } else if (c->default_source == e->data) { + pa_source *new_default; + + for (new_default = pa_idxset_first(c->sources, &idx); new_default; new_default = pa_idxset_next(c->sources, &idx)) { + if (new_default != e->data && !new_default->monitor_of) + break; + } + + if (!new_default) { + new_default = pa_idxset_first(c->sources, &idx); + + if (new_default == e->data) + new_default = pa_idxset_next(c->sources, &idx); + } + + pa_namereg_set_default_source(c, new_default); + } pa_xfree(e->name); pa_xfree(e); @@ -191,7 +218,6 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK))) return s->monitor_source; - } if (!name) @@ -242,35 +268,16 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { return s; } +/* XXX: After removing old functionality, has this function become useless? */ pa_sink *pa_namereg_get_default_sink(pa_core *c) { - pa_sink *s; - pa_assert(c); - if (c->default_sink) - return c->default_sink; - - if ((s = pa_idxset_first(c->sinks, NULL))) - return pa_namereg_set_default_sink(c, s); - - return NULL; + return c->default_sink; } +/* XXX: After removing old functionality, has this function become useless? */ pa_source *pa_namereg_get_default_source(pa_core *c) { - pa_source *s; - uint32_t idx; - pa_assert(c); - if (c->default_source) - return c->default_source; - - for (s = PA_SOURCE(pa_idxset_first(c->sources, &idx)); s; s = PA_SOURCE(pa_idxset_next(c->sources, &idx))) - if (!s->monitor_of) - return pa_namereg_set_default_source(c, s); - - if ((s = pa_idxset_first(c->sources, NULL))) - return pa_namereg_set_default_source(c, s); - - return NULL; + return c->default_source; } diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c new file mode 100644 index 00000000..fb7d168d --- /dev/null +++ b/src/pulsecore/protocol-dbus.c @@ -0,0 +1,930 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio 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. + + PulseAudio 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include +#include +#include +#include + +#include "protocol-dbus.h" + +struct pa_dbus_protocol { + PA_REFCNT_DECLARE; + + pa_core *core; + pa_hashmap *objects; /* Object path -> struct object_entry */ + pa_hashmap *connections; /* DBusConnection -> struct connection_entry */ + pa_hashmap *extensions; /* String -> anything */ +}; + +struct object_entry { + char *path; + pa_hashmap *interfaces; /* Interface name -> struct interface_entry */ + char *introspection; +}; + +struct connection_entry { + DBusConnection *connection; + pa_client *client; + + pa_bool_t listening_for_all_signals; + + /* Contains object paths. If this is empty, then signals from all objects + * are accepted. Only used when listening_for_all_signals == TRUE. */ + pa_idxset *all_signals_objects; + + /* Signal name -> idxset. The idxsets contain object paths. If an idxset is + * empty, then that signal is accepted from all objects. Only used when + * listening_for_all_signals == FALSE. */ + pa_hashmap *listening_signals; +}; + +struct interface_entry { + char *name; + pa_hashmap *method_handlers; + pa_hashmap *property_handlers; + pa_dbus_receive_cb_t get_all_properties_cb; + pa_dbus_signal_info *signals; + unsigned n_signals; + void *userdata; +}; + +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { + char *address = NULL; + char *runtime_path = NULL; + char *escaped_path = NULL; + + switch (server_type) { + case PA_SERVER_TYPE_USER: + pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))); + pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path))); + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_SYSTEM: + pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))); + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_NONE: + address = pa_xnew0(char, 1); + break; + + default: + pa_assert_not_reached(); + } + + pa_xfree(runtime_path); + pa_xfree(escaped_path); + + return address; +} + +static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { + pa_dbus_protocol *p; + + pa_assert(c); + + p = pa_xnew(pa_dbus_protocol, 1); + PA_REFCNT_INIT(p); + p->core = c; + p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + p->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0); + + return p; +} + +pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) { + pa_dbus_protocol *p; + + if ((p = pa_shared_get(c, "dbus-protocol"))) + return pa_dbus_protocol_ref(p); + + return dbus_protocol_new(c); +} + +pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) { + pa_assert(p); + pa_assert(PA_REFCNT_VALUE(p) >= 1); + + PA_REFCNT_INC(p); + + return p; +} + +void pa_dbus_protocol_unref(pa_dbus_protocol *p) { + pa_assert(p); + pa_assert(PA_REFCNT_VALUE(p) >= 1); + + if (PA_REFCNT_DEC(p) > 0) + return; + + pa_assert(pa_hashmap_isempty(p->objects)); + pa_assert(pa_hashmap_isempty(p->connections)); + pa_assert(pa_hashmap_isempty(p->extensions)); + + pa_hashmap_free(p->objects, NULL, NULL); + pa_hashmap_free(p->connections, NULL, NULL); + pa_hashmap_free(p->extensions, NULL, NULL); + + pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); + + pa_xfree(p); +} + +static void update_introspection(struct object_entry *oe) { + pa_strbuf *buf; + void *interfaces_state = NULL; + struct interface_entry *iface_entry = NULL; + + pa_assert(oe); + + buf = pa_strbuf_new(); + pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + pa_strbuf_puts(buf, "\n"); + + while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &interfaces_state, NULL))) { + pa_dbus_method_handler *method_handler; + pa_dbus_property_handler *property_handler; + void *handlers_state = NULL; + unsigned i; + unsigned j; + + pa_strbuf_printf(buf, " \n", iface_entry->name); + + while ((method_handler = pa_hashmap_iterate(iface_entry->method_handlers, &handlers_state, NULL))) { + pa_strbuf_printf(buf, " \n", method_handler->method_name); + + for (i = 0; i < method_handler->n_arguments; ++i) + pa_strbuf_printf(buf, " \n", method_handler->arguments[i].name, + method_handler->arguments[i].type, + method_handler->arguments[i].direction); + + pa_strbuf_puts(buf, " \n"); + } + + handlers_state = NULL; + + while ((property_handler = pa_hashmap_iterate(iface_entry->property_handlers, &handlers_state, NULL))) + pa_strbuf_printf(buf, " \n", property_handler->property_name, + property_handler->type, + property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); + + for (i = 0; i < iface_entry->n_signals; ++i) { + pa_strbuf_printf(buf, " \n", iface_entry->signals[i].name); + + for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) + pa_strbuf_printf(buf, " \n", iface_entry->signals[i].arguments[j].name, + iface_entry->signals[i].arguments[j].type); + + pa_strbuf_puts(buf, " \n"); + } + + pa_strbuf_puts(buf, " \n"); + } + + pa_strbuf_puts(buf, " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); + + pa_strbuf_puts(buf, "\n"); + + pa_xfree(oe->introspection); + oe->introspection = pa_strbuf_tostring_free(buf); +} + +enum find_result_t { + FOUND_METHOD, + FOUND_GET_PROPERTY, + FOUND_SET_PROPERTY, + FOUND_GET_ALL, + PROPERTY_ACCESS_DENIED, + NO_SUCH_METHOD, + NO_SUCH_PROPERTY, + INVALID_MESSAGE_ARGUMENTS +}; + +static enum find_result_t find_handler_by_property(struct object_entry *obj_entry, + DBusMessage *msg, + const char *property, + struct interface_entry **iface_entry, + pa_dbus_property_handler **property_handler) { + void *state = NULL; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(property); + pa_assert(iface_entry); + pa_assert(property_handler); + + while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { + if (dbus_message_has_member(msg, "Get")) + return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; + else if (dbus_message_has_member(msg, "Set")) + return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + else + pa_assert_not_reached(); + } + } + + return NO_SUCH_PROPERTY; +} + +static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, + const char *method, + struct interface_entry **iface_entry, + pa_dbus_method_handler **method_handler) { + void *state = NULL; + + pa_assert(obj_entry); + pa_assert(method); + pa_assert(iface_entry); + pa_assert(method_handler); + + while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) + return FOUND_METHOD; + } + + return NO_SUCH_METHOD; +} + +static enum find_result_t find_handler_from_properties_call(struct object_entry *obj_entry, + DBusMessage *msg, + struct interface_entry **iface_entry, + pa_dbus_property_handler **property_handler, + const char **attempted_property) { + const char *interface; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(iface_entry); + + if (dbus_message_has_member(msg, "GetAll")) { + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return FOUND_GET_ALL; + else + return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ + } else { + pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); + return FOUND_GET_ALL; + } + } else { + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, attempted_property, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && + (*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, *attempted_property))) { + if (dbus_message_has_member(msg, "Get")) + return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; + else if (dbus_message_has_member(msg, "Set")) + return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + else + pa_assert_not_reached(); + } else + return NO_SUCH_PROPERTY; + } else + return find_handler_by_property(obj_entry, msg, *attempted_property, iface_entry, property_handler); + } +} + +static enum find_result_t find_handler(struct object_entry *obj_entry, + DBusMessage *msg, + struct interface_entry **iface_entry, + pa_dbus_method_handler **method_handler, + pa_dbus_property_handler **property_handler, + const char **attempted_property) { + const char *interface; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(iface_entry); + pa_assert(method_handler); + pa_assert(property_handler); + pa_assert(attempted_property); + + *iface_entry = NULL; + *method_handler = NULL; + + if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) + return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + + else if ((interface = dbus_message_get_interface(msg))) { + if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && + (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) + return FOUND_METHOD; + else + return NO_SUCH_METHOD; + + } else { /* The method call doesn't contain an interface. */ + if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { + if (find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler) == FOUND_METHOD) + return FOUND_METHOD; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + else + /* Assume this is a .Properties call. */ + return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + + } else /* This is not a .Properties call. */ + return find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler); + } +} + +static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + pa_dbus_protocol *p = user_data; + struct object_entry *obj_entry = NULL; + struct interface_entry *iface_entry = NULL; + pa_dbus_method_handler *method_handler = NULL; + pa_dbus_property_handler *property_handler = NULL; + const char *attempted_property = NULL; + DBusMessage *reply = NULL; + + pa_assert(connection); + pa_assert(message); + pa_assert(p); + pa_assert(p->objects); + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { + pa_assert_se((reply = dbus_message_new_method_return(message))); + pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + + pa_log_debug("%s.Introspect handled.", obj_entry->path); + + goto finish; + } + + switch (find_handler(obj_entry, message, &iface_entry, &method_handler, &property_handler, &attempted_property)) { + case FOUND_METHOD: + method_handler->receive_cb(connection, message, iface_entry->userdata); + break; + + case FOUND_GET_PROPERTY: + property_handler->get_cb(connection, message, iface_entry->userdata); + break; + + case FOUND_SET_PROPERTY: + property_handler->set_cb(connection, message, iface_entry->userdata); + break; + + case FOUND_GET_ALL: + if (iface_entry->get_all_properties_cb) + iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); + break; + + case PROPERTY_ACCESS_DENIED: + pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + case NO_SUCH_METHOD: + pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + case NO_SUCH_PROPERTY: + pa_assert_se((reply = dbus_message_new_error_printf(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + case INVALID_MESSAGE_ARGUMENTS: + pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + default: + pa_assert_not_reached(); + } + +finish: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusObjectPathVTable vtable = { + .unregister_function = NULL, + .message_function = handle_message_cb, + .dbus_internal_pad1 = NULL, + .dbus_internal_pad2 = NULL, + .dbus_internal_pad3 = NULL, + .dbus_internal_pad4 = NULL +}; + +static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) { + struct connection_entry *conn_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(obj_entry); + + while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p)); +} + +static pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) { + pa_dbus_arg_info *dst; + unsigned i; + + if (n == 0) + return NULL; + + pa_assert(src); + + dst = pa_xnew0(pa_dbus_arg_info, n); + + for (i = 0; i < n; ++i) { + dst[i].name = pa_xstrdup(src[i].name); + dst[i].type = pa_xstrdup(src[i].type); + dst[i].direction = pa_xstrdup(src[i].direction); + } + + return dst; +} + +static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) { + pa_hashmap *handlers; + unsigned i; + + pa_assert(info); + pa_assert(info->method_handlers || info->n_method_handlers == 0); + + handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < info->n_method_handlers; ++i) { + pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1); + h->method_name = pa_xstrdup(info->method_handlers[i].method_name); + h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments); + h->n_arguments = info->method_handlers[i].n_arguments; + h->receive_cb = info->method_handlers[i].receive_cb; + + pa_hashmap_put(handlers, h->method_name, h); + } + + return handlers; +} + +static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) { + pa_hashmap *handlers; + unsigned i = 0; + + pa_assert(info); + pa_assert(info->property_handlers || info->n_property_handlers == 0); + + handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < info->n_property_handlers; ++i) { + pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1); + h->property_name = pa_xstrdup(info->property_handlers[i].property_name); + h->type = pa_xstrdup(info->property_handlers[i].type); + h->get_cb = info->property_handlers[i].get_cb; + h->set_cb = info->property_handlers[i].set_cb; + + pa_hashmap_put(handlers, h->property_name, h); + } + + return handlers; +} + +static pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) { + pa_dbus_signal_info *dst; + unsigned i; + + pa_assert(info); + + if (info->n_signals == 0) + return NULL; + + pa_assert(info->signals); + + dst = pa_xnew(pa_dbus_signal_info, info->n_signals); + + for (i = 0; i < info->n_signals; ++i) { + dst[i].name = pa_xstrdup(info->signals[i].name); + dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments); + dst[i].n_arguments = info->signals[i].n_arguments; + } + + return dst; +} + +int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, + const char *path, + const pa_dbus_interface_info *info, + void *userdata) { + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + pa_bool_t obj_entry_created = FALSE; + + pa_assert(p); + pa_assert(path); + pa_assert(info); + pa_assert(info->name); + pa_assert(info->method_handlers || info->n_method_handlers == 0); + pa_assert(info->property_handlers || info->n_property_handlers == 0); + pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0); + pa_assert(info->signals || info->n_signals == 0); + + if (!(obj_entry = pa_hashmap_get(p->objects, path))) { + obj_entry = pa_xnew(struct object_entry, 1); + obj_entry->path = pa_xstrdup(path); + obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + obj_entry->introspection = NULL; + + pa_hashmap_put(p->objects, path, obj_entry); + obj_entry_created = TRUE; + } + + if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL) + goto fail; /* The interface was already registered. */ + + iface_entry = pa_xnew(struct interface_entry, 1); + iface_entry->name = pa_xstrdup(info->name); + iface_entry->method_handlers = create_method_handlers(info); + iface_entry->property_handlers = create_property_handlers(info); + iface_entry->get_all_properties_cb = info->get_all_properties_cb; + iface_entry->signals = copy_signals(info); + iface_entry->n_signals = info->n_signals; + iface_entry->userdata = userdata; + pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry); + + update_introspection(obj_entry); + + if (obj_entry_created) + register_object(p, obj_entry); + + return 0; + +fail: + if (obj_entry_created) { + pa_hashmap_remove(p->objects, path); + pa_dbus_protocol_unref(p); + pa_xfree(obj_entry); + } + + return -1; +} + +static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) { + struct connection_entry *conn_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(obj_entry); + + while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path)); +} + +static void method_handler_free_cb(void *p, void *userdata) { + pa_dbus_method_handler *h = p; + unsigned i; + + pa_assert(h); + + pa_xfree((char *) h->method_name); + + for (i = 0; i < h->n_arguments; ++i) { + pa_xfree((char *) h->arguments[i].name); + pa_xfree((char *) h->arguments[i].type); + pa_xfree((char *) h->arguments[i].direction); + } + + pa_xfree((pa_dbus_arg_info *) h->arguments); +} + +static void property_handler_free_cb(void *p, void *userdata) { + pa_dbus_property_handler *h = p; + + pa_assert(h); + + pa_xfree((char *) h->property_name); + pa_xfree((char *) h->type); +} + +int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) { + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + unsigned i; + + pa_assert(p); + pa_assert(path); + pa_assert(interface); + + if (!(obj_entry = pa_hashmap_get(p->objects, path))) + return -1; + + if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface))) + return -1; + + update_introspection(obj_entry); + + pa_xfree(iface_entry->name); + pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); + pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); + + for (i = 0; i < iface_entry->n_signals; ++i) { + unsigned j; + + pa_xfree((char *) iface_entry->signals[i].name); + + for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) { + pa_xfree((char *) iface_entry->signals[i].arguments[j].name); + pa_xfree((char *) iface_entry->signals[i].arguments[j].type); + pa_assert(iface_entry->signals[i].arguments[j].direction == NULL); + } + + pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments); + } + + pa_xfree(iface_entry->signals); + pa_xfree(iface_entry); + + if (pa_hashmap_isempty(obj_entry->interfaces)) { + unregister_object(p, obj_entry); + + pa_hashmap_remove(p->objects, path); + pa_xfree(obj_entry->path); + pa_hashmap_free(obj_entry->interfaces, NULL, NULL); + pa_xfree(obj_entry->introspection); + pa_xfree(obj_entry); + } + + return 0; +} + +static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(conn); + + while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p)); +} + +int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + pa_assert(client); + + if (pa_hashmap_get(p->connections, conn)) + return -1; /* The connection was already registered. */ + + register_all_objects(p, conn); + + conn_entry = pa_xnew(struct connection_entry, 1); + conn_entry->connection = dbus_connection_ref(conn); + conn_entry->client = client; + conn_entry->listening_for_all_signals = FALSE; + conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + conn_entry->listening_signals = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + pa_hashmap_put(p->connections, conn, conn_entry); + + return 0; +} + +static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(conn); + + while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path)); +} + +static void free_listened_object_name_cb(void *p, void *userdata) { + pa_assert(p); + + pa_xfree(p); +} + +static void free_listening_signals_idxset_cb(void *p, void *userdata) { + pa_idxset *set = p; + + pa_assert(set); + + pa_idxset_free(set, free_listened_object_name_cb, NULL); +} + +int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + + if (!(conn_entry = pa_hashmap_remove(p->connections, conn))) + return -1; + + unregister_all_objects(p, conn); + + dbus_connection_unref(conn_entry->connection); + pa_idxset_free(conn_entry->all_signals_objects, free_listened_object_name_cb, NULL); + pa_hashmap_free(conn_entry->listening_signals, free_listening_signals_idxset_cb, NULL); + pa_xfree(conn_entry); + + return 0; +} + +void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { + struct connection_entry *conn_entry; + pa_idxset *object_set; + char *object_path; + unsigned i; + + pa_assert(p); + pa_assert(conn); + pa_assert(objects || n_objects == 0); + + pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn))); + + /* all_signals_objects will either be emptied or replaced with new objects, + * so we empty it here unconditionally. If listening_for_all_signals is + * currently FALSE, the idxset is empty already. */ + while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL))) + pa_xfree(object_path); + + if (signal) { + conn_entry->listening_for_all_signals = FALSE; + + /* Replace the old object list with a new one. */ + if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < n_objects; ++i) + pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL); + + } else { + conn_entry->listening_for_all_signals = TRUE; + + /* We're not interested in individual signals anymore, so let's empty + * listening_signals. */ + while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + + for (i = 0; i < n_objects; ++i) + pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL); + } +} + +void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal) { + struct connection_entry *conn_entry; + pa_idxset *object_set; + + pa_assert(p); + pa_assert(conn); + + pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn))); + + if (signal) { + if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + + } else { + char *object_path; + + conn_entry->listening_for_all_signals = FALSE; + + while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL))) + pa_xfree(object_path); + + while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + } +} + +void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { + struct connection_entry *conn_entry; + void *state = NULL; + pa_idxset *object_set; + DBusMessage *signal_copy; + + pa_assert(p); + pa_assert(signal); + pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); + + /* XXX: We have to do some linear searching to find connections that want + * to receive the signal. This shouldn't be very significant performance + * problem, and adding an (object path, signal name) -> connection mapping + * would be likely to create substantial complexity. */ + + while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) { + + if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ + && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL) + || pa_idxset_isempty(conn_entry->all_signals_objects))) + + || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */ + && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal)) + && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL) + || pa_idxset_isempty(object_set)))) { + + pa_assert_se(signal_copy = dbus_message_copy(signal)); + pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL)); + dbus_message_unref(signal_copy); + } + } +} + +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + + if (!(conn_entry = pa_hashmap_get(p->connections, conn))) + return NULL; + + return conn_entry->client; +} + +const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { + const char **extensions; + const char *ext_name; + void *state = NULL; + unsigned i = 0; + + pa_assert(p); + pa_assert(n); + + *n = pa_hashmap_size(p->extensions); + + if (*n <= 0) + return NULL; + + extensions = pa_xnew(const char *, *n); + + while (pa_hashmap_iterate(p->extensions, &state, (const void **) &ext_name)) { + extensions[i] = ext_name; + ++i; + } + + return extensions; +} diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h new file mode 100644 index 00000000..27198f48 --- /dev/null +++ b/src/pulsecore/protocol-dbus.h @@ -0,0 +1,158 @@ +#ifndef fooprotocoldbushfoo +#define fooprotocoldbushfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio 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. + + PulseAudio 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include + +#define PA_DBUS_DEFAULT_PORT 24883 +#define PA_DBUS_SOCKET_NAME "dbus-socket" + +#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME + +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" +#define PA_DBUS_ERROR_NOT_FOUND "org.PulseAudio.Core1.NotFoundError" + +/* Returns the default address of the server type in the escaped form. For + * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the + * string. */ +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); + +typedef struct pa_dbus_protocol pa_dbus_protocol; + +/* This function either creates a new pa_dbus_protocol object, or if one + * already exists, increases the reference count. */ +pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c); + +pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p); +void pa_dbus_protocol_unref(pa_dbus_protocol *p); + +/* Called when a received message needs handling. Completely ignoring the + * message isn't a good idea; if you can't handle the message, reply with an + * error. + * + * All messages are method calls. */ +typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata); + +typedef struct pa_dbus_arg_info { + const char *name; + const char *type; + const char *direction; /* NULL for signal arguments. */ +} pa_dbus_arg_info; + +typedef struct pa_dbus_signal_info { + const char *name; + const pa_dbus_arg_info *arguments; /* NULL, if the signal has no args. */ + unsigned n_arguments; +} pa_dbus_signal_info; + +typedef struct pa_dbus_method_handler { + const char *method_name; + const pa_dbus_arg_info *arguments; /* NULL, if the method has no args. */ + unsigned n_arguments; + pa_dbus_receive_cb_t receive_cb; +} pa_dbus_method_handler; + +typedef struct pa_dbus_property_handler { + const char *property_name; + const char *type; + + /* The access mode for the property is determined by checking whether + * get_cb or set_cb is NULL. */ + pa_dbus_receive_cb_t get_cb; + pa_dbus_receive_cb_t set_cb; +} pa_dbus_property_handler; + +typedef struct pa_dbus_interface_info { + const char* name; + const pa_dbus_method_handler *method_handlers; /* NULL, if the interface has no methods. */ + unsigned n_method_handlers; + const pa_dbus_property_handler *property_handlers; /* NULL, if the interface has no properties. */ + unsigned n_property_handlers; + const pa_dbus_receive_cb_t get_all_properties_cb; /* May be NULL, in which case GetAll returns an error. */ + const pa_dbus_signal_info *signals; /* NULL, if the interface has no signals. */ + unsigned n_signals; +} pa_dbus_interface_info; + + +/* The following functions may only be called from the main thread. */ + +/* Registers the given interface to the given object path. It doesn't matter + * whether or not the object has already been registered; if it is, then its + * interface set is extended. + * + * Introspection requests are handled automatically. + * + * Userdata is passed to all the callbacks. + * + * Fails and returns a negative number if the object already has the interface + * registered. */ +int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, const char *path, const pa_dbus_interface_info *info, void *userdata); + +/* Returns a negative number if the given object doesn't have the given + * interface registered. */ +int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface); + +/* Fails and returns a negative number if the connection is already + * registered. */ +int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client); + +/* Returns a negative number if the connection wasn't registered. */ +int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn); + +/* Enables signal receiving for the given connection. The connection must have + * been registered earlier. + * + * If the signal argument is NULL, all signals will be sent to the connection, + * otherwise calling this function only adds the given signal to the list of + * signals that will be delivered to the connection. + * + * The objects argument is a list of object paths. If the list is not empty, + * only signals from the given objects are delivered. If this function is + * called multiple time for the same connection and signal, the latest call + * always replaces the previous object list. */ +void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects); + +/* Disables the delivery of the signal for the given connection. The connection + * must have been registered. If signal is NULL, all signals are disabled. If + * signal is non-NULL and _add_signal_listener() was previously called with + * NULL signal (causing all signals to be enabled), this function doesn't do + * anything. Also, if the signal wasn't enabled before, this function doesn't + * do anything in that case either. */ +void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal); + +void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); + +/* Returns NULL if the connection isn't registered. */ +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); + +/* Returns an array of extension identifier strings. The strings pointers point + * to the internal copies, so don't free the strings. The caller must free the + * array, however. Also, do not save the returned pointer or any of the string + * pointers, because the contained strings may be freed at any time. If you + * need to save the array, copy it. */ +const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n); + +#endif -- cgit From 018810ec9a96d63446bdc9a82d6c931779f7a97d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 27 Jul 2009 20:01:39 +0300 Subject: Bug fixing and minor cleanups. --- src/pulsecore/dbus-util.c | 73 ++++++++++++++++++++++++++++++++++++++++------- src/pulsecore/dbus-util.h | 9 +++--- 2 files changed, 67 insertions(+), 15 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index cfc3e8cb..43dbd6b2 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -446,18 +446,25 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) { } } -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message) { +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) { + va_list ap; + char *message; DBusMessage *reply = NULL; pa_assert(c); pa_assert(in_reply_to); pa_assert(name); - pa_assert(message); + pa_assert(format); + va_start(ap, format); + message = pa_vsprintf_malloc(format, ap); + va_end(ap); pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message))); pa_assert_se(dbus_connection_send(c, reply, NULL)); dbus_message_unref(reply); + + pa_xfree(message); } void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) { @@ -655,36 +662,71 @@ int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int dbus_message_iter_recurse(&msg_iter, &variant_iter); - if (dbus_message_iter_get_arg_type(&variant_iter) != type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Variant has wrong contained type."); + if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0) + return -1; + + return 0; +} + +int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) { + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + + pa_assert(c); + pa_assert(msg); + pa_assert(dbus_type_is_fixed(item_type)); + pa_assert(data); + pa_assert(n); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); return -1; } - dbus_message_iter_get_basic(&variant_iter, data); + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); + return -1; + } + + dbus_message_iter_recurse(&msg_iter, &variant_iter); + + if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0) + return -1; return 0; } int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { + int arg_type; + pa_assert(c); pa_assert(msg); pa_assert(iter); pa_assert(dbus_type_is_basic(type)); pa_assert(data); - if (dbus_message_iter_get_arg_type(iter) != type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != type) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type); return -1; } dbus_message_iter_get_basic(iter, data); + dbus_message_iter_next(iter); + return 0; } int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { DBusMessageIter array_iter; int signed_n; + int arg_type; + int element_type; pa_assert(c); pa_assert(msg); @@ -693,13 +735,18 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage pa_assert(array); pa_assert(n); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type); return -1; } - if (dbus_message_iter_get_element_type(iter) != item_type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type."); + element_type = dbus_message_iter_get_element_type(iter); + if (element_type != item_type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type); return -1; } @@ -707,6 +754,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); + dbus_message_iter_next(iter); + pa_assert(signed_n >= 0); *n = signed_n; @@ -769,6 +818,8 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); } + dbus_message_iter_next(iter); + return proplist; fail: diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 1a8aeac9..97aae372 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -63,7 +63,7 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); void pa_dbus_free_pending_list(pa_dbus_pending **p); /* Sends an error message as the reply to the given message. */ -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message); +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); @@ -76,10 +76,11 @@ void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); -/* Helper function for extracting the value argument of a Set call for a - * property with a basic type. If the message is invalid, an error reply is - * sent and a negative number is returned. */ +/* Helper functions for extracting the value argument of a Set call. If the + * message is invalid, an error reply is sent and a negative number is + * returned. */ int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); +int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n); /* If the arguments can't be read from the iterator, an error reply is sent and * a negative number is returned. */ -- cgit From c354a08fe3cf2468688264124b763efb1bf1df85 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 31 Jul 2009 12:05:49 +0300 Subject: dbus-protocol: Implement extension registration. --- src/pulsecore/protocol-dbus.c | 91 ++++++++++++++++++++++++++++++++++--------- src/pulsecore/protocol-dbus.h | 39 +++++++++++++++++-- 2 files changed, 108 insertions(+), 22 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index fb7d168d..81a6c024 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -41,7 +41,9 @@ struct pa_dbus_protocol { pa_core *core; pa_hashmap *objects; /* Object path -> struct object_entry */ pa_hashmap *connections; /* DBusConnection -> struct connection_entry */ - pa_hashmap *extensions; /* String -> anything */ + pa_idxset *extensions; /* Strings */ + + pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX]; }; struct object_entry { @@ -109,6 +111,7 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { pa_dbus_protocol *p; + unsigned i; pa_assert(c); @@ -117,7 +120,10 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { p->core = c; p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - p->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i) + pa_hook_init(&p->hooks[i], p); pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0); @@ -143,6 +149,8 @@ pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) { } void pa_dbus_protocol_unref(pa_dbus_protocol *p) { + unsigned i; + pa_assert(p); pa_assert(PA_REFCNT_VALUE(p) >= 1); @@ -151,11 +159,14 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) { pa_assert(pa_hashmap_isempty(p->objects)); pa_assert(pa_hashmap_isempty(p->connections)); - pa_assert(pa_hashmap_isempty(p->extensions)); + pa_assert(pa_idxset_isempty(p->extensions)); pa_hashmap_free(p->objects, NULL, NULL); pa_hashmap_free(p->connections, NULL, NULL); - pa_hashmap_free(p->extensions, NULL, NULL); + pa_idxset_free(p->extensions, NULL, NULL); + + for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i) + pa_hook_done(&p->hooks[i]); pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); @@ -660,6 +671,8 @@ static void property_handler_free_cb(void *p, void *userdata) { pa_xfree((char *) h->property_name); pa_xfree((char *) h->type); + + pa_xfree(h); } int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) { @@ -792,6 +805,18 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection * return 0; } +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + + if (!(conn_entry = pa_hashmap_get(p->connections, conn))) + return NULL; + + return conn_entry->client; +} + void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { struct connection_entry *conn_entry; pa_idxset *object_set; @@ -893,18 +918,6 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { } } -pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { - struct connection_entry *conn_entry; - - pa_assert(p); - pa_assert(conn); - - if (!(conn_entry = pa_hashmap_get(p->connections, conn))) - return NULL; - - return conn_entry->client; -} - const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { const char **extensions; const char *ext_name; @@ -914,17 +927,59 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { pa_assert(p); pa_assert(n); - *n = pa_hashmap_size(p->extensions); + *n = pa_idxset_size(p->extensions); if (*n <= 0) return NULL; extensions = pa_xnew(const char *, *n); - while (pa_hashmap_iterate(p->extensions, &state, (const void **) &ext_name)) { + while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) { extensions[i] = ext_name; ++i; } return extensions; } + +int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) { + char *internal_name; + + pa_assert(p); + pa_assert(name); + + internal_name = pa_xstrdup(name); + + if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) { + pa_xfree(internal_name); + return -1; + } + + pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name); + + return 0; +} + +int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) { + char *internal_name; + + pa_assert(p); + pa_assert(name); + + if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL))) + return -1; + + pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name); + + pa_xfree(internal_name); + + return 0; +} + +pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) { + pa_assert(p); + pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX); + pa_assert(cb); + + return pa_hook_connect(&p->hooks[hook], prio, cb, data); +} diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index 27198f48..f2b1b50b 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -119,9 +119,12 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con * registered. */ int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client); -/* Returns a negative number if the connection wasn't registered. */ +/* Returns a negative number if the connection isn't registered. */ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn); +/* Returns NULL if the connection isn't registered. */ +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); + /* Enables signal receiving for the given connection. The connection must have * been registered earlier. * @@ -145,9 +148,6 @@ void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); -/* Returns NULL if the connection isn't registered. */ -pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); - /* Returns an array of extension identifier strings. The strings pointers point * to the internal copies, so don't free the strings. The caller must free the * array, however. Also, do not save the returned pointer or any of the string @@ -155,4 +155,35 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn * need to save the array, copy it. */ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n); +/* Modules that want to provide a D-Bus interface for clients should register + * an identifier that the clients can use to check whether the additional + * functionality is available. + * + * This function registers the extension with the given name. It is recommended + * that the name follows the D-Bus interface naming convention, so that the + * names remain unique in case there will be at some point in the future + * extensions that aren't included with the main PulseAudio source tree. For + * in-tree extensions the convention is to use the org.PulseAudio.Ext + * namespace. + * + * It is suggested that the name contains a version number, and whenever the + * extension interface is modified in non-backwards compatible way, the version + * number is incremented. + * + * Fails and returns a negative number if the extension is already registered. + */ +int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name); + +/* Returns a negative number if the extension isn't registered. */ +int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name); + +/* All hooks have the pa_dbus_protocol object as hook data. */ +typedef enum pa_dbus_protocol_hook { + PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, /* Extension name as call data. */ + PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, /* Extension name as call data. */ + PA_DBUS_PROTOCOL_HOOK_MAX +} pa_dbus_protocol_hook_t; + +pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); + #endif -- cgit From 8c840572c7e2e560efcefe66707527a1dc4d16a4 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 2 Aug 2009 11:12:21 +0300 Subject: dbus-protocol: Add debugging output (temporary change). --- src/pulsecore/protocol-dbus.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 81a6c024..475b952f 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -306,6 +306,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, return FOUND_METHOD; } + pa_log("find_handler_by_method() failed."); return NO_SUCH_METHOD; } @@ -327,8 +328,10 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry if (*interface) { if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) return FOUND_GET_ALL; - else + else { + pa_log("GetAll message has unknown interface: %s", interface); return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ + } } else { pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); return FOUND_GET_ALL; @@ -378,8 +381,10 @@ static enum find_result_t find_handler(struct object_entry *obj_entry, if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) return FOUND_METHOD; - else + else { + pa_log("Message has unknown interface or there's no method handler."); return NO_SUCH_METHOD; + } } else { /* The method call doesn't contain an interface. */ if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { @@ -411,6 +416,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message)); + pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || @@ -624,6 +631,8 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, if (obj_entry_created) register_object(p, obj_entry); + pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no"); + return 0; fail: -- cgit From 805af5e8010228bee7521fbd1148e167cfb21e77 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 3 Aug 2009 19:36:19 +0300 Subject: dbus-util: Fix broken proplist reading logic. --- src/pulsecore/dbus-util.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index 43dbd6b2..5db7f218 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -765,6 +765,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + int arg_type; pa_proplist *proplist = NULL; const char *key; const uint8_t *value; @@ -774,13 +776,18 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM pa_assert(msg); pa_assert(iter); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); return NULL; } - if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + arg_type = dbus_message_iter_get_element_type(iter); + if (arg_type != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type); return NULL; } @@ -788,34 +795,45 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM dbus_message_iter_recurse(iter, &dict_iter); - while (dbus_message_iter_has_next(&dict_iter)) { - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { + dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); + + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_STRING) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); goto fail; } - dbus_message_iter_get_basic(&dict_iter, &key); + dbus_message_iter_get_basic(&dict_entry_iter, &key); + dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key)) { pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); goto fail; } - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type); goto fail; } - if (dbus_message_iter_get_element_type(&dict_iter) != DBUS_TYPE_BYTE) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type. A byte was expected."); + arg_type = dbus_message_iter_get_element_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_BYTE) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type); goto fail; } - dbus_message_iter_get_fixed_array(&dict_iter, &value, &value_length); + dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length); pa_assert(value_length >= 0); pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); + + dbus_message_iter_next(&dict_iter); } dbus_message_iter_next(iter); -- cgit From b1578e27b62e7332111bb706f79858b0866029e3 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:55:10 +0300 Subject: dbus-protocol, dbusiface-core: Take a reference when storing the core pointer. --- src/pulsecore/protocol-dbus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 475b952f..8fc08032 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -117,7 +117,7 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { p = pa_xnew(pa_dbus_protocol, 1); PA_REFCNT_INIT(p); - p->core = c; + p->core = pa_core_ref(c); p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); @@ -170,6 +170,8 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) { pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); + pa_core_unref(p->core); + pa_xfree(p); } -- cgit From 9eeb8eb2729cdc77fa98c704eb8fc7fcca15336b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:57:44 +0300 Subject: dbus-protocol: Make debug logging saner. --- src/pulsecore/protocol-dbus.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 8fc08032..c82bb5fa 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -308,7 +308,6 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, return FOUND_METHOD; } - pa_log("find_handler_by_method() failed."); return NO_SUCH_METHOD; } @@ -331,7 +330,6 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) return FOUND_GET_ALL; else { - pa_log("GetAll message has unknown interface: %s", interface); return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ } } else { @@ -418,7 +416,10 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message)); + pa_log_debug("Received message: destination = %s, interface = %s, member = %s", + dbus_message_get_path(message), + dbus_message_get_interface(message), + dbus_message_get_member(message)); pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); @@ -428,8 +429,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); pa_assert_se(dbus_connection_send(connection, reply, NULL)); - pa_log_debug("%s.Introspect handled.", obj_entry->path); - goto finish; } @@ -633,7 +632,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, if (obj_entry_created) register_object(p, obj_entry); - pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no"); + pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path); return 0; -- cgit From 0fc055226c60fa7429abf80e38f40a565f9e7922 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 18:00:08 +0300 Subject: dbus-protocol: Remove erroneous protocol object unref. --- src/pulsecore/protocol-dbus.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index c82bb5fa..06098673 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -639,7 +639,6 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, fail: if (obj_entry_created) { pa_hashmap_remove(p->objects, path); - pa_dbus_protocol_unref(p); pa_xfree(obj_entry); } -- cgit From fcf68752e687123a171b1144f78f8eba1625a204 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 08:37:33 +0300 Subject: dbus: Three entangled changes: * Make the dbus object constructors take a pa_dbusiface_core pointer as an argument. Remove the path_prefix argument. * Expose the core object path as a constant in protocol-dbus.h. * Move the core interface name constant from iface-core.h to protocol-dbus.h. --- src/pulsecore/protocol-dbus.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index f2b1b50b..c6b630a1 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -32,8 +32,11 @@ #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME -#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" -#define PA_DBUS_ERROR_NOT_FOUND "org.PulseAudio.Core1.NotFoundError" +#define PA_DBUS_CORE_INTERFACE "org.PulseAudio.Core1" +#define PA_DBUS_CORE_OBJECT_PATH "/org/pulseaudio/core1" + +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY PA_DBUS_CORE_INTERFACE ".NoSuchPropertyError" +#define PA_DBUS_ERROR_NOT_FOUND PA_DBUS_CORE_INTERFACE ".NotFoundError" /* Returns the default address of the server type in the escaped form. For * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the -- cgit From 06232e2965ee02d62ca566fcbf5e805c571b574a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:04:15 +0300 Subject: dbus: Take advantage of the PA_HASHMAP_FOREACH macro. --- src/pulsecore/protocol-dbus.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 06098673..5cbd0d91 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -186,7 +186,7 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); pa_strbuf_puts(buf, "\n"); - while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &interfaces_state, NULL))) { + PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) { pa_dbus_method_handler *method_handler; pa_dbus_property_handler *property_handler; void *handlers_state = NULL; @@ -195,7 +195,7 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_printf(buf, " \n", iface_entry->name); - while ((method_handler = pa_hashmap_iterate(iface_entry->method_handlers, &handlers_state, NULL))) { + PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) { pa_strbuf_printf(buf, " \n", method_handler->method_name); for (i = 0; i < method_handler->n_arguments; ++i) @@ -278,7 +278,7 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr pa_assert(iface_entry); pa_assert(property_handler); - while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { if (dbus_message_has_member(msg, "Get")) return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; @@ -303,7 +303,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, pa_assert(iface_entry); pa_assert(method_handler); - while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) return FOUND_METHOD; } @@ -497,7 +497,7 @@ static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) pa_assert(p); pa_assert(obj_entry); - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p)); } @@ -652,7 +652,7 @@ static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entr pa_assert(p); pa_assert(obj_entry); - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path)); } @@ -742,7 +742,7 @@ static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { pa_assert(p); pa_assert(conn); - while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + PA_HASHMAP_FOREACH(obj_entry, p->objects, state) pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p)); } @@ -777,7 +777,7 @@ static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { pa_assert(p); pa_assert(conn); - while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + PA_HASHMAP_FOREACH(obj_entry, p->objects, state) pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path)); } @@ -904,13 +904,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { pa_assert(signal); pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); - /* XXX: We have to do some linear searching to find connections that want - * to receive the signal. This shouldn't be very significant performance - * problem, and adding an (object path, signal name) -> connection mapping - * would be likely to create substantial complexity. */ - - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) { - + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) { if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL) || pa_idxset_isempty(conn_entry->all_signals_objects))) @@ -943,10 +937,8 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { extensions = pa_xnew(const char *, *n); - while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) { - extensions[i] = ext_name; - ++i; - } + while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) + extensions[i++] = ext_name; return extensions; } -- cgit From 3e9de1a36c3f85f558f08167cd82163b0cbf3484 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:12:31 +0300 Subject: dbus-util: Add helpers for proplist handling. --- src/pulsecore/dbus-util.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/dbus-util.h | 4 +++ 2 files changed, 75 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index 5db7f218..903acadb 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -564,6 +564,21 @@ void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_r dbus_message_unref(reply); } +void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(proplist); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_dbus_append_proplist_variant(&msg_iter, proplist); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) { DBusMessageIter array_iter; unsigned i; @@ -640,6 +655,62 @@ void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, c pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } +void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) { + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter array_iter; + void *state = NULL; + const char *key; + + pa_assert(iter); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter)); + + while ((key = pa_proplist_iterate(proplist, state))) { + const void *value = NULL; + size_t nbytes; + + pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0); + + pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + + pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter)); + pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes)); + pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter)); + + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + } + + pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter)); +} + +void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) { + DBusMessageIter variant_iter; + + pa_assert(iter); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter)); + pa_dbus_append_proplist(&variant_iter, proplist); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); +} + +void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) { + DBusMessageIter dict_entry_iter; + + pa_assert(dict_iter); + pa_assert(key); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_dbus_append_proplist_variant(&dict_entry_iter, proplist); + pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); +} + int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { DBusMessageIter msg_iter; DBusMessageIter variant_iter; diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 97aae372..5443a4c1 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -69,12 +69,16 @@ void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); +void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist); void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); +void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist); +void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist); +void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist); /* Helper functions for extracting the value argument of a Set call. If the * message is invalid, an error reply is sent and a negative number is -- cgit From 76bd03bddb7967c56d68ead22e5d4eae5a82625a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:14:27 +0300 Subject: dbus-util: Trivial comment punctuation fix. --- src/pulsecore/dbus-util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 5443a4c1..9cee293c 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -91,8 +91,8 @@ int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); -/* Returns a new proplist, that the caller has to free. If the proplist can't - * be read from the iterator, an error reply is sent and NULL is returned. */ +/* Returns a new proplist that the caller has to free. If the proplist can't be + * read from the iterator, an error reply is sent and NULL is returned. */ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); #endif -- cgit From 7699cfd4c041eedd2dae124cac75da0879b87f1e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:18:03 +0300 Subject: dbus-protocol: Split some overly long lines. --- src/pulsecore/protocol-dbus.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 5cbd0d91..2461e4ba 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -199,19 +199,21 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_printf(buf, " \n", method_handler->method_name); for (i = 0; i < method_handler->n_arguments; ++i) - pa_strbuf_printf(buf, " \n", method_handler->arguments[i].name, - method_handler->arguments[i].type, - method_handler->arguments[i].direction); + pa_strbuf_printf(buf, " \n", + method_handler->arguments[i].name, + method_handler->arguments[i].type, + method_handler->arguments[i].direction); pa_strbuf_puts(buf, " \n"); } handlers_state = NULL; - while ((property_handler = pa_hashmap_iterate(iface_entry->property_handlers, &handlers_state, NULL))) - pa_strbuf_printf(buf, " \n", property_handler->property_name, - property_handler->type, - property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); + PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state) + pa_strbuf_printf(buf, " \n", + property_handler->property_name, + property_handler->type, + property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); for (i = 0; i < iface_entry->n_signals; ++i) { pa_strbuf_printf(buf, " \n", iface_entry->signals[i].name); -- cgit From 16dce8d7cbe4dcad56c3fbd1f47af6b50d2581a7 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:19:33 +0300 Subject: dbus-protocol: Take advantage of the helpers in dbus-util. --- src/pulsecore/protocol-dbus.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 2461e4ba..62f74a56 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -408,7 +409,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_dbus_method_handler *method_handler = NULL; pa_dbus_property_handler *property_handler = NULL; const char *attempted_property = NULL; - DBusMessage *reply = NULL; pa_assert(connection); pa_assert(message); @@ -427,10 +427,7 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - pa_assert_se((reply = dbus_message_new_method_return(message))); - pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); - + pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection); goto finish; } @@ -450,26 +447,23 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa case FOUND_GET_ALL: if (iface_entry->get_all_properties_cb) iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); + /* TODO: Write an else branch where a dummy response is sent. */ break; case PROPERTY_ACCESS_DENIED: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property); break; case NO_SUCH_METHOD: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)); break; case NO_SUCH_PROPERTY: - pa_assert_se((reply = dbus_message_new_error_printf(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property); break; case INVALID_MESSAGE_ARGUMENTS: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)); break; default: @@ -477,9 +471,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa } finish: - if (reply) - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; } -- cgit From 7cfda56af99b6d6bbb92fd6de208edd4c6991240 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 10 Aug 2009 10:38:01 +0300 Subject: dbus-protocol: Add a note for _send_signal that by default the signal isn't actually sent. --- src/pulsecore/protocol-dbus.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index c6b630a1..38ba8a18 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -149,6 +149,10 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c * do anything in that case either. */ void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal); +/* Sends the given signal to all interested clients. By default no signals are + * sent - clients have to explicitly to request signals by calling + * .Core1.ListenForSignal. That method's handler then calls + * pa_dbus_protocol_add_signal_listener(). */ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); /* Returns an array of extension identifier strings. The strings pointers point -- cgit From 31117fe99e07b11b55564a6df455211db712c2f3 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 10 Aug 2009 10:40:40 +0300 Subject: dbus-protocol: Fix signal sending for the case when the client doesn't listen for all signals. --- src/pulsecore/protocol-dbus.c | 11 ++++++++++- src/pulsecore/protocol-dbus.h | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 62f74a56..01285705 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -848,6 +848,8 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c for (i = 0; i < n_objects; ++i) pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL); + pa_hashmap_put(conn_entry->listening_signals, signal, object_set); + } else { conn_entry->listening_for_all_signals = TRUE; @@ -892,10 +894,15 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { void *state = NULL; pa_idxset *object_set; DBusMessage *signal_copy; + char *signal_string; pa_assert(p); pa_assert(signal); pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); + pa_assert_se(dbus_message_get_interface(signal)); + pa_assert_se(dbus_message_get_member(signal)); + + signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal), dbus_message_get_member(signal)); PA_HASHMAP_FOREACH(conn_entry, p->connections, state) { if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ @@ -903,7 +910,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { || pa_idxset_isempty(conn_entry->all_signals_objects))) || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */ - && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal)) + && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal_string)) && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL) || pa_idxset_isempty(object_set)))) { @@ -912,6 +919,8 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { dbus_message_unref(signal_copy); } } + + pa_xfree(signal_string); } const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index 38ba8a18..d771b4fc 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -129,7 +129,8 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection * pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); /* Enables signal receiving for the given connection. The connection must have - * been registered earlier. + * been registered earlier. The signal string must contain both the signal + * interface and the signal name, concatenated using a period as the separator. * * If the signal argument is NULL, all signals will be sent to the connection, * otherwise calling this function only adds the given signal to the list of -- cgit From 22ab1414507f84d6a42c5255d66ba15d8097d35f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:13:17 +0300 Subject: dbus-protocol: Use pa_hashmap_remove() instead of _get(). --- src/pulsecore/protocol-dbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 01285705..cf561c83 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -841,7 +841,7 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c conn_entry->listening_for_all_signals = FALSE; /* Replace the old object list with a new one. */ - if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal))) + if ((object_set = pa_hashmap_remove(conn_entry->listening_signals, signal))) pa_idxset_free(object_set, free_listened_object_name_cb, NULL); object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -- cgit From f48684e4dbc00d102ed17700fb693726a2676566 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 08:26:06 +0300 Subject: namereg: Revert default device handling back to the upstream version. --- src/pulsecore/namereg.c | 57 ++++++++----------------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index d7d83c5e..e26923d4 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -149,55 +149,21 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0); - if (type == PA_NAMEREG_SINK && !c->default_sink) - pa_namereg_set_default_sink(c, data); - else if (type == PA_NAMEREG_SOURCE && !c->default_source) - pa_namereg_set_default_source(c, data); - return e->name; } void pa_namereg_unregister(pa_core *c, const char *name) { struct namereg_entry *e; - uint32_t idx; pa_assert(c); pa_assert(name); pa_assert_se(e = pa_hashmap_remove(c->namereg, name)); - if (c->default_sink == e->data) { - pa_sink *new_default = NULL; - - /* FIXME: the selection here should be based priority values on - * the sinks */ - - PA_IDXSET_FOREACH(new_default, c->sinks, idx) { - if (new_default != e->data && PA_SINK_IS_LINKED(pa_sink_get_state(new_default))) - break; - } - - pa_namereg_set_default_sink(c, new_default); - - } else if (c->default_source == e->data) { - pa_source *new_default = NULL; - - /* First, try to find one that isn't a monitor */ - PA_IDXSET_FOREACH(new_default, c->sources, idx) { - if (new_default != e->data && !new_default->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default))) - break; - } - - if (!new_default) { - /* Then, fallback to a monitor */ - PA_IDXSET_FOREACH(new_default, c->sources, idx) { - if (new_default != e->data && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default))) - break; - } - } - - pa_namereg_set_default_source(c, new_default); - } + if (c->default_sink == e->data) + pa_namereg_set_default_sink(c, NULL); + else if (c->default_source == e->data) + pa_namereg_set_default_source(c, NULL); pa_xfree(e->name); pa_xfree(e); @@ -225,6 +191,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK))) return s->monitor_source; + } if (!name) @@ -281,18 +248,15 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { return s; } -/* XXX: After removing old functionality, has this function become useless? */ pa_sink *pa_namereg_get_default_sink(pa_core *c) { pa_sink *s; uint32_t idx; pa_assert(c); - if (!c->default_sink || PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) + if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) return c->default_sink; - /* The old default sink has become unlinked, set a new one. */ - /* FIXME: the selection here should be based priority values on * the sinks */ @@ -300,21 +264,18 @@ pa_sink *pa_namereg_get_default_sink(pa_core *c) { if (PA_SINK_IS_LINKED(pa_sink_get_state(s))) return pa_namereg_set_default_sink(c, s); - return pa_namereg_set_default_sink(c, NULL); + return NULL; } -/* XXX: After removing old functionality, has this function become useless? */ pa_source *pa_namereg_get_default_source(pa_core *c) { pa_source *s; uint32_t idx; pa_assert(c); - if (!c->default_source || PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) + if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) return c->default_source; - /* The old default source has become unlinked, set a new one. */ - /* First, try to find one that isn't a monitor */ PA_IDXSET_FOREACH(s, c->sources, idx) if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) @@ -325,5 +286,5 @@ pa_source *pa_namereg_get_default_source(pa_core *c) { if (PA_SOURCE_IS_LINKED(pa_source_get_state(s))) return pa_namereg_set_default_source(c, s); - return pa_namereg_set_default_source(c, NULL); + return NULL; } -- cgit From 7049b3c5bc351b9ea7ed932baa5bf7ccc1b67347 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:24:59 +0300 Subject: modargs: New function: pa_modargs_iterate(). --- src/pulsecore/modargs.c | 10 ++++++++++ src/pulsecore/modargs.h | 9 +++++++++ 2 files changed, 19 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index c7d734d9..e78cdb9a 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -415,3 +415,13 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa return 0; } + +const char *pa_modargs_iterate(pa_modargs *ma, void **state) { + pa_hashmap *map = (pa_hashmap*) ma; + struct entry *e; + + if (!(e = pa_hashmap_iterate(map, state, NULL))) + return NULL; + + return e->key; +} diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h index b3125b10..1ed66e9a 100644 --- a/src/pulsecore/modargs.h +++ b/src/pulsecore/modargs.h @@ -60,4 +60,13 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *s int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m); +/* Iterate through the module argument list. The user should allocate a + * state variable of type void* and initialize it with NULL. A pointer + * to this variable should then be passed to pa_modargs_iterate() + * which should be called in a loop until it returns NULL which + * signifies EOL. On each invication this function will return the + * key string for the next entry. The keys in the argument list do not + * have any particular order. */ +const char *pa_modargs_iterate(pa_modargs *ma, void **state); + #endif -- cgit From 57886ff34ac39dae709cb50cbc7fbbf817df534b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:26:13 +0300 Subject: dbus-protocol: Print a debug line whenever interfaces are unregistered. --- src/pulsecore/protocol-dbus.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index cf561c83..d1e19ff3 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -694,6 +694,8 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con update_introspection(obj_entry); + pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path); + pa_xfree(iface_entry->name); pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); -- cgit From 0e096632c53b746b9f4b4c0249d9e5a18c1c543d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 30 Aug 2009 19:52:22 +0300 Subject: dbus: Do message argument type checking early, centrally. --- src/pulsecore/dbus-util.c | 193 ++++-------------------- src/pulsecore/dbus-util.h | 42 +++--- src/pulsecore/protocol-dbus.c | 336 +++++++++++++++++++++++++++--------------- src/pulsecore/protocol-dbus.h | 26 +++- 4 files changed, 293 insertions(+), 304 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index b45e6a6c..e3700ea5 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -291,7 +291,10 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool return pconn; } -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusConnection *conn) { +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing( + pa_mainloop_api *m, + pa_bool_t use_rtclock, + DBusConnection *conn) { pa_dbus_wrap_connection *pconn; pa_assert(m); @@ -522,7 +525,10 @@ void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_t pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter)); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, + DBUS_TYPE_VARIANT, + signature_from_basic_type(type), + &variant_iter)); pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data)); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter)); pa_assert_se(dbus_connection_send(c, reply, NULL)); @@ -548,7 +554,12 @@ static unsigned basic_type_size(int type) { } } -void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) { +void pa_dbus_send_basic_array_variant_reply( + DBusConnection *c, + DBusMessage *in_reply_to, + int item_type, + void *array, + unsigned n) { DBusMessage *reply = NULL; DBusMessageIter msg_iter; @@ -641,7 +652,12 @@ void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const c pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } -void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) { +void pa_dbus_append_basic_array_variant_dict_entry( + DBusMessageIter *dict_iter, + const char *key, + int item_type, + const void *array, + unsigned n) { DBusMessageIter dict_entry_iter; pa_assert(dict_iter); @@ -711,156 +727,18 @@ void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, cons pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } -int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(c); - pa_assert(msg); - pa_assert(dbus_type_is_basic(type)); - pa_assert(data); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return -1; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return -1; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0) - return -1; - - return 0; -} - -int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) { - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(c); - pa_assert(msg); - pa_assert(dbus_type_is_fixed(item_type)); - pa_assert(data); - pa_assert(n); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return -1; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return -1; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0) - return -1; - - return 0; -} - -int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { - int arg_type; - - pa_assert(c); - pa_assert(msg); - pa_assert(iter); - pa_assert(dbus_type_is_basic(type)); - pa_assert(data); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != type) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type); - return -1; - } - - dbus_message_iter_get_basic(iter, data); - - dbus_message_iter_next(iter); - - return 0; -} - -int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { - DBusMessageIter array_iter; - int signed_n; - int arg_type; - int element_type; - - pa_assert(c); - pa_assert(msg); - pa_assert(iter); - pa_assert(dbus_type_is_fixed(item_type)); - pa_assert(array); - pa_assert(n); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type); - return -1; - } - - element_type = dbus_message_iter_get_element_type(iter); - if (element_type != item_type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type); - return -1; - } - - dbus_message_iter_recurse(iter, &array_iter); - - dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); - - dbus_message_iter_next(iter); - - pa_assert(signed_n >= 0); - - *n = signed_n; - - return 0; -} - pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { DBusMessageIter dict_iter; DBusMessageIter dict_entry_iter; - int arg_type; pa_proplist *proplist = NULL; - const char *key; - const uint8_t *value; - int value_length; + const char *key = NULL; + const uint8_t *value = NULL; + int value_length = 0; pa_assert(c); pa_assert(msg); pa_assert(iter); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); - return NULL; - } - - arg_type = dbus_message_iter_get_element_type(iter); - if (arg_type != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type); - return NULL; - } + pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a{say}")); proplist = pa_proplist_new(); @@ -869,32 +747,11 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_STRING) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); - goto fail; - } - dbus_message_iter_get_basic(&dict_entry_iter, &key); dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); - goto fail; - } - - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type); - goto fail; - } - - arg_type = dbus_message_iter_get_element_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_BYTE) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type); + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key); goto fail; } diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 9cee293c..f35e66cb 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -32,7 +32,10 @@ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error); -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusConnection *conn); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing( + pa_mainloop_api *mainloop, + pa_bool_t use_rtclock, + DBusConnection *conn); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); @@ -63,36 +66,41 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); void pa_dbus_free_pending_list(pa_dbus_pending **p); /* Sends an error message as the reply to the given message. */ -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); +void pa_dbus_send_error( + DBusConnection *c, + DBusMessage *in_reply_to, + const char *name, + const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); -void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); +void pa_dbus_send_basic_array_variant_reply( + DBusConnection *c, + DBusMessage *in_reply_to, + int item_type, + void *array, + unsigned n); void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist); void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); -void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); +void pa_dbus_append_basic_array_variant_dict_entry( + DBusMessageIter *dict_iter, + const char *key, + int item_type, + const void *array, + unsigned n); void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist); void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist); void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist); -/* Helper functions for extracting the value argument of a Set call. If the - * message is invalid, an error reply is sent and a negative number is - * returned. */ -int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); -int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n); - -/* If the arguments can't be read from the iterator, an error reply is sent and - * a negative number is returned. */ -int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); -int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); - -/* Returns a new proplist that the caller has to free. If the proplist can't be - * read from the iterator, an error reply is sent and NULL is returned. */ +/* Returns a new proplist that the caller has to free. If the proplist contains + * invalid keys, an error reply is sent and NULL is returned. The iterator must + * point to "a{say}" element. This function calls dbus_message_iter_next(iter) + * before returning. */ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); #endif diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index d1e19ff3..91022511 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -257,37 +257,90 @@ static void update_introspection(struct object_entry *oe) { oe->introspection = pa_strbuf_tostring_free(buf); } +/* Return value of find_handler() and its subfunctions. */ enum find_result_t { - FOUND_METHOD, + /* The received message is a valid .Get call. */ FOUND_GET_PROPERTY, + + /* The received message is a valid .Set call. */ FOUND_SET_PROPERTY, + + /* The received message is a valid .GetAll call. */ FOUND_GET_ALL, + + /* The received message is a valid method call. */ + FOUND_METHOD, + + /* The interface of the received message hasn't been registered for the + * destination object. */ + NO_SUCH_INTERFACE, + + /* No property handler was found for the received .Get or .Set call. */ + NO_SUCH_PROPERTY, + + /* The interface argument of a property call didn't match any registered + * interface. */ + NO_SUCH_PROPERTY_INTERFACE, + + /* The received message called .Get or .Set for a property whose access + * mode doesn't match the call. */ PROPERTY_ACCESS_DENIED, + + /* The new value signature of a .Set call didn't match the expexted + * signature. */ + INVALID_PROPERTY_SIG, + + /* No method handler was found for the received message. */ NO_SUCH_METHOD, - NO_SUCH_PROPERTY, - INVALID_MESSAGE_ARGUMENTS + + /* The signature of the received message didn't match the expected + * signature. Despite the name, this can also be returned for a property + * call if its message signature is invalid. */ + INVALID_METHOD_SIG }; -static enum find_result_t find_handler_by_property(struct object_entry *obj_entry, - DBusMessage *msg, - const char *property, - struct interface_entry **iface_entry, - pa_dbus_property_handler **property_handler) { +/* Data for resolving the correct reaction to a received message. */ +struct call_info { + DBusMessage *message; /* The received message. */ + struct object_entry *obj_entry; + const char *interface; /* Destination interface name (extracted from the message). */ + struct interface_entry *iface_entry; + + const char *property; /* Property name (extracted from the message). */ + const char *property_interface; /* The interface argument of a property call is stored here. */ + pa_dbus_property_handler *property_handler; + const char *expected_property_sig; /* Property signature from the introspection data. */ + const char *property_sig; /* The signature of the new value in the received .Set message. */ + DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */ + + const char *method; /* Method name (extracted from the message). */ + pa_dbus_method_handler *method_handler; + const char *expected_method_sig; /* Method signature from the introspection data. */ + const char *method_sig; /* The signature of the received message. */ +}; + +/* Called when call_info->property has been set and the property interface has + * not been given. In case of a Set call, call_info->property_sig is also set, + * which is checked against the expected value in this function. */ +static enum find_result_t find_handler_by_property(struct call_info *call_info) { void *state = NULL; - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(property); - pa_assert(iface_entry); - pa_assert(property_handler); - - PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { - if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { - if (dbus_message_has_member(msg, "Get")) - return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; - else if (dbus_message_has_member(msg, "Set")) - return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; - else + pa_assert(call_info); + + PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { + if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) { + if (pa_streq(call_info->method, "Get")) + return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; + + else if (pa_streq(call_info->method, "Set")) { + call_info->expected_property_sig = call_info->property_handler->type; + + if (pa_streq(call_info->property_sig, call_info->expected_property_sig)) + return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + else + return INVALID_PROPERTY_SIG; + + } else pa_assert_not_reached(); } } @@ -295,120 +348,138 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr return NO_SUCH_PROPERTY; } -static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, - const char *method, - struct interface_entry **iface_entry, - pa_dbus_method_handler **method_handler) { +static enum find_result_t find_handler_by_method(struct call_info *call_info) { void *state = NULL; - pa_assert(obj_entry); - pa_assert(method); - pa_assert(iface_entry); - pa_assert(method_handler); + pa_assert(call_info); - PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { - if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) + PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { + if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) return FOUND_METHOD; } return NO_SUCH_METHOD; } -static enum find_result_t find_handler_from_properties_call(struct object_entry *obj_entry, - DBusMessage *msg, - struct interface_entry **iface_entry, - pa_dbus_property_handler **property_handler, - const char **attempted_property) { - const char *interface; +static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) { + pa_assert(call_info); - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(iface_entry); + if (pa_streq(call_info->method, "GetAll")) { + call_info->expected_method_sig = "s"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; - if (dbus_message_has_member(msg, "GetAll")) { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; + pa_assert_se(dbus_message_get_args(call_info->message, NULL, + DBUS_TYPE_STRING, &call_info->property_interface, + DBUS_TYPE_INVALID)); - if (*interface) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) + if (*call_info->property_interface) { + if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) return FOUND_GET_ALL; - else { - return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ - } + else + return NO_SUCH_PROPERTY_INTERFACE; + } else { - pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); + pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces)); return FOUND_GET_ALL; } - } else { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, attempted_property, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; - - if (*interface) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && - (*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, *attempted_property))) { - if (dbus_message_has_member(msg, "Get")) - return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; - else if (dbus_message_has_member(msg, "Set")) - return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + + } else if (pa_streq(call_info->method, "Get")) { + call_info->expected_method_sig = "ss"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; + + pa_assert_se(dbus_message_get_args(call_info->message, NULL, + DBUS_TYPE_STRING, &call_info->property_interface, + DBUS_TYPE_STRING, &call_info->property, + DBUS_TYPE_INVALID)); + + if (*call_info->property_interface) { + if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) + return NO_SUCH_PROPERTY_INTERFACE; + else if ((call_info->property_handler = + pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) + return FOUND_GET_PROPERTY; + else + return NO_SUCH_PROPERTY; + + } else + return find_handler_by_property(call_info); + + } else if (pa_streq(call_info->method, "Set")) { + DBusMessageIter msg_iter; + + call_info->expected_method_sig = "ssv"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; + + pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter)); + + dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface); + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &call_info->property); + pa_assert_se(dbus_message_iter_next(&msg_iter)); + + dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter); + + call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter); + + if (*call_info->property_interface) { + if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) + return NO_SUCH_PROPERTY_INTERFACE; + + else if ((call_info->property_handler = + pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) { + call_info->expected_property_sig = call_info->property_handler->type; + + if (pa_streq(call_info->property_sig, call_info->expected_property_sig)) + return FOUND_SET_PROPERTY; else - pa_assert_not_reached(); + return INVALID_PROPERTY_SIG; + } else return NO_SUCH_PROPERTY; + } else - return find_handler_by_property(obj_entry, msg, *attempted_property, iface_entry, property_handler); - } -} + return find_handler_by_property(call_info); -static enum find_result_t find_handler(struct object_entry *obj_entry, - DBusMessage *msg, - struct interface_entry **iface_entry, - pa_dbus_method_handler **method_handler, - pa_dbus_property_handler **property_handler, - const char **attempted_property) { - const char *interface; + } else + pa_assert_not_reached(); +} - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(iface_entry); - pa_assert(method_handler); - pa_assert(property_handler); - pa_assert(attempted_property); +static enum find_result_t find_handler(struct call_info *call_info) { + pa_assert(call_info); - *iface_entry = NULL; - *method_handler = NULL; + if (call_info->interface) { + if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES)) + return find_handler_from_properties_call(call_info); - if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) - return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface))) + return NO_SUCH_INTERFACE; - else if ((interface = dbus_message_get_interface(msg))) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && - (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) + else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) return FOUND_METHOD; - else { - pa_log("Message has unknown interface or there's no method handler."); + + else return NO_SUCH_METHOD; - } } else { /* The method call doesn't contain an interface. */ - if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { - if (find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler) == FOUND_METHOD) - return FOUND_METHOD; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) { + if (find_handler_by_method(call_info) == FOUND_METHOD) + /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + return FOUND_METHOD; else /* Assume this is a .Properties call. */ - return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + return find_handler_from_properties_call(call_info); } else /* This is not a .Properties call. */ - return find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler); + return find_handler_by_method(call_info); } } static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { pa_dbus_protocol *p = user_data; - struct object_entry *obj_entry = NULL; - struct interface_entry *iface_entry = NULL; - pa_dbus_method_handler *method_handler = NULL; - pa_dbus_property_handler *property_handler = NULL; - const char *attempted_property = NULL; + struct call_info call_info; pa_assert(connection); pa_assert(message); @@ -423,49 +494,72 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa dbus_message_get_interface(message), dbus_message_get_member(message)); - pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); + call_info.message = message; + pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message))); + call_info.interface = dbus_message_get_interface(message); + pa_assert_se(call_info.method = dbus_message_get_member(message)); + pa_assert_se(call_info.method_sig = dbus_message_get_signature(message)); if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection); + pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection); goto finish; } - switch (find_handler(obj_entry, message, &iface_entry, &method_handler, &property_handler, &attempted_property)) { - case FOUND_METHOD: - method_handler->receive_cb(connection, message, iface_entry->userdata); - break; - + switch (find_handler(&call_info)) { case FOUND_GET_PROPERTY: - property_handler->get_cb(connection, message, iface_entry->userdata); + call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata); break; case FOUND_SET_PROPERTY: - property_handler->set_cb(connection, message, iface_entry->userdata); + call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata); + break; + + case FOUND_METHOD: + call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata); break; case FOUND_GET_ALL: - if (iface_entry->get_all_properties_cb) - iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); - /* TODO: Write an else branch where a dummy response is sent. */ + if (call_info.iface_entry->get_all_properties_cb) + call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata); + else { + DBusMessage *dummy_reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + + pa_assert_se(dummy_reply = dbus_message_new_method_return(message)); + dbus_message_iter_init_append(dummy_reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL)); + dbus_message_unref(dummy_reply); + } break; case PROPERTY_ACCESS_DENIED: - pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property); + pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, + "%s access denied for property %s", call_info.method, call_info.property); break; case NO_SUCH_METHOD: - pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)); + pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method); break; case NO_SUCH_PROPERTY: - pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property); + pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property); break; - case INVALID_MESSAGE_ARGUMENTS: - pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)); + case INVALID_METHOD_SIG: + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, + "Invalid signature for method %s: '%s'. Expected '%s'.", + call_info.method, call_info.method_sig, call_info.expected_method_sig); break; + case INVALID_PROPERTY_SIG: + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, + "Invalid signature for property %s: '%s'. Expected '%s'.", + call_info.property, call_info.property_sig, call_info.expected_property_sig); + default: pa_assert_not_reached(); } @@ -821,7 +915,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn return conn_entry->client; } -void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { +void pa_dbus_protocol_add_signal_listener( + pa_dbus_protocol *p, + DBusConnection *conn, + const char *signal, + char **objects, + unsigned n_objects) { struct connection_entry *conn_entry; pa_idxset *object_set; char *object_path; @@ -981,7 +1080,12 @@ int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) return 0; } -pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) { +pa_hook_slot *pa_dbus_protocol_hook_connect( + pa_dbus_protocol *p, + pa_dbus_protocol_hook_t hook, + pa_hook_priority_t prio, + pa_hook_cb_t cb, + void *data) { pa_assert(p); pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX); pa_assert(cb); diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index d771b4fc..6d100f7c 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -56,9 +56,19 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p); * message isn't a good idea; if you can't handle the message, reply with an * error. * + * The message signature is already checked against the introspection data, so + * you don't have to do that yourself. + * * All messages are method calls. */ typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata); +/* A specialized version of pa_dbus_receive_cb_t: the additional iterator + * argument points to the element inside the new value variant. + * + * The new value signature is checked against the introspection data, so you + * don't have to do that yourself. */ +typedef void (*pa_dbus_set_property_cb_t)(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); + typedef struct pa_dbus_arg_info { const char *name; const char *type; @@ -85,7 +95,7 @@ typedef struct pa_dbus_property_handler { /* The access mode for the property is determined by checking whether * get_cb or set_cb is NULL. */ pa_dbus_receive_cb_t get_cb; - pa_dbus_receive_cb_t set_cb; + pa_dbus_set_property_cb_t set_cb; } pa_dbus_property_handler; typedef struct pa_dbus_interface_info { @@ -140,7 +150,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn * only signals from the given objects are delivered. If this function is * called multiple time for the same connection and signal, the latest call * always replaces the previous object list. */ -void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects); +void pa_dbus_protocol_add_signal_listener( + pa_dbus_protocol *p, + DBusConnection *conn, + const char *signal, + char **objects, + unsigned n_objects); /* Disables the delivery of the signal for the given connection. The connection * must have been registered. If signal is NULL, all signals are disabled. If @@ -192,6 +207,11 @@ typedef enum pa_dbus_protocol_hook { PA_DBUS_PROTOCOL_HOOK_MAX } pa_dbus_protocol_hook_t; -pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); +pa_hook_slot *pa_dbus_protocol_hook_connect( + pa_dbus_protocol *p, + pa_dbus_protocol_hook_t hook, + pa_hook_priority_t prio, + pa_hook_cb_t cb, + void *data); #endif -- cgit From 587131917f9129c8347c789febb7e755dfb091de Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 31 Aug 2009 18:12:55 +0300 Subject: dbus-protocol: Implement argument type checking for normal methods. --- src/pulsecore/protocol-dbus.c | 44 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 91022511..5c1127be 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -72,6 +72,7 @@ struct connection_entry { struct interface_entry { char *name; pa_hashmap *method_handlers; + pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */ pa_hashmap *property_handlers; pa_dbus_receive_cb_t get_all_properties_cb; pa_dbus_signal_info *signals; @@ -354,8 +355,14 @@ static enum find_result_t find_handler_by_method(struct call_info *call_info) { pa_assert(call_info); PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { - if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) - return FOUND_METHOD; + if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) { + call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method); + + if (pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return FOUND_METHOD; + else + return INVALID_METHOD_SIG; + } } return NO_SUCH_METHOD; @@ -630,6 +637,31 @@ static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) { return handlers; } +static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) { + pa_hashmap *signatures = NULL; + pa_dbus_method_handler *handler = NULL; + void *state = NULL; + pa_strbuf *sig_buf = NULL; + unsigned i = 0; + + pa_assert(method_handlers); + + signatures = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + PA_HASHMAP_FOREACH(handler, method_handlers, state) { + sig_buf = pa_strbuf_new(); + + for (i = 0; i < handler->n_arguments; ++i) { + if (pa_streq(handler->arguments[i].direction, "in")) + pa_strbuf_puts(sig_buf, handler->arguments[i].type); + } + + pa_hashmap_put(signatures, handler->method_name, pa_strbuf_tostring_free(sig_buf)); + } + + return signatures; +} + static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) { pa_hashmap *handlers; unsigned i = 0; @@ -707,6 +739,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, iface_entry = pa_xnew(struct interface_entry, 1); iface_entry->name = pa_xstrdup(info->name); iface_entry->method_handlers = create_method_handlers(info); + iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers); iface_entry->property_handlers = create_property_handlers(info); iface_entry->get_all_properties_cb = info->get_all_properties_cb; iface_entry->signals = copy_signals(info); @@ -760,6 +793,12 @@ static void method_handler_free_cb(void *p, void *userdata) { pa_xfree((pa_dbus_arg_info *) h->arguments); } +static void method_signature_free_cb(void *p, void *userdata) { + pa_assert(p); + + pa_xfree(p); +} + static void property_handler_free_cb(void *p, void *userdata) { pa_dbus_property_handler *h = p; @@ -792,6 +831,7 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con pa_xfree(iface_entry->name); pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); + pa_hashmap_free(iface_entry->method_signatures, method_signature_free_cb, NULL); pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); for (i = 0; i < iface_entry->n_signals; ++i) { -- cgit