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/Makefile.am | 14 +- src/daemon/server-lookup.c | 4 +- src/modules/module-dbus-protocol.c | 575 ----------------------- 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 +++++++ 14 files changed, 1526 insertions(+), 1861 deletions(-) delete mode 100644 src/modules/module-dbus-protocol.c 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 diff --git a/src/Makefile.am b/src/Makefile.am index 58668890..47eb6479 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -852,7 +852,7 @@ endif if HAVE_DBUS libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += \ pulsecore/dbus-shared.c pulsecore/dbus-shared.h \ - pulsecore/dbus-common.c pulsecore/dbus-common.h + pulsecore/protocol-dbus.c pulsecore/protocol-dbus.h libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS) libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS) endif @@ -1225,7 +1225,7 @@ SYMDEF_FILES = \ modules/module-augment-properties-symdef.h \ modules/module-cork-music-on-phone-symdef.h \ modules/module-console-kit-symdef.h \ - modules/module-dbus-protocol-symdef.h + modules/dbus/module-dbus-protocol-symdef.h EXTRA_DIST += $(SYMDEF_FILES) BUILT_SOURCES += $(SYMDEF_FILES) @@ -1277,8 +1277,14 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMI # D-Bus protocol module_dbus_protocol_la_SOURCES = \ - pulsecore/dbus-objs/core.c pulsecore/dbus-objs/core.h \ - modules/module-dbus-protocol.c + modules/dbus/iface-card.c modules/dbus/iface-card.h \ + modules/dbus/iface-client.c modules/dbus/iface-client.h \ + modules/dbus/iface-core.c modules/dbus/iface-core.h \ + modules/dbus/iface-device.c modules/dbus/iface-device.h \ + modules/dbus/iface-module.c modules/dbus/iface-module.h \ + modules/dbus/iface-sample.c modules/dbus/iface-sample.h \ + modules/dbus/iface-stream.c modules/dbus/iface-stream.h \ + modules/dbus/module-dbus-protocol.c module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS) module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index ebacc099..45796e72 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -30,13 +30,13 @@ #include #include -#include #include #include +#include #include "server-lookup.h" -#define OBJECT_PATH "/org/pulseaudio1/server_lookup" +#define OBJECT_PATH "/org/pulseaudio/server_lookup1" #define INTERFACE "org.PulseAudio.ServerLookup1" struct pa_dbusobj_server_lookup { diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c deleted file mode 100644 index f7c1f0a9..00000000 --- a/src/modules/module-dbus-protocol.c +++ /dev/null @@ -1,575 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - Copyright 2006 Lennart Poettering - Copyright 2006 Shams E. King - - 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 -#include -#include -#include -#include - -#include - -#include "module-dbus-protocol-symdef.h" - -PA_MODULE_DESCRIPTION("D-Bus interface"); -PA_MODULE_USAGE( - "access=local|remote|local,remote " - "tcp_port="); -PA_MODULE_LOAD_ONCE(TRUE); -PA_MODULE_AUTHOR("Tanu Kaskinen"); -PA_MODULE_VERSION(PACKAGE_VERSION); - -#define CLEANUP_INTERVAL 10 /* seconds */ - -enum server_type { - SERVER_TYPE_LOCAL, - SERVER_TYPE_TCP -}; - -struct server; -struct connection; - -struct userdata { - pa_module *module; - pa_bool_t local_access; - pa_bool_t remote_access; - uint32_t tcp_port; - - struct server *local_server; - struct server *tcp_server; - - pa_idxset *connections; - - pa_time_event *cleanup_event; - - pa_dbusobj_core *core_object; -}; - -struct server { - struct userdata *userdata; - enum server_type type; - DBusServer *dbus_server; -}; - -struct connection { - struct server *server; - pa_dbus_wrap_connection *wrap_conn; - pa_client *client; -}; - -static const char* const valid_modargs[] = { - "access", - "tcp_port", - NULL -}; - -static void connection_free(struct connection *c) { - pa_assert(c); - - pa_assert_se(pa_dbus_unregister_connection(c->server->userdata->module->core, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0); - - pa_client_free(c->client); - pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL)); - pa_dbus_wrap_connection_free(c->wrap_conn); - pa_xfree(c); -} - -/* Called from pa_client_kill(). */ -static void client_kill_cb(pa_client *c) { - struct connection *conn; - - pa_assert(c); - pa_assert(c->userdata); - - conn = c->userdata; - connection_free(conn); - - pa_log_info("Connection killed."); -} - -static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) { - pa_log_debug("Allowing connection by user %lu.", uid); - - return TRUE; -} - -/* Called by D-Bus when a new client connection is received. */ -static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) { - struct server *s = data; - struct connection *c; - pa_client_new_data new_data; - pa_client *client; - - pa_assert(new_connection); - pa_assert(s); - - pa_client_new_data_init(&new_data); - new_data.module = s->userdata->module; - new_data.driver = __FILE__; - pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */ - client = pa_client_new(s->userdata->module->core, &new_data); - pa_client_new_data_done(&new_data); - - if (!client) { - dbus_connection_close(new_connection); - return; - } - - if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) { - /* FIXME: Here we allow anyone from anywhere to access the server, - * anonymously. Access control should be configurable. */ - dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL); - dbus_connection_set_allow_anonymous(new_connection, TRUE); - } - - c = pa_xnew(struct connection, 1); - c->server = s; - c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection); - c->client = client; - - c->client->kill = client_kill_cb; - c->client->send_event = NULL; /* TODO: Implement this. */ - c->client->userdata = c; - - pa_idxset_put(s->userdata->connections, c, NULL); - - pa_assert_se(pa_dbus_register_connection(s->userdata->module->core, new_connection) >= 0); -} - -/* Called by PA mainloop when a D-Bus fd watch event needs handling. */ -static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { - unsigned int flags = 0; - DBusWatch *watch = userdata; - -#if HAVE_DBUS_WATCH_GET_UNIX_FD - pa_assert(fd == dbus_watch_get_unix_fd(watch)); -#else - pa_assert(fd == dbus_watch_get_fd(watch)); -#endif - - if (!dbus_watch_get_enabled(watch)) { - pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd); - return; - } - - if (events & PA_IO_EVENT_INPUT) - flags |= DBUS_WATCH_READABLE; - if (events & PA_IO_EVENT_OUTPUT) - flags |= DBUS_WATCH_WRITABLE; - if (events & PA_IO_EVENT_HANGUP) - flags |= DBUS_WATCH_HANGUP; - if (events & PA_IO_EVENT_ERROR) - flags |= DBUS_WATCH_ERROR; - - dbus_watch_handle(watch, flags); -} - -/* Called by PA mainloop when a D-Bus timer event needs handling. */ -static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) { - DBusTimeout *timeout = userdata; - - if (dbus_timeout_get_enabled(timeout)) { - struct timeval next = *tv; - dbus_timeout_handle(timeout); - - /* restart it for the next scheduled time */ - pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - mainloop->time_restart(e, &next); - } -} - -/* Translates D-Bus fd watch event flags to PA IO event flags. */ -static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) { - unsigned int flags; - pa_io_event_flags_t events = 0; - - pa_assert(watch); - - flags = dbus_watch_get_flags(watch); - - /* no watch flags for disabled watches */ - if (!dbus_watch_get_enabled(watch)) - return PA_IO_EVENT_NULL; - - if (flags & DBUS_WATCH_READABLE) - events |= PA_IO_EVENT_INPUT; - if (flags & DBUS_WATCH_WRITABLE) - events |= PA_IO_EVENT_OUTPUT; - - return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR; -} - -/* Called by D-Bus when a D-Bus fd watch event is added. */ -static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) { - struct server *s = data; - pa_mainloop_api *mainloop; - pa_io_event *ev; - - pa_assert(watch); - pa_assert(s); - - mainloop = s->userdata->module->core->mainloop; - - ev = mainloop->io_new( - mainloop, -#if HAVE_DBUS_WATCH_GET_UNIX_FD - dbus_watch_get_unix_fd(watch), -#else - dbus_watch_get_fd(watch), -#endif - get_watch_flags(watch), io_event_cb, watch); - - dbus_watch_set_data(watch, ev, NULL); - - return TRUE; -} - -/* Called by D-Bus when a D-Bus fd watch event is removed. */ -static void watch_remove_cb(DBusWatch *watch, void *data) { - struct server *s = data; - pa_io_event *ev; - - pa_assert(watch); - pa_assert(s); - - if ((ev = dbus_watch_get_data(watch))) - s->userdata->module->core->mainloop->io_free(ev); -} - -/* Called by D-Bus when a D-Bus fd watch event is toggled. */ -static void watch_toggled_cb(DBusWatch *watch, void *data) { - struct server *s = data; - pa_io_event *ev; - - pa_assert(watch); - pa_assert(s); - - pa_assert_se(ev = dbus_watch_get_data(watch)); - - /* get_watch_flags() checks if the watch is enabled */ - s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch)); -} - -/* Called by D-Bus when a D-Bus timer event is added. */ -static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) { - struct server *s = data; - pa_mainloop_api *mainloop; - pa_time_event *ev; - struct timeval tv; - - pa_assert(timeout); - pa_assert(s); - - if (!dbus_timeout_get_enabled(timeout)) - return FALSE; - - mainloop = s->userdata->module->core->mainloop; - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - - ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout); - - dbus_timeout_set_data(timeout, ev, NULL); - - return TRUE; -} - -/* Called by D-Bus when a D-Bus timer event is removed. */ -static void timeout_remove_cb(DBusTimeout *timeout, void *data) { - struct server *s = data; - pa_time_event *ev; - - pa_assert(timeout); - pa_assert(s); - - if ((ev = dbus_timeout_get_data(timeout))) - s->userdata->module->core->mainloop->time_free(ev); -} - -/* Called by D-Bus when a D-Bus timer event is toggled. */ -static void timeout_toggled_cb(DBusTimeout *timeout, void *data) { - struct server *s = data; - pa_mainloop_api *mainloop; - pa_time_event *ev; - - pa_assert(timeout); - pa_assert(s); - - mainloop = s->userdata->module->core->mainloop; - - pa_assert_se(ev = dbus_timeout_get_data(timeout)); - - if (dbus_timeout_get_enabled(timeout)) { - struct timeval tv; - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - - mainloop->time_restart(ev, &tv); - } else - mainloop->time_restart(ev, NULL); -} - -static void server_free(struct server *s) { - pa_assert(s); - - if (s->dbus_server) { - dbus_server_disconnect(s->dbus_server); - dbus_server_unref(s->dbus_server); - } - - pa_xfree(s); -} - -static struct server *start_server(struct userdata *u, const char *address, enum server_type type) { - /* XXX: We assume that when we unref the DBusServer instance at module - * shutdown, nobody else holds any references to it. If we stop assuming - * that someday, dbus_server_set_new_connection_function, - * dbus_server_set_watch_functions and dbus_server_set_timeout_functions - * calls should probably register free callbacks, instead of providing NULL - * as they do now. */ - - struct server *s = NULL; - DBusError error; - - pa_assert(u); - pa_assert(address); - - dbus_error_init(&error); - - s = pa_xnew0(struct server, 1); - s->userdata = u; - s->dbus_server = dbus_server_listen(address, &error); - - if (dbus_error_is_set(&error)) { - pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message); - goto fail; - } - - dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL); - - if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) { - pa_log("dbus_server_set_watch_functions() ran out of memory."); - goto fail; - } - - if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) { - pa_log("dbus_server_set_timeout_functions() ran out of memory."); - goto fail; - } - - return s; - -fail: - if (s) - server_free(s); - - dbus_error_free(&error); - - return NULL; -} - -static struct server *start_local_server(struct userdata *u) { - struct server *s = NULL; - char *address = NULL; - - pa_assert(u); - - address = pa_get_dbus_address_from_server_type(u->module->core->server_type); - - s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */ - - pa_xfree(address); - - return s; -} - -static struct server *start_tcp_server(struct userdata *u) { - struct server *s = NULL; - char *address = NULL; - - pa_assert(u); - - address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port); - - s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */ - - pa_xfree(address); - - return s; -} - -static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) { - const char *value = NULL; - - pa_assert(ma); - pa_assert(local_access); - pa_assert(remote_access); - - if (!(value = pa_modargs_get_value(ma, "access", NULL))) - return 0; - - if (!strcmp(value, "local")) { - *local_access = TRUE; - *remote_access = FALSE; - } else if (!strcmp(value, "remote")) { - *local_access = FALSE; - *remote_access = TRUE; - } else if (!strcmp(value, "local,remote")) { - *local_access = TRUE; - *remote_access = TRUE; - } else - return -1; - - return 0; -} - -/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */ -static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { - struct userdata *u = userdata; - struct connection *conn = NULL; - uint32_t idx; - struct timeval cleanup_timeval; - unsigned free_count = 0; - - for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) { - if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) { - connection_free(conn); - ++free_count; - } - } - - if (free_count > 0) - pa_log_debug("Freed %u dead D-Bus client connections.", free_count); - - pa_gettimeofday(&cleanup_timeval); - cleanup_timeval.tv_sec += CLEANUP_INTERVAL; - u->module->core->mainloop->time_restart(e, &cleanup_timeval); -} - -int pa__init(pa_module *m) { - struct userdata *u = NULL; - pa_modargs *ma = NULL; - struct timeval cleanup_timeval; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments."); - goto fail; - } - - m->userdata = u = pa_xnew0(struct userdata, 1); - u->module = m; - u->local_access = TRUE; - u->remote_access = FALSE; - u->tcp_port = PA_DBUS_DEFAULT_PORT; - - if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) { - pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL)); - goto fail; - } - - if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) { - pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL)); - goto fail; - } - - if (u->local_access && !(u->local_server = start_local_server(u))) { - pa_log("Starting the local D-Bus server failed."); - goto fail; - } - - if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) { - pa_log("Starting the D-Bus server for remote connections failed."); - goto fail; - } - - u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - - pa_gettimeofday(&cleanup_timeval); - cleanup_timeval.tv_sec += CLEANUP_INTERVAL; - u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u); - - u->core_object = pa_dbusobj_core_new(m->core); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(m); - - return -1; -} - -/* Called by idxset when the connection set is freed. */ -static void connection_free_cb(void *p, void *userdata) { - struct connection *conn = p; - - pa_assert(conn); - - connection_free(conn); -} - -void pa__done(pa_module *m) { - struct userdata *u; - - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->core_object) - pa_dbusobj_core_free(u->core_object); - - if (u->cleanup_event) - m->core->mainloop->time_free(u->cleanup_event); - - if (u->connections) - pa_idxset_free(u->connections, connection_free_cb, NULL); - - if (u->tcp_server) - server_free(u->tcp_server); - - if (u->local_server) - server_free(u->local_server); - - pa_xfree(u); - m->userdata = NULL; -} 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