From c94e7421aad58c7714f6e26f20642c51af17cc4d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 3 Jun 2009 15:21:50 +0300 Subject: Create module-dbus-protocol with "Hello, world!" functionality. --- src/Makefile.am | 13 ++++++-- src/daemon/default.pa.in | 3 ++ src/daemon/system.pa.in | 3 ++ src/modules/module-dbus-protocol.c | 66 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/modules/module-dbus-protocol.c diff --git a/src/Makefile.am b/src/Makefile.am index a7ec6917..70b16be3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1072,7 +1072,8 @@ endif if HAVE_DBUS modlibexec_LTLIBRARIES += \ - module-rygel-media-server.la + module-rygel-media-server.la \ + module-dbus-protocol.la endif if HAVE_BLUEZ @@ -1164,7 +1165,8 @@ SYMDEF_FILES = \ modules/module-position-event-sounds-symdef.h \ modules/module-augment-properties-symdef.h \ modules/module-cork-music-on-phone-symdef.h \ - modules/module-console-kit-symdef.h + modules/module-console-kit-symdef.h \ + modules/module-dbus-protocol-symdef.h EXTRA_DIST += $(SYMDEF_FILES) BUILT_SOURCES += $(SYMDEF_FILES) @@ -1213,6 +1215,13 @@ module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(A module_http_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS) module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +# D-Bus protocol + +module_dbus_protocol_la_SOURCES = modules/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 + # Native protocol module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index fa0683e1..ba8e3c80 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -66,6 +66,9 @@ load-module module-bluetooth-discover .ifexists module-esound-protocol-unix@PA_SOEXT@ load-module module-esound-protocol-unix .endif +.ifexists module-dbus-protocol@PA_SOEXT@ +load-module module-dbus-protocol +.endif load-module module-native-protocol-unix ### Network access (may be configured with paprefs, so leave this commented diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in index 27e42815..5541bbe4 100755 --- a/src/daemon/system.pa.in +++ b/src/daemon/system.pa.in @@ -32,6 +32,9 @@ load-module module-detect .ifexists module-esound-protocol-unix@PA_SOEXT@ load-module module-esound-protocol-unix .endif +.ifexists module-dbus-protocol@PA_SOEXT@ +load-module module-dbus-protocol +.endif load-module module-native-protocol-unix ### Automatically restore the volume of streams and devices diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c new file mode 100644 index 00000000..077f4178 --- /dev/null +++ b/src/modules/module-dbus-protocol.c @@ -0,0 +1,66 @@ +/*** + 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 "module-dbus-protocol-symdef.h" + +PA_MODULE_DESCRIPTION("D-Bus interface"); +PA_MODULE_USAGE(""); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_AUTHOR("Tanu Kaskinen"); +PA_MODULE_VERSION(PACKAGE_VERSION); + +struct userdata { + pa_module *module; +}; + +int pa__init(pa_module *m) { + struct userdata *u = NULL; + + pa_assert(m); + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->module = m; + + pa_log_notice("Hello, world!"); + + return 0; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + pa_xfree(u); + m->userdata = NULL; +} -- cgit From 5babbaafb26ac4f83db0d8bca53006a843472b8f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 12 Jun 2009 07:16:05 +0300 Subject: daemon: Implement the DBus server lookup service. --- src/Makefile.am | 5 +- src/daemon/daemon-conf.c | 48 ++++++++ src/daemon/daemon-conf.h | 9 ++ src/daemon/daemon.conf.in | 1 + src/daemon/main.c | 136 ++++++++++++++++------ src/daemon/server-lookup.c | 277 +++++++++++++++++++++++++++++++++++++++++++++ src/daemon/server-lookup.h | 42 +++++++ src/pulse/client-conf.c | 3 + src/pulse/client-conf.h | 3 +- src/pulse/client.conf.in | 1 + 10 files changed, 489 insertions(+), 36 deletions(-) create mode 100644 src/daemon/server-lookup.c create mode 100644 src/daemon/server-lookup.h diff --git a/src/Makefile.am b/src/Makefile.am index 70b16be3..c56f7608 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -135,13 +135,14 @@ BUILT_SOURCES = \ bin_PROGRAMS = pulseaudio pulseaudio_SOURCES = \ - daemon/caps.h daemon/caps.c \ + daemon/caps.c daemon/caps.h \ daemon/cmdline.c daemon/cmdline.h \ daemon/cpulimit.c daemon/cpulimit.h \ daemon/daemon-conf.c daemon/daemon-conf.h \ daemon/dumpmodules.c daemon/dumpmodules.h \ daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \ - daemon/main.c + daemon/main.c \ + daemon/server-lookup.c daemon/server-lookup.h pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS) pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS) diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index ac6cc8aa..e6fa8c64 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -83,6 +83,9 @@ static const pa_daemon_conf default_conf = { .config_file = NULL, .use_pid_file = TRUE, .system_instance = FALSE, +#ifdef HAVE_DBUS + .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */ +#endif .no_cpu_limit = FALSE, .disable_shm = FALSE, .default_n_fragments = 4, @@ -203,6 +206,22 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) { return 0; } +int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) { + pa_assert(c); + pa_assert(string); + + if (!strcmp(string, "user")) + c->local_server_type = PA_SERVER_TYPE_USER; + else if (!strcmp(string, "system")) { + c->local_server_type = PA_SERVER_TYPE_SYSTEM; + } else if (!strcmp(string, "none")) { + c->local_server_type = PA_SERVER_TYPE_NONE; + } else + return -1; + + return 0; +} + static int parse_log_target(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { pa_daemon_conf *c = data; @@ -430,6 +449,22 @@ static int parse_rtprio(const char *filename, unsigned line, const char *section return 0; } +static int parse_server_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { + pa_daemon_conf *c = data; + + pa_assert(filename); + pa_assert(lvalue); + pa_assert(rvalue); + pa_assert(data); + + if (pa_daemon_conf_set_local_server_type(c, rvalue) < 0) { + pa_log(_("[%s:%u] Invalid server type '%s'."), filename, line, rvalue); + return -1; + } + + return 0; +} + int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { int r = -1; FILE *f = NULL; @@ -443,6 +478,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { { "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL }, { "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL }, { "system-instance", pa_config_parse_bool, &c->system_instance, NULL }, +#ifdef HAVE_DBUS + { "local-server-type", parse_server_type, c, NULL }, +#endif { "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL }, { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL }, { "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL }, @@ -604,6 +642,13 @@ static const char* const log_level_to_string[] = { [PA_LOG_ERROR] = "error" }; +static const char* const server_type_to_string[] = { + [PA_SERVER_TYPE_UNSET] = "!!UNSET!!", + [PA_SERVER_TYPE_USER] = "user", + [PA_SERVER_TYPE_SYSTEM] = "system", + [PA_SERVER_TYPE_NONE] = "none" +}; + char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf *s; char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; @@ -627,6 +672,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf_printf(s, "disallow-exit = %s\n", pa_yes_no(c->disallow_exit)); pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file)); pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance)); +#ifdef HAVE_DBUS + pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]); +#endif pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit)); pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm)); pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes)); diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 9cec189f..98db8641 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -48,6 +48,13 @@ typedef enum pa_daemon_conf_cmd { PA_CMD_CLEANUP_SHM } pa_daemon_conf_cmd_t; +typedef enum pa_daemon_conf_server_type { + PA_SERVER_TYPE_UNSET, + PA_SERVER_TYPE_USER, + PA_SERVER_TYPE_SYSTEM, + PA_SERVER_TYPE_NONE +} pa_daemon_conf_server_type_t; + #ifdef HAVE_SYS_RESOURCE_H typedef struct pa_rlimit { rlim_t value; @@ -74,6 +81,7 @@ typedef struct pa_daemon_conf { log_meta, log_time, flat_volumes; + pa_daemon_conf_server_type_t local_server_type; int exit_idle_time, scache_idle_time, auto_log_target, @@ -151,6 +159,7 @@ int pa_daemon_conf_env(pa_daemon_conf *c); int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string); int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string); int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string); +int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string); const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c); FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c); diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index fcd2513a..ecdb3a64 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -25,6 +25,7 @@ ; disallow-exit = no ; use-pid-file = yes ; system-instance = no +; local-server-type = user ; disable-shm = no ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB diff --git a/src/daemon/main.c b/src/daemon/main.c index 3e50baad..1b6ed793 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -99,6 +99,7 @@ #include "caps.h" #include "ltdl-bind-now.h" #include "polkit.h" +#include "server-lookup.h" #ifdef HAVE_LIBWRAP /* Only one instance of these variables */ @@ -335,33 +336,31 @@ static void set_all_rlimits(const pa_daemon_conf *conf) { #endif #ifdef HAVE_DBUS -static pa_dbus_connection *register_dbus(pa_core *c) { +static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) { DBusError error; pa_dbus_connection *conn; dbus_error_init(&error); - if (!(conn = pa_dbus_bus_get(c, pa_in_system_mode() ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { + if (!(conn = pa_dbus_bus_get(c, bus, &error)) || dbus_error_is_set(&error)) { pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message); goto fail; } - if (dbus_bus_request_name(pa_dbus_connection_get(conn), "org.pulseaudio.Server", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - pa_log_debug("Got org.pulseaudio.Server!"); + if (dbus_bus_request_name(pa_dbus_connection_get(conn), name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + pa_log_debug("Got %s!", name); return conn; } if (dbus_error_is_set(&error)) - pa_log_warn("Failed to acquire org.pulseaudio.Server: %s: %s", error.name, error.message); + pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message); else - pa_log_warn("D-Bus name org.pulseaudio.Server already taken. Weird shit!"); + pa_log_error("D-Bus name %s already taken. Weird shit!", name); /* PA cannot be started twice by the same user and hence we can - * ignore mostly the case that org.pulseaudio.Server is already - * taken. */ + * ignore mostly the case that a name is already taken. */ fail: - if (conn) pa_dbus_connection_unref(conn); @@ -393,7 +392,10 @@ int main(int argc, char *argv[]) { int autospawn_fd = -1; pa_bool_t autospawn_locked = FALSE; #ifdef HAVE_DBUS - pa_dbus_connection *dbus = NULL; + pa_dbusobj_server_lookup *server_lookup = NULL; /* /org/pulseaudio/server_lookup */ + pa_dbus_connection *lookup_service_bus = NULL; /* Always the user bus. */ + pa_dbus_connection *server_bus = NULL; /* The bus where we reserve org.pulseaudio.Server, either the user or the system bus. */ + pa_bool_t start_server; #endif pa_log_set_ident("pulseaudio"); @@ -486,8 +488,45 @@ int main(int argc, char *argv[]) { pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET); pa_log_set_show_backtrace(conf->log_backtrace); +#ifdef HAVE_DBUS + /* conf->system_instance and conf->local_server_type control almost the + * same thing; make them agree about what is requested. */ + switch (conf->local_server_type) { + case PA_SERVER_TYPE_UNSET: + conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER; + break; + case PA_SERVER_TYPE_USER: + case PA_SERVER_TYPE_NONE: + conf->system_instance = FALSE; + break; + case PA_SERVER_TYPE_SYSTEM: + conf->system_instance = TRUE; + break; + default: + pa_assert_not_reached(); + } + + start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (real_root && conf->local_server_type == PA_SERVER_TYPE_SYSTEM); + + if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) { + pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service.")); + conf->system_instance = FALSE; + } +#endif + pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root)); +#ifdef HAVE_DBUS + /* XXX: Uhh, goto programming... as if this wasn't hard enough to follow + * already. But if we won't start the full server, we want to just skip all + * the capability stuff. */ + if (!start_server) { + if (!real_root && pa_have_caps()) + pa_drop_caps(); + goto after_caps_setup; + } +#endif + if (!real_root && pa_have_caps()) { #ifdef HAVE_SYS_RESOURCE_H struct rlimit rl; @@ -628,6 +667,10 @@ int main(int argc, char *argv[]) { conf->realtime_scheduling = FALSE; } +#ifdef HAVE_DBUS +after_caps_setup: +#endif + pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority())); LTDL_SET_PRELOADED_SYMBOLS(); @@ -716,10 +759,12 @@ int main(int argc, char *argv[]) { if (real_root && !conf->system_instance) pa_log_warn(_("This program is not intended to be run as root (unless --system is specified).")); +#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */ else if (!real_root && conf->system_instance) { pa_log(_("Root privileges required.")); goto finish; } +#endif if (conf->cmd == PA_CMD_START && conf->system_instance) { pa_log(_("--start not supported for system instances.")); @@ -1007,34 +1052,47 @@ int main(int argc, char *argv[]) { pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0); buf = pa_strbuf_new(); - if (conf->load_default_script_file) { - FILE *f; - if ((f = pa_daemon_conf_open_default_script_file(conf))) { - r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail); - fclose(f); +#ifdef HAVE_DBUS + if (start_server) { +#endif + if (conf->load_default_script_file) { + FILE *f; + + if ((f = pa_daemon_conf_open_default_script_file(conf))) { + r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail); + fclose(f); + } } - } - if (r >= 0) - r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); + if (r >= 0) + r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); - pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); - pa_xfree(s); + pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); + pa_xfree(s); - /* We completed the initial module loading, so let's disable it - * from now on, if requested */ - c->disallow_module_loading = !!conf->disallow_module_loading; + if (r < 0 && conf->fail) { + pa_log(_("Failed to initialize daemon.")); + goto finish; + } - if (r < 0 && conf->fail) { - pa_log(_("Failed to initialize daemon.")); - goto finish; + if (!c->modules || pa_idxset_size(c->modules) == 0) { + pa_log(_("Daemon startup without any loaded modules, refusing to work.")); + goto finish; + } +#ifdef HAVE_DBUS + } else { + /* When we just provide the D-Bus server lookup service, we don't want + * any modules to be loaded. We haven't loaded any so far, so one might + * think there's no way to contact the server, but receiving certain + * signals could still cause modules to load. */ + conf->disallow_module_loading = TRUE; } +#endif - if (!c->modules || pa_idxset_size(c->modules) == 0) { - pa_log(_("Daemon startup without any loaded modules, refusing to work.")); - goto finish; - } + /* We completed the initial module loading, so let's disable it + * from now on, if requested */ + c->disallow_module_loading = !!conf->disallow_module_loading; #ifdef HAVE_FORK if (daemon_pipe[1] >= 0) { @@ -1046,7 +1104,15 @@ int main(int argc, char *argv[]) { #endif #ifdef HAVE_DBUS - dbus = register_dbus(c); + if (!conf->system_instance) { + if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type))) + goto finish; + if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio"))) + goto finish; + } + + if (start_server && !(server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server"))) + goto finish; #endif pa_log_info(_("Daemon startup complete.")); @@ -1059,8 +1125,12 @@ int main(int argc, char *argv[]) { finish: #ifdef HAVE_DBUS - if (dbus) - pa_dbus_connection_unref(dbus); + if (server_bus) + pa_dbus_connection_unref(server_bus); + if (lookup_service_bus) + pa_dbus_connection_unref(lookup_service_bus); + if (server_lookup) + pa_dbusobj_server_lookup_free(server_lookup); #endif if (autospawn_fd >= 0) { diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c new file mode 100644 index 00000000..867c3a1c --- /dev/null +++ b/src/daemon/server-lookup.c @@ -0,0 +1,277 @@ +/*** + 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 "server-lookup.h" + +struct pa_dbusobj_server_lookup { + pa_dbus_connection *conn; + pa_bool_t path_registered; + pa_daemon_conf_server_type_t server_type; +}; + +static const char introspection[] = + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "" + " \n" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static void unregister_cb(DBusConnection *conn, void *user_data) { + pa_dbusobj_server_lookup *sl = user_data; + + pa_assert(sl); + pa_assert(sl->path_registered); + + sl->path_registered = FALSE; +} + +static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + const char *i = introspection; + DBusMessage *reply = NULL; + + pa_assert(conn); + pa_assert(msg); + + if (!(reply = dbus_message_new_method_return(msg))) + goto fail; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +/* Caller frees the string. */ +static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) { + char *server_string = NULL; + char *runtime_dir = NULL; + + switch (server_type) { + case PA_SERVER_TYPE_USER: + runtime_dir = pa_get_runtime_dir(); + + if (!runtime_dir) + return NULL; + + server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir); + break; + + case PA_SERVER_TYPE_SYSTEM: + server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket"); + break; + + case PA_SERVER_TYPE_NONE: + server_string = pa_xnew0(char, 1); + break; + + default: + pa_assert_not_reached(); + } + + return server_string; +} + +static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusMessage *reply = NULL; + pa_client_conf *conf = NULL; + char *server_string = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + conf = pa_client_conf_new(); + + if (pa_client_conf_load(conf, NULL) < 0) { + if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) + goto fail; + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + return DBUS_HANDLER_RESULT_HANDLED; + } + + server_string = pa_xstrdup(conf->default_dbus_server); + + pa_client_conf_free(conf); + + if (!server_string) { + if (!(server_string = get_dbus_server_from_type(sl->server_type))) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed."))) + goto fail; + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + if (!(reply = dbus_message_new_method_return(msg))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + + pa_log("Sent reply with server_string '%s'.", server_string); + + pa_xfree(server_string); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + if (conf) + pa_client_conf_free(conf); + + pa_xfree(server_string); + + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (conf) + pa_client_conf_free(conf); + + pa_xfree(server_string); + + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) { + pa_dbusobj_server_lookup *sl = user_data; + + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + /* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */ + + if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) + return handle_introspect(conn, msg, sl); + + if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers")) + return handle_get_dbus_servers(conn, msg, sl); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusObjectPathVTable vtable = { + .unregister_function = unregister_cb, + .message_function = message_cb, + .dbus_internal_pad1 = NULL, + .dbus_internal_pad2 = NULL, + .dbus_internal_pad3 = NULL, + .dbus_internal_pad4 = NULL +}; + +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type) { + pa_dbusobj_server_lookup *sl; + DBusError error; + + dbus_error_init(&error); + + sl = pa_xnew(pa_dbusobj_server_lookup, 1); + sl->path_registered = FALSE; + sl->server_type = server_type; + + if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { + pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message); + goto fail; + } + + if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup", &vtable, sl)) { + pa_log("dbus_connection_register_object_path() failed for /org/pulseaudio/server_lookup."); + goto fail; + } + + sl->path_registered = TRUE; + + return sl; + +fail: + dbus_error_free(&error); + + pa_dbusobj_server_lookup_free(sl); + + return NULL; +} + +void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) { + pa_assert(sl); + + if (sl->path_registered) { + pa_assert(sl->conn); + if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup")) + pa_log_debug("dbus_connection_unregister_object_path() failed for /org/pulseaudio/server_lookup."); + } + + if (sl->conn) + pa_dbus_connection_unref(sl->conn); + + pa_xfree(sl); +} \ No newline at end of file diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h new file mode 100644 index 00000000..69fdacdd --- /dev/null +++ b/src/daemon/server-lookup.h @@ -0,0 +1,42 @@ +#ifndef fooserverlookuphfoo +#define fooserverlookuphfoo + +/*** + 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/server_lookup. Implemented interfaces + * are org.pulseaudio.ServerLookup and org.freedesktop.DBus.Introspectable. + * + * See http://pulseaudio.org/wiki/DBusInterface for the ServerLookup interface + * documentation. + */ + +#include + +#include "daemon-conf.h" + +typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup; + +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type); +void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl); + +#endif \ No newline at end of file diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 940d0b67..8eab1094 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -57,6 +57,7 @@ static const pa_client_conf default_conf = { .default_sink = NULL, .default_source = NULL, .default_server = NULL, + .default_dbus_server = NULL, .autospawn = TRUE, .disable_shm = FALSE, .cookie_file = NULL, @@ -81,6 +82,7 @@ void pa_client_conf_free(pa_client_conf *c) { pa_xfree(c->default_sink); pa_xfree(c->default_source); pa_xfree(c->default_server); + pa_xfree(c->default_dbus_server); pa_xfree(c->cookie_file); pa_xfree(c); } @@ -97,6 +99,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { { "default-sink", pa_config_parse_string, &c->default_sink, NULL }, { "default-source", pa_config_parse_string, &c->default_source, NULL }, { "default-server", pa_config_parse_string, &c->default_server, NULL }, + { "default-dbus-server", pa_config_parse_string, &c->default_dbus_server, NULL }, { "autospawn", pa_config_parse_bool, &c->autospawn, NULL }, { "cookie-file", pa_config_parse_string, &c->cookie_file, NULL }, { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL }, diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index ab97dc6a..618216f4 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -22,12 +22,13 @@ USA. ***/ +#include #include /* A structure containing configuration data for PulseAudio clients. */ typedef struct pa_client_conf { - char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; + char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *default_dbus_server, *cookie_file; pa_bool_t autospawn, disable_shm; uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; pa_bool_t cookie_valid; /* non-zero, when cookie is valid */ diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index 579bcc20..3340b0b2 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -22,6 +22,7 @@ ; default-sink = ; default-source = ; default-server = +; default-dbus-server = ; autospawn = yes ; daemon-binary = @PA_BINARY@ -- cgit From c8d819a5adbe32e14d7f03a252bca6f7df01d795 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 16 Jun 2009 19:03:22 +0300 Subject: dbus-protocol: Connection handling for local connections. --- src/Makefile.am | 2 + src/daemon/daemon-conf.h | 10 +- src/daemon/main.c | 3 +- src/daemon/server-lookup.c | 69 ++---- src/daemon/server-lookup.h | 6 +- src/modules/module-dbus-protocol.c | 470 ++++++++++++++++++++++++++++++++++++- src/pulsecore/core.h | 9 + src/pulsecore/dbus-common.c | 73 ++++++ src/pulsecore/dbus-common.h | 39 +++ src/pulsecore/dbus-util.c | 21 ++ src/pulsecore/dbus-util.h | 1 + 11 files changed, 639 insertions(+), 64 deletions(-) create mode 100644 src/pulsecore/dbus-common.c create mode 100644 src/pulsecore/dbus-common.h diff --git a/src/Makefile.am b/src/Makefile.am index c56f7608..5302bc22 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -783,6 +783,7 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/core-scache.c pulsecore/core-scache.h \ pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ pulsecore/core.c pulsecore/core.h \ + pulsecore/dbus-common.c pulsecore/dbus-common.h \ pulsecore/envelope.c pulsecore/envelope.h \ pulsecore/fdsem.c pulsecore/fdsem.h \ pulsecore/g711.c pulsecore/g711.h \ @@ -796,6 +797,7 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/object.c pulsecore/object.h \ pulsecore/play-memblockq.c pulsecore/play-memblockq.h \ pulsecore/play-memchunk.c pulsecore/play-memchunk.h \ + pulsecore/protocol-dbus.h \ pulsecore/resampler.c pulsecore/resampler.h \ pulsecore/rtpoll.c pulsecore/rtpoll.h \ pulsecore/rtsig.c pulsecore/rtsig.h \ diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 98db8641..c4f78cbe 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -28,6 +28,7 @@ #include #include +#include #include #ifdef HAVE_SYS_RESOURCE_H @@ -48,13 +49,6 @@ typedef enum pa_daemon_conf_cmd { PA_CMD_CLEANUP_SHM } pa_daemon_conf_cmd_t; -typedef enum pa_daemon_conf_server_type { - PA_SERVER_TYPE_UNSET, - PA_SERVER_TYPE_USER, - PA_SERVER_TYPE_SYSTEM, - PA_SERVER_TYPE_NONE -} pa_daemon_conf_server_type_t; - #ifdef HAVE_SYS_RESOURCE_H typedef struct pa_rlimit { rlim_t value; @@ -81,7 +75,7 @@ typedef struct pa_daemon_conf { log_meta, log_time, flat_volumes; - pa_daemon_conf_server_type_t local_server_type; + pa_server_type_t local_server_type; int exit_idle_time, scache_idle_time, auto_log_target, diff --git a/src/daemon/main.c b/src/daemon/main.c index 1b6ed793..62214a56 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -1028,6 +1028,7 @@ after_caps_setup: c->running_as_daemon = !!conf->daemonize; c->disallow_exit = conf->disallow_exit; c->flat_volumes = conf->flat_volumes; + c->server_type = conf->local_server_type; pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0); pa_signal_new(SIGINT, signal_callback, c); @@ -1105,7 +1106,7 @@ after_caps_setup: #ifdef HAVE_DBUS if (!conf->system_instance) { - if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type))) + if (!(server_lookup = pa_dbusobj_server_lookup_new(c))) goto finish; if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio"))) goto finish; diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 867c3a1c..b53fda76 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -28,16 +28,18 @@ #include #include +#include #include +#include #include #include #include "server-lookup.h" struct pa_dbusobj_server_lookup { + pa_core *core; pa_dbus_connection *conn; pa_bool_t path_registered; - pa_daemon_conf_server_type_t server_type; }; static const char introspection[] = @@ -46,7 +48,7 @@ static const char introspection[] = " \n" " " - " " + " " " " " " " " @@ -99,40 +101,10 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } -/* Caller frees the string. */ -static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) { - char *server_string = NULL; - char *runtime_dir = NULL; - - switch (server_type) { - case PA_SERVER_TYPE_USER: - runtime_dir = pa_get_runtime_dir(); - - if (!runtime_dir) - return NULL; - - server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir); - break; - - case PA_SERVER_TYPE_SYSTEM: - server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket"); - break; - - case PA_SERVER_TYPE_NONE: - server_string = pa_xnew0(char, 1); - break; - - default: - pa_assert_not_reached(); - } - - return server_string; -} - -static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { +static DBusHandlerResult handle_get_dbus_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { DBusMessage *reply = NULL; pa_client_conf *conf = NULL; - char *server_string = NULL; + char *address = NULL; pa_assert(conn); pa_assert(msg); @@ -148,12 +120,13 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa return DBUS_HANDLER_RESULT_HANDLED; } - server_string = pa_xstrdup(conf->default_dbus_server); - pa_client_conf_free(conf); - if (!server_string) { - if (!(server_string = get_dbus_server_from_type(sl->server_type))) { + if (conf->default_dbus_server) { + if (!(address = dbus_address_escape_value(conf->default_dbus_server))) + goto oom; + } else { + if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) { if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed."))) goto fail; if (!dbus_connection_send(conn, reply, NULL)) @@ -165,15 +138,15 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa if (!(reply = dbus_message_new_method_return(msg))) goto oom; - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID)) + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) goto fail; if (!dbus_connection_send(conn, reply, NULL)) goto oom; - pa_log("Sent reply with server_string '%s'.", server_string); + pa_log_debug("handle_get_dbus_address(): Sent reply with address '%s'.", address); - pa_xfree(server_string); + pa_xfree(address); dbus_message_unref(reply); @@ -183,7 +156,7 @@ fail: if (conf) pa_client_conf_free(conf); - pa_xfree(server_string); + pa_xfree(address); if (reply) dbus_message_unref(reply); @@ -194,7 +167,7 @@ oom: if (conf) pa_client_conf_free(conf); - pa_xfree(server_string); + pa_xfree(address); if (reply) dbus_message_unref(reply); @@ -214,8 +187,8 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers")) - return handle_get_dbus_servers(conn, msg, sl); + if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusAddress")) + return handle_get_dbus_address(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -229,15 +202,15 @@ static DBusObjectPathVTable vtable = { .dbus_internal_pad4 = NULL }; -pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type) { +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) { pa_dbusobj_server_lookup *sl; DBusError error; dbus_error_init(&error); sl = pa_xnew(pa_dbusobj_server_lookup, 1); + sl->core = c; sl->path_registered = FALSE; - sl->server_type = server_type; if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message); @@ -274,4 +247,4 @@ void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) { pa_dbus_connection_unref(sl->conn); pa_xfree(sl); -} \ No newline at end of file +} diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h index 69fdacdd..c930d5b7 100644 --- a/src/daemon/server-lookup.h +++ b/src/daemon/server-lookup.h @@ -32,11 +32,9 @@ #include -#include "daemon-conf.h" - typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup; -pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type); +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c); void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl); -#endif \ No newline at end of file +#endif diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 077f4178..14551488 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -2,6 +2,8 @@ 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 @@ -23,37 +25,487 @@ #include #endif +#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(""); +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 */ + +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; +}; + +struct server { + struct userdata *userdata; + 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_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); +} + +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."); +} + +/* 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: Fancier name. */ + client = pa_client_new(s->userdata->module->core, &new_data); + pa_client_new_data_done(&new_data); + + if (!client) + return; + + c = pa_xnew(struct connection, 1); + c->server = s; + c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, new_connection); + c->client = client; + + c->client->kill = client_kill_cb; + c->client->send_event = NULL; + c->client->userdata = c; + + pa_idxset_put(s->userdata->connections, c, NULL); +} + +/* 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) { + /* 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); /* May return NULL */ + + pa_xfree(address); + + return s; +} + +static struct server *start_tcp_server(struct userdata *u) { + pa_log("start_tcp_server(): Not implemented!"); + return NULL; +} + +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; + *local_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; + } - pa_log_notice("Hello, world!"); + 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); return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; } -void pa__done(pa_module*m) { +/* 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); @@ -61,6 +513,18 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; + 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.h b/src/pulsecore/core.h index c6794445..f93652e2 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -42,6 +42,13 @@ typedef struct pa_core pa_core; #include #include +typedef enum pa_server_type { + PA_SERVER_TYPE_UNSET, + PA_SERVER_TYPE_USER, + PA_SERVER_TYPE_SYSTEM, + PA_SERVER_TYPE_NONE +} pa_server_type_t; + typedef enum pa_core_state { PA_CORE_STARTUP, PA_CORE_RUNNING, @@ -152,6 +159,8 @@ struct pa_core { pa_resample_method_t resample_method; int realtime_priority; + pa_server_type_t server_type; + /* hooks */ pa_hook hooks[PA_CORE_HOOK_MAX]; }; diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c new file mode 100644 index 00000000..05931e0a --- /dev/null +++ b/src/pulsecore/dbus-common.c @@ -0,0 +1,73 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "dbus-common.h" + +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { + char *address = NULL; + char *runtime_path = NULL; + char *escaped_path = NULL; + + switch (server_type) { + case PA_SERVER_TYPE_USER: + if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) { + pa_log("pa_runtime_path() failed."); + break; + } + + if (!(escaped_path = dbus_address_escape_value(runtime_path))) { + pa_log("dbus_address_escape_value() failed."); + break; + } + + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_SYSTEM: + if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) { + pa_log("dbus_address_escape_value() failed."); + break; + } + + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_NONE: + address = pa_xnew0(char, 1); + break; + + default: + pa_assert_not_reached(); + } + + pa_xfree(runtime_path); + pa_xfree(escaped_path); + + return address; +} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h new file mode 100644 index 00000000..26bd05d4 --- /dev/null +++ b/src/pulsecore/dbus-common.h @@ -0,0 +1,39 @@ +#ifndef foodbuscommonhfoo +#define foodbuscommonhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#define PA_DBUS_DEFAULT_PORT 24883 +#define PA_DBUS_SOCKET_NAME "dbus_socket" + +#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME + +/* Returns the default address of the server type in the escaped form. For + * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the + * string. This function may fail in some rare cases, in which case NULL is + * returned. */ +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); + +#endif diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index ece36def..d8bd0e0a 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -276,6 +276,27 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus return pconn; } +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) { + pa_dbus_wrap_connection *pconn; + + pa_assert(m); + pa_assert(conn); + + pconn = pa_xnew(pa_dbus_wrap_connection, 1); + pconn->mainloop = m; + pconn->connection = dbus_connection_ref(conn); + + dbus_connection_set_exit_on_disconnect(conn, FALSE); + dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); + dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL); + dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL); + dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL); + + pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn); + + return pconn; +} + void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) { pa_assert(c); diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 55cda7a0..cd08485d 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -31,6 +31,7 @@ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, DBusConnection *conn); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); -- cgit From 123c6a3c6ffc9903c0855e38445fc3b6588311ce Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 19 Jun 2009 10:28:08 +0300 Subject: dbus-common: Implement infrastructure for registering D-Bus objects on all client connections and for receiving method calls from clients. --- src/Makefile.am | 9 +- src/daemon/server-lookup.c | 8 +- src/modules/module-dbus-protocol.c | 18 +- src/pulsecore/dbus-common.c | 430 +++++++++++++++++++++++++++++++++++++ src/pulsecore/dbus-common.h | 34 +++ src/pulsecore/dbus-objs/core.c | 120 +++++++++++ src/pulsecore/dbus-objs/core.h | 39 ++++ 7 files changed, 649 insertions(+), 9 deletions(-) create mode 100644 src/pulsecore/dbus-objs/core.c create mode 100644 src/pulsecore/dbus-objs/core.h diff --git a/src/Makefile.am b/src/Makefile.am index 5302bc22..d28ffa1e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -783,7 +783,6 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/core-scache.c pulsecore/core-scache.h \ pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ pulsecore/core.c pulsecore/core.h \ - pulsecore/dbus-common.c pulsecore/dbus-common.h \ pulsecore/envelope.c pulsecore/envelope.h \ pulsecore/fdsem.c pulsecore/fdsem.h \ pulsecore/g711.c pulsecore/g711.h \ @@ -830,7 +829,9 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS) endif if HAVE_DBUS -libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dbus-shared.c pulsecore/dbus-shared.h +libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += \ + pulsecore/dbus-shared.c pulsecore/dbus-shared.h \ + pulsecore/dbus-common.c pulsecore/dbus-common.h libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS) libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS) endif @@ -1220,7 +1221,9 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMI # D-Bus protocol -module_dbus_protocol_la_SOURCES = modules/module-dbus-protocol.c +module_dbus_protocol_la_SOURCES = \ + pulsecore/dbus-objs/core.c pulsecore/dbus-objs/core.h \ + modules/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 b53fda76..2d2d8ce6 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -48,7 +48,7 @@ static const char introspection[] = " \n" " " - " " + " " " " " " " " @@ -101,7 +101,7 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } -static DBusHandlerResult handle_get_dbus_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { +static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { DBusMessage *reply = NULL; pa_client_conf *conf = NULL; char *address = NULL; @@ -187,8 +187,8 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusAddress")) - return handle_get_dbus_address(conn, msg, sl); + if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetAddress")) + return handle_get_address(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 14551488..74dfeef0 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -40,6 +40,8 @@ #include #include +#include + #include "module-dbus-protocol-symdef.h" PA_MODULE_DESCRIPTION("D-Bus interface"); @@ -67,6 +69,8 @@ struct userdata { pa_idxset *connections; pa_time_event *cleanup_event; + + pa_dbusobj_core *core_object; }; struct server { @@ -89,12 +93,15 @@ static const char* const valid_modargs[] = { 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; @@ -120,7 +127,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne 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: Fancier name. */ + 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); @@ -133,10 +140,12 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne c->client = client; c->client->kill = client_kill_cb; - c->client->send_event = NULL; + 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. */ @@ -485,6 +494,8 @@ int pa__init(pa_module *m) { 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: @@ -513,6 +524,9 @@ void pa__done(pa_module *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); diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c index 05931e0a..350add82 100644 --- a/src/pulsecore/dbus-common.c +++ b/src/pulsecore/dbus-common.c @@ -25,10 +25,35 @@ #include +#include + #include +#include +#include +#include #include "dbus-common.h" +struct dbus_state { + pa_core *core; + pa_hashmap *objects; /* Object path -> struct object_entry */ + pa_idxset *connections; /* DBusConnections */ +}; + +struct object_entry { + char *path; + pa_hashmap *interfaces; /* Interface name -> struct interface_entry */ + char *introspection; +}; + +struct interface_entry { + char *name; + char **methods; + char *introspection_snippet; + DBusObjectPathMessageFunction receive; + void *userdata; +}; + char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { char *address = NULL; char *runtime_path = NULL; @@ -71,3 +96,408 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { return address; } + +static void update_introspection(struct object_entry *oe) { + pa_strbuf *buf; + void *state = NULL; + struct interface_entry *iface_entry = NULL; + + pa_assert(oe); + + buf = pa_strbuf_new(); + pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + pa_strbuf_puts(buf, ""); + + while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) + pa_strbuf_puts(buf, iface_entry->introspection_snippet); + + pa_strbuf_puts(buf, " " + " " + " " + " " + " "); + + pa_strbuf_puts(buf, ""); + + pa_xfree(oe->introspection); + oe->introspection = pa_strbuf_tostring_free(buf); +} + +static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) { + const char *interface; + struct interface_entry *iface_entry; + void *state = NULL; + + pa_assert(obj_entry); + pa_assert(msg); + + if ((interface = dbus_message_get_interface(msg))) + return pa_hashmap_get(obj_entry->interfaces, interface); + + /* NULL interface, we'll have to search for an interface that contains the + * method. */ + + while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *method; + char **pos = iface_entry->methods; + + while ((method = *pos++)) { + if (!strcmp(dbus_message_get_member(msg), method)) + return iface_entry; + } + } + + return NULL; +} + +static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + struct dbus_state *dbus_state = user_data; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + DBusMessage *reply = NULL; + + pa_assert(connection); + pa_assert(message); + pa_assert(dbus_state); + pa_assert(dbus_state->objects); + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + pa_log_debug("%s.%s handled.", obj_entry->path, "Introspect"); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (!(iface_entry = find_interface(obj_entry, message))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + return iface_entry->receive(connection, message, iface_entry->userdata); + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusObjectPathVTable vtable = { + .unregister_function = NULL, + .message_function = handle_message_cb, + .dbus_internal_pad1 = NULL, + .dbus_internal_pad2 = NULL, + .dbus_internal_pad3 = NULL, + .dbus_internal_pad4 = NULL +}; + +static void register_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { + DBusConnection *conn; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(obj_entry); + + if (!dbus_state->connections) + return; + + while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { + if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) + pa_log_debug("dbus_connection_register_object_path() failed."); + } +} + +static char **copy_methods(const char * const *methods) { + unsigned n = 0; + char **copy; + unsigned i; + + while (methods[n++]) + ; + + copy = pa_xnew0(char *, n); + + for (i = 0; i < n - 1; ++i) + copy[i] = pa_xstrdup(methods[i]); + + return copy; +} + +int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) { + struct dbus_state *dbus_state; + pa_hashmap *objects; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + pa_bool_t state_created = FALSE; + pa_bool_t object_map_created = FALSE; + pa_bool_t obj_entry_created = FALSE; + + pa_assert(c); + pa_assert(path); + pa_assert(introspection_snippet); + pa_assert(receive_cb); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { + dbus_state = pa_xnew0(struct dbus_state, 1); + dbus_state->core = c; + pa_hashmap_put(c->shared, "dbus-state", dbus_state); + state_created = TRUE; + } + + if (!(objects = dbus_state->objects)) { + objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + dbus_state->objects = objects; + object_map_created = TRUE; + } + + if (!(obj_entry = pa_hashmap_get(objects, path))) { + obj_entry = pa_xnew(struct object_entry, 1); + obj_entry->path = pa_xstrdup(path); + obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + obj_entry->introspection = NULL; + pa_hashmap_put(objects, path, obj_entry); + obj_entry_created = TRUE; + } + + if (pa_hashmap_get(obj_entry->interfaces, interface) != NULL) + goto fail; /* The interface was already registered. */ + + iface_entry = pa_xnew(struct interface_entry, 1); + iface_entry->name = pa_xstrdup(interface); + iface_entry->methods = copy_methods(methods); + iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); + iface_entry->receive = receive_cb; + iface_entry->userdata = userdata; + pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry); + + update_introspection(obj_entry); + + if (obj_entry_created) + register_object(dbus_state, obj_entry); + + return 0; + +fail: + if (obj_entry_created) { + pa_hashmap_remove(objects, path); + pa_xfree(obj_entry); + } + + if (object_map_created) { + dbus_state->objects = NULL; + pa_hashmap_free(objects, NULL, NULL); + } + + if (state_created) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return -1; +} + +static void unregister_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { + DBusConnection *conn; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(obj_entry); + + if (!dbus_state->connections) + return; + + while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { + if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) + pa_log_debug("dbus_connection_unregister_object_path() failed."); + } +} + +static void free_methods(char **methods) { + char **pos = methods; + + while (*pos++) + pa_xfree(*pos); + + pa_xfree(methods); +} + +int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { + struct dbus_state *dbus_state; + pa_hashmap *objects; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + + pa_assert(c); + pa_assert(path); + pa_assert(interface); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) + return -1; + + if (!(objects = dbus_state->objects)) + return -1; + + if (!(obj_entry = pa_hashmap_get(objects, path))) + return -1; + + if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface))) + return -1; + + update_introspection(obj_entry); + + pa_xfree(iface_entry->name); + free_methods(iface_entry->methods); + pa_xfree(iface_entry->introspection_snippet); + pa_xfree(iface_entry); + + if (pa_hashmap_isempty(obj_entry->interfaces)) { + unregister_object(dbus_state, obj_entry); + + pa_hashmap_remove(objects, path); + pa_xfree(obj_entry->path); + pa_hashmap_free(obj_entry->interfaces, NULL, NULL); + pa_xfree(obj_entry->introspection); + pa_xfree(obj_entry); + } + + if (pa_hashmap_isempty(objects)) { + dbus_state->objects = NULL; + pa_hashmap_free(objects, NULL, NULL); + } + + if (!dbus_state->objects && !dbus_state->connections) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return 0; +} + +static void register_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(conn); + + if (!dbus_state->objects) + return; + + while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { + if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) + pa_log_debug("dbus_connection_register_object_path() failed."); + } +} + +int pa_dbus_register_connection(pa_core *c, DBusConnection *conn) { + struct dbus_state *dbus_state; + pa_idxset *connections; + pa_bool_t state_created = FALSE; + pa_bool_t connection_set_created = FALSE; + + pa_assert(c); + pa_assert(conn); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { + dbus_state = pa_xnew0(struct dbus_state, 1); + dbus_state->core = c; + pa_hashmap_put(c->shared, "dbus-state", dbus_state); + state_created = TRUE; + } + + if (!(connections = dbus_state->connections)) { + connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + dbus_state->connections = connections; + connection_set_created = TRUE; + } + + if (pa_idxset_get_by_data(connections, conn, NULL)) + goto fail; /* The connection was already registered. */ + + register_all_objects(dbus_state, conn); + + pa_idxset_put(connections, dbus_connection_ref(conn), NULL); + + return 0; + +fail: + if (connection_set_created) { + dbus_state->connections = NULL; + pa_idxset_free(connections, NULL, NULL); + } + + if (state_created) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return -1; +} + +static void unregister_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(conn); + + if (!dbus_state->objects) + return; + + while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { + if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) + pa_log_debug("dus_connection_unregister_object_path() failed."); + } +} + +int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn) { + struct dbus_state *dbus_state; + pa_idxset *connections; + + pa_assert(c); + pa_assert(conn); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) + return -1; + + if (!(connections = dbus_state->connections)) + return -1; + + if (!pa_idxset_remove_by_data(connections, conn, NULL)) + return -1; + + unregister_all_objects(dbus_state, conn); + + dbus_connection_unref(conn); + + if (pa_idxset_isempty(connections)) { + dbus_state->connections = NULL; + pa_idxset_free(connections, NULL, NULL); + } + + if (!dbus_state->objects && !dbus_state->connections) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return 0; +} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h index 26bd05d4..23c7c221 100644 --- a/src/pulsecore/dbus-common.h +++ b/src/pulsecore/dbus-common.h @@ -22,6 +22,8 @@ USA. ***/ +#include + #include #include @@ -30,10 +32,42 @@ #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME +/* NOTE: These functions may only be called from the main thread. */ + /* Returns the default address of the server type in the escaped form. For * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the * string. This function may fail in some rare cases, in which case NULL is * returned. */ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); +/* Registers the given interface to the given object path. This is additive: it + * doesn't matter whether or not the object has already been registered; if it + * is, then its interface set is just extended. + * + * Introspection requests are handled automatically. For that to work, the + * caller gives an XML snippet containing the interface introspection element. + * All interface snippets are automatically combined to provide the final + * introspection string. + * + * The introspection snippet contains the interface name and the methods, but + * since this function doesn't do XML parsing, the interface name and the set + * of method names have to be supplied separately. If the interface doesn't + * contain any methods, NULL may be given as the methods parameter, otherwise + * the methods parameter must be a NULL-terminated array of strings. + * + * Fails and returns a negative number if the object already has the interface + * registered. */ +int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata); + +/* Returns a negative number if the given object doesn't have the given + * interface registered. */ +int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface); + +/* Fails and returns a negative number if the connection is already + * registered. */ +int pa_dbus_register_connection(pa_core *c, DBusConnection *conn); + +/* Returns a negative number if the connection wasn't registered. */ +int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn); + #endif diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c new file mode 100644 index 00000000..f59c478a --- /dev/null +++ b/src/pulsecore/dbus-objs/core.c @@ -0,0 +1,120 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include + +#include "core.h" + +#define OBJECT_NAME "/org/pulseaudio/core" +#define INTERFACE_NAME "org.pulseaudio.Core" + +struct pa_dbusobj_core { + pa_core *core; +}; + +static const char *introspection_snippet = + " " + " " + " " + " " + " "; + +static const char *methods[] = { + "Test", + NULL +}; + +static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusMessage *reply = NULL; + const char *reply_message = "Hello!"; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!(reply = dbus_message_new_method_return(msg))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + pa_dbusobj_core *c = user_data; + + pa_assert(connection); + pa_assert(message); + pa_assert(c); + + if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test")) + return handle_test(connection, message, c); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { + pa_dbusobj_core *c; + + pa_assert(core); + + c = pa_xnew(pa_dbusobj_core, 1); + c->core = core; + + pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c); + + return c; +} + +void pa_dbusobj_core_free(pa_dbusobj_core *c) { + pa_assert(c); + + pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME); + + pa_xfree(c); +} diff --git a/src/pulsecore/dbus-objs/core.h b/src/pulsecore/dbus-objs/core.h new file mode 100644 index 00000000..8e59cc3d --- /dev/null +++ b/src/pulsecore/dbus-objs/core.h @@ -0,0 +1,39 @@ +#ifndef foodbusobjscorehfoo +#define foodbusobjscorehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus object at path /org/pulseaudio/core. + * The implemented interface is org.pulseaudio.Core. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Core interface + * documentation. + */ + +#include + +typedef struct pa_dbusobj_core pa_dbusobj_core; + +pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core); +void pa_dbusobj_core_free(pa_dbusobj_core *c); + +#endif -- cgit From 3c6a0acc98a8326ad7a19c29005bba353396a88b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 19 Jun 2009 15:17:57 +0300 Subject: dbus-protocol: Implement TCP server startup. --- src/daemon/server-lookup.c | 11 +++------- src/daemon/system.pa.in | 3 ++- src/modules/module-dbus-protocol.c | 43 ++++++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 2d2d8ce6..33d6b24c 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -120,14 +120,11 @@ static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *m return DBUS_HANDLER_RESULT_HANDLED; } - pa_client_conf_free(conf); - if (conf->default_dbus_server) { - if (!(address = dbus_address_escape_value(conf->default_dbus_server))) - goto oom; + address = pa_xstrdup(conf->default_dbus_server); } else { if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed."))) + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) goto fail; if (!dbus_connection_send(conn, reply, NULL)) goto oom; @@ -144,10 +141,8 @@ static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *m if (!dbus_connection_send(conn, reply, NULL)) goto oom; - pa_log_debug("handle_get_dbus_address(): Sent reply with address '%s'.", address); - + pa_client_conf_free(conf); pa_xfree(address); - dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in index 5541bbe4..0ca32bd3 100755 --- a/src/daemon/system.pa.in +++ b/src/daemon/system.pa.in @@ -33,7 +33,8 @@ load-module module-detect load-module module-esound-protocol-unix .endif .ifexists module-dbus-protocol@PA_SOEXT@ -load-module module-dbus-protocol +### If you want to allow TCP connections, set access to "remote" or "local,remote". +load-module module-dbus-protocol access=local .endif load-module module-native-protocol-unix diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 74dfeef0..8be9d1aa 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -54,6 +54,11 @@ PA_MODULE_VERSION(PACKAGE_VERSION); #define CLEANUP_INTERVAL 10 /* seconds */ +enum server_type { + SERVER_TYPE_LOCAL, + SERVER_TYPE_TCP +}; + struct server; struct connection; @@ -75,6 +80,7 @@ struct userdata { struct server { struct userdata *userdata; + enum server_type type; DBusServer *dbus_server; }; @@ -114,6 +120,12 @@ static void client_kill_cb(pa_client *c) { 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; @@ -131,8 +143,17 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne client = pa_client_new(s->userdata->module->core, &new_data); pa_client_new_data_done(&new_data); - if (!client) + if (!client) { + dbus_connection_close(new_connection); return; + } + + if (s->type == SERVER_TYPE_TCP) { + /* 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; @@ -334,7 +355,7 @@ static void server_free(struct server *s) { pa_xfree(s); } -static struct server *start_server(struct userdata *u, const char *address) { +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, @@ -390,7 +411,7 @@ static struct server *start_local_server(struct userdata *u) { address = pa_get_dbus_address_from_server_type(u->module->core->server_type); - s = start_server(u, address); /* May return NULL */ + s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */ pa_xfree(address); @@ -398,8 +419,18 @@ static struct server *start_local_server(struct userdata *u) { } static struct server *start_tcp_server(struct userdata *u) { - pa_log("start_tcp_server(): Not implemented!"); - return NULL; + 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) { @@ -420,7 +451,7 @@ static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *re *remote_access = TRUE; } else if (!strcmp(value, "local,remote")) { *local_access = TRUE; - *local_access = TRUE; + *remote_access = TRUE; } else return -1; -- cgit From b152f3a052eca7225870a7dc4d8a719bee107f0f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 20 Jun 2009 10:17:46 +0300 Subject: module-dbus-protocol: Allow anyone to connect the daemon in system mode. --- src/modules/module-dbus-protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 8be9d1aa..46f64921 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -148,7 +148,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne return; } - if (s->type == SERVER_TYPE_TCP) { + 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); -- cgit From 3bff2eee4bf02ff3bc2619079c3c5fe8a07040a9 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 30 Jun 2009 15:18:11 +0300 Subject: module-cli: Fix compilation by adding libpulsecommon to module_cli_la_LIBADD. --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 6cddb83a..58668890 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1250,7 +1250,7 @@ module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR module_cli_la_SOURCES = modules/module-cli.c module_cli_la_LDFLAGS = $(MODULE_LDFLAGS) -module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la +module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la libpulsecommon-@PA_MAJORMINORMICRO@.la module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) -- cgit From 6e2fec05ddd38fa460276202ca2f4ecf68761ed0 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 30 Jun 2009 15:21:20 +0300 Subject: server-lookup: Update the D-Bus identifiers to be versioned. --- src/daemon/main.c | 2 +- src/daemon/server-lookup.c | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/daemon/main.c b/src/daemon/main.c index 61b0c840..d320c9e4 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -943,7 +943,7 @@ int main(int argc, char *argv[]) { if (!conf->system_instance) { if (!(server_lookup = pa_dbusobj_server_lookup_new(c))) goto finish; - if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio"))) + if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio1"))) goto finish; } diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 33d6b24c..7c80d677 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -36,6 +36,9 @@ #include "server-lookup.h" +#define OBJECT_PATH "/org/pulseaudio1/server_lookup" +#define INTERFACE "org.pulseaudio.ServerLookup1" + struct pa_dbusobj_server_lookup { pa_core *core; pa_dbus_connection *conn; @@ -47,7 +50,7 @@ static const char introspection[] = "" " \n" - " " + " " " " " " " " @@ -182,7 +185,7 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetAddress")) + if (dbus_message_is_method_call(msg, INTERFACE, "GetAddress")) return handle_get_address(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -212,8 +215,8 @@ pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) { goto fail; } - if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup", &vtable, sl)) { - pa_log("dbus_connection_register_object_path() failed for /org/pulseaudio/server_lookup."); + if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) { + pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH "."); goto fail; } @@ -234,8 +237,8 @@ void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) { if (sl->path_registered) { pa_assert(sl->conn); - if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup")) - pa_log_debug("dbus_connection_unregister_object_path() failed for /org/pulseaudio/server_lookup."); + if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH)) + pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH "."); } if (sl->conn) -- cgit From 5c7952e4fa5e560f64255ef173c3e6570bee433a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 3 Jul 2009 02:49:07 +0300 Subject: dbus: Implement the Name property of the core object. --- src/daemon/main.c | 2 +- src/daemon/server-lookup.c | 414 ++++++++++++++++++++++++++++++++++------- src/pulsecore/dbus-common.c | 215 +++++++++++++++++---- src/pulsecore/dbus-common.h | 28 ++- src/pulsecore/dbus-objs/core.c | 414 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 932 insertions(+), 141 deletions(-) diff --git a/src/daemon/main.c b/src/daemon/main.c index d320c9e4..f4209859 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -943,7 +943,7 @@ int main(int argc, char *argv[]) { if (!conf->system_instance) { if (!(server_lookup = pa_dbusobj_server_lookup_new(c))) goto finish; - if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio1"))) + if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.PulseAudio1"))) goto finish; } diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 7c80d677..ebacc099 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -37,7 +37,7 @@ #include "server-lookup.h" #define OBJECT_PATH "/org/pulseaudio1/server_lookup" -#define INTERFACE "org.pulseaudio.ServerLookup1" +#define INTERFACE "org.PulseAudio.ServerLookup1" struct pa_dbusobj_server_lookup { pa_core *core; @@ -50,17 +50,31 @@ static const char introspection[] = "" " \n" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; static void unregister_cb(DBusConnection *conn, void *user_data) { pa_dbusobj_server_lookup *sl = user_data; @@ -72,105 +86,352 @@ static void unregister_cb(DBusConnection *conn, void *user_data) { } static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; const char *i = introspection; DBusMessage *reply = NULL; pa_assert(conn); pa_assert(msg); - if (!(reply = dbus_message_new_method_return(msg))) - goto fail; + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) - goto fail; +finish: + if (reply) + dbus_message_unref(reply); - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; + return r; +} - dbus_message_unref(reply); +enum get_address_result_t { + SUCCESS, + FAILED_TO_LOAD_CLIENT_CONF, + SERVER_FROM_TYPE_FAILED +}; - return DBUS_HANDLER_RESULT_HANDLED; +/* Caller frees the returned address. */ +static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) { + enum get_address_result_t r = SUCCESS; + pa_client_conf *conf = pa_client_conf_new(); -fail: - if (reply) - dbus_message_unref(reply); + *address = NULL; - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + if (pa_client_conf_load(conf, NULL) < 0) { + r = FAILED_TO_LOAD_CLIENT_CONF; + goto finish; + } -oom: - if (reply) - dbus_message_unref(reply); + if (conf->default_dbus_server) + *address = pa_xstrdup(conf->default_dbus_server); + else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) { + r = SERVER_FROM_TYPE_FAILED; + goto finish; + } - return DBUS_HANDLER_RESULT_NEED_MEMORY; +finish: + pa_client_conf_free(conf); + return r; } static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; DBusMessage *reply = NULL; - pa_client_conf *conf = NULL; char *address = NULL; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; pa_assert(conn); pa_assert(msg); pa_assert(sl); - conf = pa_client_conf_new(); + switch (get_address(sl->core->server_type, &address)) { + case SUCCESS: + 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, &address)) { + 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; + goto finish; + + case FAILED_TO_LOAD_CLIENT_CONF: + if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) { + 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; + + case SERVER_FROM_TYPE_FAILED: + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) { + 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; + + default: + pa_assert_not_reached(); + } - if (pa_client_conf_load(conf, NULL) < 0) { - if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) - goto fail; - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (conf->default_dbus_server) { - address = pa_xstrdup(conf->default_dbus_server); - } else { - if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) - goto fail; - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; - return DBUS_HANDLER_RESULT_HANDLED; +finish: + pa_xfree(address); + if (reply) + dbus_message_unref(reply); + + return r; +} + +static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + 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 (!(reply = dbus_message_new_method_return(msg))) - goto oom; + if (*interface && !pa_streq(interface, INTERFACE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) - goto fail; + if (!pa_streq(property, "Address")) { + 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 (!dbus_connection_send(conn, reply, NULL)) - goto oom; + r = handle_get_address(conn, msg, sl); - pa_client_conf_free(conf); - pa_xfree(address); - dbus_message_unref(reply); +finish: + if (reply) + dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; + return r; +} -fail: - if (conf) - pa_client_conf_free(conf); +static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - pa_xfree(address); + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + 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)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (!pa_streq(property, "Address")) { + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return r; +} -oom: - if (conf) - pa_client_conf_free(conf); +static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + DBusMessage *reply = NULL; + const char *property = "Address"; + char *interface = NULL; + char *address = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter variant_iter; - pa_xfree(address); + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + 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; + } + + switch (get_address(sl->core->server_type, &address)) { + case SUCCESS: + 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, &property)) { + 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, &address)) { + 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; + goto finish; + + case FAILED_TO_LOAD_CLIENT_CONF: + if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) { + 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; + + case SERVER_FROM_TYPE_FAILED: + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) { + 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; + + default: + pa_assert_not_reached(); + } + +finish: + pa_xfree(address); if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return r; } static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -182,11 +443,24 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void /* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */ - if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect"))) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, INTERFACE, "GetAddress")) - return handle_get_address(conn, msg, sl); + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get"))) + return handle_get(conn, msg, sl); + + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set"))) + return handle_set(conn, msg, sl); + + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll"))) + return handle_get_all(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c index 350add82..6562cda2 100644 --- a/src/pulsecore/dbus-common.c +++ b/src/pulsecore/dbus-common.c @@ -48,6 +48,7 @@ struct object_entry { struct interface_entry { char *name; + char **properties; char **methods; char *introspection_snippet; DBusObjectPathMessageFunction receive; @@ -106,48 +107,152 @@ static void update_introspection(struct object_entry *oe) { buf = pa_strbuf_new(); pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); - pa_strbuf_puts(buf, ""); + pa_strbuf_puts(buf, "\n"); while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) pa_strbuf_puts(buf, iface_entry->introspection_snippet); - pa_strbuf_puts(buf, " " - " " - " " - " " - " "); - - pa_strbuf_puts(buf, ""); + pa_strbuf_puts(buf, " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); + + pa_strbuf_puts(buf, "\n"); pa_xfree(oe->introspection); oe->introspection = pa_strbuf_tostring_free(buf); } -static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) { - const char *interface; - struct interface_entry *iface_entry; +enum find_result_t { + SUCCESS, + NO_SUCH_PROPERTY, + NO_SUCH_METHOD, + INVALID_MESSAGE_ARGUMENTS +}; + +static enum find_result_t find_interface_by_property(struct object_entry *obj_entry, const char *property, struct interface_entry **entry) { void *state = NULL; pa_assert(obj_entry); - pa_assert(msg); + pa_assert(property); + pa_assert(entry); + + while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *iface_property; + char **pos = (*entry)->properties; + + while ((iface_property = *pos++)) { + if (pa_streq(iface_property, property)) + return SUCCESS; + } + } + + return NO_SUCH_PROPERTY; +} - if ((interface = dbus_message_get_interface(msg))) - return pa_hashmap_get(obj_entry->interfaces, interface); +static enum find_result_t find_interface_by_method(struct object_entry *obj_entry, const char *method, struct interface_entry **entry) { + void *state = NULL; - /* NULL interface, we'll have to search for an interface that contains the - * method. */ + pa_assert(obj_entry); + pa_assert(method); + pa_assert(entry); - while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { - char *method; - char **pos = iface_entry->methods; + while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *iface_method; + char **pos = (*entry)->methods; - while ((method = *pos++)) { - if (!strcmp(dbus_message_get_member(msg), method)) - return iface_entry; + while ((iface_method = *pos++)) { + if (pa_streq(iface_method, method)) + return SUCCESS; } } - return NULL; + return NO_SUCH_METHOD; +} + +static enum find_result_t find_interface_from_properties_call(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { + const char *interface; + const char *property; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(entry); + + if (dbus_message_has_member(msg, "GetAll")) { + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + } else { + pa_assert_se((*entry = pa_hashmap_first(obj_entry->interfaces))); + return SUCCESS; + } + } else { + pa_assert(dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set")); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + } else + return find_interface_by_property(obj_entry, property, entry); + } +} + +static enum find_result_t find_interface(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { + const char *interface; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(entry); + + *entry = NULL; + + if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) + return find_interface_from_properties_call(obj_entry, msg, entry); + + else if ((interface = dbus_message_get_interface(msg))) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + + } else { /* The method call doesn't contain an interface. */ + if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { + if (find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry) == SUCCESS) + return SUCCESS; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + else + /* Assume this is a .Properties call. */ + return find_interface_from_properties_call(obj_entry, msg, entry); + + } else /* This is not a .Properties call. */ + return find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry); + } } static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -166,7 +271,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); - if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { if (!(reply = dbus_message_new_method_return(message))) goto oom; @@ -183,10 +289,38 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa return DBUS_HANDLER_RESULT_HANDLED; } - if (!(iface_entry = find_interface(obj_entry, message))) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + switch (find_interface(obj_entry, message, &iface_entry)) { + case SUCCESS: + return iface_entry->receive(connection, message, iface_entry->userdata); + + case NO_SUCH_PROPERTY: + if (!(reply = dbus_message_new_error(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property"))) + goto fail; - return iface_entry->receive(connection, message, iface_entry->userdata); + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + + case NO_SUCH_METHOD: + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + case INVALID_MESSAGE_ARGUMENTS: + if (!(reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) + goto fail; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + + default: + pa_assert_not_reached(); + } fail: if (reply) @@ -226,23 +360,30 @@ static void register_object(struct dbus_state *dbus_state, struct object_entry * } } -static char **copy_methods(const char * const *methods) { +static char **copy_strarray(const char * const *array) { unsigned n = 0; char **copy; unsigned i; - while (methods[n++]) + while (array[n++]) ; copy = pa_xnew0(char *, n); for (i = 0; i < n - 1; ++i) - copy[i] = pa_xstrdup(methods[i]); + copy[i] = pa_xstrdup(array[i]); return copy; } -int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) { +int pa_dbus_add_interface(pa_core *c, + const char* path, + const char* interface, + const char * const *properties, + const char * const *methods, + const char* introspection_snippet, + DBusObjectPathMessageFunction receive_cb, + void *userdata) { struct dbus_state *dbus_state; pa_hashmap *objects; struct object_entry *obj_entry; @@ -283,7 +424,8 @@ int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, c iface_entry = pa_xnew(struct interface_entry, 1); iface_entry->name = pa_xstrdup(interface); - iface_entry->methods = copy_methods(methods); + iface_entry->properties = copy_strarray(properties); + iface_entry->methods = copy_strarray(methods); iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); iface_entry->receive = receive_cb; iface_entry->userdata = userdata; @@ -331,13 +473,13 @@ static void unregister_object(struct dbus_state *dbus_state, struct object_entry } } -static void free_methods(char **methods) { - char **pos = methods; +static void free_strarray(char **array) { + char **pos = array; while (*pos++) pa_xfree(*pos); - pa_xfree(methods); + pa_xfree(array); } int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { @@ -365,7 +507,8 @@ int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface update_introspection(obj_entry); pa_xfree(iface_entry->name); - free_methods(iface_entry->methods); + free_strarray(iface_entry->properties); + free_strarray(iface_entry->methods); pa_xfree(iface_entry->introspection_snippet); pa_xfree(iface_entry); diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h index 23c7c221..4354c4ea 100644 --- a/src/pulsecore/dbus-common.h +++ b/src/pulsecore/dbus-common.h @@ -28,10 +28,12 @@ #include #define PA_DBUS_DEFAULT_PORT 24883 -#define PA_DBUS_SOCKET_NAME "dbus_socket" +#define PA_DBUS_SOCKET_NAME "dbus-socket" #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" + /* NOTE: These functions may only be called from the main thread. */ /* Returns the default address of the server type in the escaped form. For @@ -47,17 +49,27 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); * Introspection requests are handled automatically. For that to work, the * caller gives an XML snippet containing the interface introspection element. * All interface snippets are automatically combined to provide the final - * introspection string. + * introspection string for the object. * - * The introspection snippet contains the interface name and the methods, but - * since this function doesn't do XML parsing, the interface name and the set - * of method names have to be supplied separately. If the interface doesn't - * contain any methods, NULL may be given as the methods parameter, otherwise - * the methods parameter must be a NULL-terminated array of strings. + * The introspection snippet contains the interface name, the property names + * and the method namess, but since this function doesn't do XML parsing, the + * information needs to be given separately. Property and method names are + * given as a NULL-terminated array of strings. The interface name is used for + * message routing, and so are the property and method names too in case the + * client doesn't tell which interface he's trying to access; in absence of + * interface information from the client, the correct interface is searched + * based on the property or method name. * * Fails and returns a negative number if the object already has the interface * registered. */ -int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata); +int pa_dbus_add_interface(pa_core *c, + const char* path, + const char* interface, + const char * const *properties, + const char * const *methods, + const char* introspection_snippet, + DBusObjectPathMessageFunction receive_cb, + void *userdata); /* Returns a negative number if the given object doesn't have the given * interface registered. */ diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c index f59c478a..18bbf789 100644 --- a/src/pulsecore/dbus-objs/core.c +++ b/src/pulsecore/dbus-objs/core.c @@ -27,62 +27,414 @@ #include +#include #include #include #include "core.h" -#define OBJECT_NAME "/org/pulseaudio/core" -#define INTERFACE_NAME "org.pulseaudio.Core" +#define OBJECT_PATH "/org/pulseaudio1" +#define INTERFACE_CORE "org.PulseAudio.Core1" struct pa_dbusobj_core { pa_core *core; }; static const char *introspection_snippet = - " " - " " - " " - " " - " "; + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"; + +/* If you need to modify this list, note that handle_get_all() uses hard-coded + * indexes to point to these strings, so make sure the indexes don't go wrong + * there. */ +static const char *properties[] = { + "InterfaceRevision", + "Name", + "Version", + "Username", + "Hostname", + "DefaultChannels", + "DefaultSampleFormat", + "DefaultSampleRate", + "Sinks", + "FallbackSink", + "Sources", + "FallbackSource", + "PlaybackStreams", + "RecordStreams", + "Samples", + "Modules", + "Clients", + "Extensions", + NULL +}; static const char *methods[] = { - "Test", + "GetCardByName", + "GetSinkByName", + "GetSourceByName", + "GetSampleByName", + "UploadSample", + "LoadSampleFromFile", + "AddLazySample", + "AddLazySamplesFromDirectory", + "LoadModule", + "Exit", NULL }; -static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { +static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; DBusMessage *reply = NULL; - const char *reply_message = "Hello!"; + const char *server_name = PACKAGE_NAME; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; pa_assert(conn); pa_assert(msg); pa_assert(c); - if (!(reply = dbus_message_new_method_return(msg))) - goto oom; + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: + if (reply) + dbus_message_unref(reply); + + return r; +} - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID)) - goto fail; +static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (*interface && !pa_streq(interface, INTERFACE_CORE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (pa_streq(property, "Name")) { + r = handle_get_name(conn, msg, c); + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: + if (reply) + dbus_message_unref(reply); + + return r; +} - dbus_message_unref(reply); +static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - return DBUS_HANDLER_RESULT_HANDLED; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); -fail: + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (*interface && !pa_streq(interface, INTERFACE_CORE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (pa_streq(property, "Name")) { + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return r; +} + +static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + DBusMessage *reply = NULL; + char *interface = NULL; + char const *server_name = PACKAGE_NAME; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter variant_iter; -oom: + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */ + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return r; } static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -92,8 +444,17 @@ static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *mes pa_assert(message); pa_assert(c); - if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test")) - return handle_test(connection, message, c); + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get"))) + return handle_get(connection, message, c); + + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set"))) + return handle_set(connection, message, c); + + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll"))) + return handle_get_all(connection, message, c); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -106,7 +467,8 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { c = pa_xnew(pa_dbusobj_core, 1); c->core = core; - pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c); + pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c); + return c; } @@ -114,7 +476,7 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { void pa_dbusobj_core_free(pa_dbusobj_core *c) { pa_assert(c); - pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME); + pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE); pa_xfree(c); } -- cgit From 9347e90fed732dac619bb88f6518c344e7436447 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 21 Jul 2009 00:02:27 +0300 Subject: Finish the Core dbus interface. --- src/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 From 9a77d2f81d693d64b63d38be7214626dedf91e11 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 21 Jul 2009 00:04:52 +0300 Subject: Add the forgotten src/modules/dbus directory to git. --- src/modules/dbus/iface-card.c | 61 + src/modules/dbus/iface-card.h | 40 + src/modules/dbus/iface-client.c | 61 + src/modules/dbus/iface-client.h | 40 + src/modules/dbus/iface-core.c | 1968 +++++++++++++++++++++++++++++++ src/modules/dbus/iface-core.h | 38 + src/modules/dbus/iface-device.c | 105 ++ src/modules/dbus/iface-device.h | 46 + src/modules/dbus/iface-module.c | 61 + src/modules/dbus/iface-module.h | 40 + src/modules/dbus/iface-sample.c | 61 + src/modules/dbus/iface-sample.h | 40 + src/modules/dbus/iface-stream.c | 91 ++ src/modules/dbus/iface-stream.h | 42 + src/modules/dbus/module-dbus-protocol.c | 580 +++++++++ 15 files changed, 3274 insertions(+) create mode 100644 src/modules/dbus/iface-card.c create mode 100644 src/modules/dbus/iface-card.h create mode 100644 src/modules/dbus/iface-client.c create mode 100644 src/modules/dbus/iface-client.h create mode 100644 src/modules/dbus/iface-core.c create mode 100644 src/modules/dbus/iface-core.h create mode 100644 src/modules/dbus/iface-device.c create mode 100644 src/modules/dbus/iface-device.h create mode 100644 src/modules/dbus/iface-module.c create mode 100644 src/modules/dbus/iface-module.h create mode 100644 src/modules/dbus/iface-sample.c create mode 100644 src/modules/dbus/iface-sample.h create mode 100644 src/modules/dbus/iface-stream.c create mode 100644 src/modules/dbus/iface-stream.h create mode 100644 src/modules/dbus/module-dbus-protocol.c diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c new file mode 100644 index 00000000..db6aa26f --- /dev/null +++ b/src/modules/dbus/iface-card.c @@ -0,0 +1,61 @@ +/*** + 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 "iface-card.h" + +#define OBJECT_NAME "card" + +struct pa_dbusiface_card { + pa_card *card; + char *path; +}; + +pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix) { + pa_dbusiface_card *c; + + pa_assert(card); + pa_assert(path_prefix); + + c = pa_xnew(pa_dbusiface_card, 1); + c->card = card; + c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, card->index); + + return c; +} + +void pa_dbusiface_card_free(pa_dbusiface_card *c) { + pa_assert(c); + + pa_xfree(c->path); + pa_xfree(c); +} + +const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) { + pa_assert(c); + + return c->path; +} diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h new file mode 100644 index 00000000..54db610c --- /dev/null +++ b/src/modules/dbus/iface-card.h @@ -0,0 +1,40 @@ +#ifndef foodbusifacecardhfoo +#define foodbusifacecardhfoo + +/*** + 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 interface org.PulseAudio.Core1.Card. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Card interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_card pa_dbusiface_card; + +pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix); +void pa_dbusiface_card_free(pa_dbusiface_card *c); + +const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c); + +#endif diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c new file mode 100644 index 00000000..cfa36d0c --- /dev/null +++ b/src/modules/dbus/iface-client.c @@ -0,0 +1,61 @@ +/*** + 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 "iface-client.h" + +#define OBJECT_NAME "client" + +struct pa_dbusiface_client { + pa_client *client; + char *path; +}; + +pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix) { + pa_dbusiface_client *c; + + pa_assert(client); + pa_assert(path_prefix); + + c = pa_xnew(pa_dbusiface_client, 1); + c->client = client; + c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, client->index); + + return c; +} + +void pa_dbusiface_client_free(pa_dbusiface_client *c) { + pa_assert(c); + + pa_xfree(c->path); + pa_xfree(c); +} + +const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c) { + pa_assert(c); + + return c->path; +} diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h new file mode 100644 index 00000000..62cca7f8 --- /dev/null +++ b/src/modules/dbus/iface-client.h @@ -0,0 +1,40 @@ +#ifndef foodbusifaceclienthfoo +#define foodbusifaceclienthfoo + +/*** + 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 interface org.PulseAudio.Core1.Card. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Card interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_client pa_dbusiface_client; + +pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix); +void pa_dbusiface_client_free(pa_dbusiface_client *c); + +const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c); + +#endif diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c new file mode 100644 index 00000000..31f12603 --- /dev/null +++ b/src/modules/dbus/iface-core.c @@ -0,0 +1,1968 @@ +/*** + 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 +#include +#include +#include + +#include "iface-card.h" +#include "iface-client.h" +#include "iface-device.h" +#include "iface-module.h" +#include "iface-sample.h" +#include "iface-stream.h" + +#include "iface-core.h" + +#define OBJECT_PATH "/org/pulseaudio/core1" +#define INTERFACE_CORE "org.PulseAudio.Core1" + +#define INTERFACE_REVISION 0 + + + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata); + +struct pa_dbusiface_core { + pa_core *core; + pa_subscription *subscription; + + pa_dbus_protocol *dbus_protocol; + + pa_hashmap *cards; + pa_hashmap *sinks_by_index; + pa_hashmap *sinks_by_path; + pa_hashmap *sources_by_index; + pa_hashmap *sources_by_path; + pa_hashmap *playback_streams; + pa_hashmap *record_streams; + pa_hashmap *samples; + pa_hashmap *modules; + pa_hashmap *clients; + + pa_sink *fallback_sink; + pa_source *fallback_source; +}; + +enum property_handler_index { + PROPERTY_HANDLER_INTERFACE_REVISION, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_VERSION, + PROPERTY_HANDLER_IS_LOCAL, + PROPERTY_HANDLER_USERNAME, + PROPERTY_HANDLER_HOSTNAME, + PROPERTY_HANDLER_DEFAULT_CHANNELS, + PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT, + PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE, + PROPERTY_HANDLER_CARDS, + PROPERTY_HANDLER_SINKS, + PROPERTY_HANDLER_FALLBACK_SINK, + PROPERTY_HANDLER_SOURCES, + PROPERTY_HANDLER_FALLBACK_SOURCE, + PROPERTY_HANDLER_PLAYBACK_STREAMS, + PROPERTY_HANDLER_RECORD_STREAMS, + PROPERTY_HANDLER_SAMPLES, + PROPERTY_HANDLER_MODULES, + PROPERTY_HANDLER_CLIENTS, + PROPERTY_HANDLER_MY_CLIENT, + PROPERTY_HANDLER_EXTENSIONS, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INTERFACE_REVISION] = { .property_name = "InterfaceRevision", .type = "u", .get_cb = handle_get_interface_revision, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_VERSION] = { .property_name = "Version", .type = "s", .get_cb = handle_get_version, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_LOCAL] = { .property_name = "IsLocal", .type = "b", .get_cb = handle_get_is_local, .set_cb = NULL }, + [PROPERTY_HANDLER_USERNAME] = { .property_name = "Username", .type = "s", .get_cb = handle_get_username, .set_cb = NULL }, + [PROPERTY_HANDLER_HOSTNAME] = { .property_name = "Hostname", .type = "s", .get_cb = handle_get_hostname, .set_cb = NULL }, + [PROPERTY_HANDLER_DEFAULT_CHANNELS] = { .property_name = "DefaultChannels", .type = "au", .get_cb = handle_get_default_channels, .set_cb = handle_set_default_channels }, + [PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT] = { .property_name = "DefaultSampleFormat", .type = "u", .get_cb = handle_get_default_sample_format, .set_cb = handle_set_default_sample_format }, + [PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE] = { .property_name = "DefaultSampleRate", .type = "u", .get_cb = handle_get_default_sample_rate, .set_cb = handle_set_default_sample_rate }, + [PROPERTY_HANDLER_CARDS] = { .property_name = "Cards", .type = "ao", .get_cb = handle_get_cards, .set_cb = NULL }, + [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "ao", .get_cb = handle_get_sinks, .set_cb = NULL }, + [PROPERTY_HANDLER_FALLBACK_SINK] = { .property_name = "FallbackSink", .type = "o", .get_cb = handle_get_fallback_sink, .set_cb = handle_set_fallback_sink }, + [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "ao", .get_cb = handle_get_sources, .set_cb = NULL }, + [PROPERTY_HANDLER_FALLBACK_SOURCE] = { .property_name = "FallbackSource", .type = "o", .get_cb = handle_get_fallback_source, .set_cb = handle_set_fallback_source }, + [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao", .get_cb = handle_get_playback_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_RECORD_STREAMS] = { .property_name = "RecordStreams", .type = "ao", .get_cb = handle_get_record_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLES] = { .property_name = "Samples", .type = "ao", .get_cb = handle_get_samples, .set_cb = NULL }, + [PROPERTY_HANDLER_MODULES] = { .property_name = "Modules", .type = "ao", .get_cb = handle_get_modules, .set_cb = NULL }, + [PROPERTY_HANDLER_CLIENTS] = { .property_name = "Clients", .type = "ao", .get_cb = handle_get_clients, .set_cb = NULL }, + [PROPERTY_HANDLER_MY_CLIENT] = { .property_name = "MyClient", .type = "o", .get_cb = handle_get_my_client, .set_cb = NULL }, + [PROPERTY_HANDLER_EXTENSIONS] = { .property_name = "Extensions", .type = "as", .get_cb = handle_get_extensions, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_GET_CARD_BY_NAME, + METHOD_HANDLER_GET_SINK_BY_NAME, + METHOD_HANDLER_GET_SOURCE_BY_NAME, + METHOD_HANDLER_GET_SAMPLE_BY_NAME, + METHOD_HANDLER_UPLOAD_SAMPLE, + METHOD_HANDLER_LOAD_MODULE, + METHOD_HANDLER_EXIT, + METHOD_HANDLER_LISTEN_FOR_SIGNAL, + METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info get_card_by_name_args[] = { { "name", "s", "in" }, { "card", "o", "out" } }; +static pa_dbus_arg_info get_sink_by_name_args[] = { { "name", "s", "in" }, { "sink", "o", "out" } }; +static pa_dbus_arg_info get_source_by_name_args[] = { { "name", "s", "in" }, { "source", "o", "out" } }; +static pa_dbus_arg_info get_sample_by_name_args[] = { { "name", "s", "in" }, { "sample", "o", "out" } }; +static pa_dbus_arg_info upload_sample_args[] = { { "name", "s", "in" }, + { "sample_format", "u", "in" }, + { "sample_rate", "u", "in" }, + { "channels", "au", "in" }, + { "default_volume", "au", "in" }, + { "property_list", "a{say}", "in" }, + { "data", "ay", "in" }, + { "sample", "o", "out" } }; +static pa_dbus_arg_info load_module_args[] = { { "name", "s", "in" }, { "arguments", "a{ss}", "in" }, { "module", "o", "out" } }; +static pa_dbus_arg_info listen_for_signal_args[] = { { "signal", "s", "in" }, { "objects", "ao", "in" } }; +static pa_dbus_arg_info stop_listening_for_signal_args[] = { { "signal", "s", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_GET_CARD_BY_NAME] = { + .method_name = "GetCardByName", + .arguments = get_card_by_name_args, + .n_arguments = sizeof(get_card_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_card_by_name }, + [METHOD_HANDLER_GET_SINK_BY_NAME] = { + .method_name = "GetSinkByName", + .arguments = get_sink_by_name_args, + .n_arguments = sizeof(get_sink_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_sink_by_name }, + [METHOD_HANDLER_GET_SOURCE_BY_NAME] = { + .method_name = "GetSourceByName", + .arguments = get_source_by_name_args, + .n_arguments = sizeof(get_source_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_source_by_name }, + [METHOD_HANDLER_GET_SAMPLE_BY_NAME] = { + .method_name = "GetSampleByName", + .arguments = get_sample_by_name_args, + .n_arguments = sizeof(get_sample_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_sample_by_name }, + [METHOD_HANDLER_UPLOAD_SAMPLE] = { + .method_name = "UploadSample", + .arguments = upload_sample_args, + .n_arguments = sizeof(upload_sample_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_upload_sample }, + [METHOD_HANDLER_LOAD_MODULE] = { + .method_name = "LoadModule", + .arguments = upload_sample_args, + .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_load_module }, + [METHOD_HANDLER_EXIT] = { + .method_name = "Exit", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_exit }, + [METHOD_HANDLER_LISTEN_FOR_SIGNAL] = { + .method_name = "ListenForSignal", + .arguments = listen_for_signal_args, + .n_arguments = sizeof(listen_for_signal_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_listen_for_signal }, + [METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL] = { + .method_name = "StopListeningForSignal", + .arguments = stop_listening_for_signal_args, + .n_arguments = sizeof(stop_listening_for_signal_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_stop_listening_for_signal } +}; + +enum signal_index { + SIGNAL_NEW_CARD, + SIGNAL_CARD_REMOVED, + SIGNAL_NEW_SINK, + SIGNAL_SINK_REMOVED, + SIGNAL_FALLBACK_SINK_UPDATED, + SIGNAL_NEW_SOURCE, + SIGNAL_SOURCE_REMOVED, + SIGNAL_FALLBACK_SOURCE_UPDATED, + SIGNAL_NEW_PLAYBACK_STREAM, + SIGNAL_PLAYBACK_STREAM_REMOVED, + SIGNAL_NEW_RECORD_STREAM, + SIGNAL_RECORD_STREAM_REMOVED, + SIGNAL_NEW_SAMPLE, + SIGNAL_SAMPLE_REMOVED, + SIGNAL_NEW_MODULE, + SIGNAL_MODULE_REMOVED, + SIGNAL_NEW_CLIENT, + SIGNAL_CLIENT_REMOVED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info new_card_args[] = { { "card", "o", NULL } }; +static pa_dbus_arg_info card_removed_args[] = { { "card", "o", NULL } }; +static pa_dbus_arg_info new_sink_args[] = { { "sink", "o", NULL } }; +static pa_dbus_arg_info sink_removed_args[] = { { "sink", "o", NULL } }; +static pa_dbus_arg_info fallback_sink_updated_args[] = { { "sink", "o", NULL } }; +static pa_dbus_arg_info new_source_args[] = { { "source", "o", NULL } }; +static pa_dbus_arg_info source_removed_args[] = { { "source", "o", NULL } }; +static pa_dbus_arg_info fallback_source_updated_args[] = { { "source", "o", NULL } }; +static pa_dbus_arg_info new_playback_stream_args[] = { { "playback_stream", "o", NULL } }; +static pa_dbus_arg_info playback_stream_removed_args[] = { { "playback_stream", "o", NULL } }; +static pa_dbus_arg_info new_record_stream_args[] = { { "record_stream", "o", NULL } }; +static pa_dbus_arg_info record_stream_removed_args[] = { { "record_stream", "o", NULL } }; +static pa_dbus_arg_info new_sample_args[] = { { "sample", "o", NULL } }; +static pa_dbus_arg_info sample_removed_args[] = { { "sample", "o", NULL } }; +static pa_dbus_arg_info new_module_args[] = { { "module", "o", NULL } }; +static pa_dbus_arg_info module_removed_args[] = { { "module", "o", NULL } }; +static pa_dbus_arg_info new_client_args[] = { { "client", "o", NULL } }; +static pa_dbus_arg_info client_removed_args[] = { { "client", "o", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_NEW_CARD] = { .name = "NewCard", .arguments = new_card_args, .n_arguments = 1 }, + [SIGNAL_CARD_REMOVED] = { .name = "CardRemoved", .arguments = card_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_SINK] = { .name = "NewSink", .arguments = new_sink_args, .n_arguments = 1 }, + [SIGNAL_SINK_REMOVED] = { .name = "SinkRemoved", .arguments = sink_removed_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SINK_UPDATED] = { .name = "FallbackSinkUpdated", .arguments = fallback_sink_updated_args, .n_arguments = 1 }, + [SIGNAL_NEW_SOURCE] = { .name = "NewSource", .arguments = new_source_args, .n_arguments = 1 }, + [SIGNAL_SOURCE_REMOVED] = { .name = "SourceRemoved", .arguments = source_removed_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 }, + [SIGNAL_NEW_PLAYBACK_STREAM] = { .name = "NewPlaybackStream", .arguments = new_playback_stream_args, .n_arguments = 1 }, + [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_RECORD_STREAM] = { .name = "NewRecordStream", .arguments = new_record_stream_args, .n_arguments = 1 }, + [SIGNAL_RECORD_STREAM_REMOVED] = { .name = "RecordStreamRemoved", .arguments = record_stream_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_SAMPLE] = { .name = "NewSample", .arguments = new_sample_args, .n_arguments = 1 }, + [SIGNAL_SAMPLE_REMOVED] = { .name = "SampleRemoved", .arguments = sample_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_MODULE] = { .name = "NewModule", .arguments = new_module_args, .n_arguments = 1 }, + [SIGNAL_MODULE_REMOVED] = { .name = "ModuleRemoved", .arguments = module_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_CLIENT] = { .name = "NewClient", .arguments = new_client_args, .n_arguments = 1 }, + [SIGNAL_CLIENT_REMOVED] = { .name = "ClientRemoved", .arguments = client_removed_args, .n_arguments = 1 }, +}; + +static pa_dbus_interface_info core_interface_info = { + .name = INTERFACE_CORE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) { + dbus_uint32_t interface_revision = INTERFACE_REVISION; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + const char *server_name = PACKAGE_NAME; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &server_name); +} + +static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata) { + const char *version = PACKAGE_VERSION; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &version); +} + +static dbus_bool_t get_is_local(DBusConnection *conn) { + int conn_fd; + + pa_assert(conn); + + if (!dbus_connection_get_socket(conn, &conn_fd)) + return FALSE; + + return pa_socket_is_local(conn_fd); +} + +static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata) { + dbus_bool_t is_local; + + pa_assert(conn); + pa_assert(msg); + + is_local = get_is_local(conn); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_local); +} + +static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata) { + char *username = NULL; + + pa_assert(conn); + pa_assert(msg); + + username = pa_get_user_name_malloc(); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &username); + + pa_xfree(username); +} + +static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata) { + char *hostname = NULL; + + pa_assert(conn); + pa_assert(msg); + + hostname = pa_get_host_name_malloc(); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &hostname); + + pa_xfree(hostname); +} + +/* Caller frees the returned array. */ +static dbus_uint32_t *get_default_channels(pa_dbusiface_core *c, unsigned *n) { + dbus_uint32_t *default_channels = NULL; + unsigned i; + + pa_assert(c); + pa_assert(n); + + *n = c->core->default_channel_map.channels; + default_channels = pa_xnew(dbus_uint32_t, *n); + + for (i = 0; i < *n; ++i) + default_channels[i] = c->core->default_channel_map.map[i]; + + return default_channels; +} + +static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t *default_channels = NULL; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + default_channels = get_default_channels(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_channels, n); + + pa_xfree(default_channels); +} + +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_channel_map new_channel_map; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + DBusMessageIter array_iter; + dbus_uint32_t *default_channels; + int n_channels; + unsigned i; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_channel_map_init(&new_channel_map); + + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); + return; + } + + dbus_message_iter_recurse(&msg_iter, &variant_iter); + + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Variant doesn't contain an array."); + return; + } + + dbus_message_iter_recurse(&variant_iter, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_UINT32) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Array type is not uint32."); + return; + } + + dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels); + + if (n_channels <= 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); + return; + } + + new_channel_map.channels = n_channels; + + for (i = 0; i < new_channel_map.channels; ++i) { + if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position."); + return; + } + + new_channel_map.map[i] = default_channels[i]; + } + + c->core->default_channel_map = new_channel_map; + c->core->default_sample_spec.channels = n_channels; + + pa_dbus_send_empty_reply(conn, msg); +}; + +static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_format; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + default_sample_format = c->core->default_sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format); +} + +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_format; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_format) < 0) + return; + + if (default_sample_format >= PA_SAMPLE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + return; + } + + c->core->default_sample_spec.format = default_sample_format; + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_rate; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + default_sample_rate = c->core->default_sample_spec.rate; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate); +} + +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_rate; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate) < 0) + return; + + if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + return; + } + + c->core->default_sample_spec.rate = default_sample_rate; + + pa_dbus_send_empty_reply(conn, msg); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_cards(pa_dbusiface_core *c, unsigned *n) { + const char **cards; + unsigned i; + void *state = NULL; + pa_dbusiface_card *card; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->cards); + + if (*n == 0) + return NULL; + + cards = pa_xnew(const char *, *n); + + for (i = 0, card = pa_hashmap_iterate(c->cards, &state, NULL); card; ++i, card = pa_hashmap_iterate(c->cards, &state, NULL)) + cards[i] = pa_dbusiface_card_get_path(card); + + pa_assert(i == *n); + + return cards; +} + +static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **cards; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + cards = get_cards(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, cards, n); + + pa_xfree(cards); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) { + const char **sinks; + unsigned i; + void *state = NULL; + pa_dbusiface_device *sink; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->sinks_by_index); + + if (*n == 0) + return NULL; + + sinks = pa_xnew(const char *, *n); + + for (i = 0, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL); sink; ++i, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL)) + sinks[i] = pa_dbusiface_device_get_path(sink); + + pa_assert(i == *n); + + return sinks; +} + +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **sinks; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sinks = get_sinks(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n); + + pa_xfree(sinks); +} + +static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_sink; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_sink) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + return; + } + + pa_assert_se((fallback_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)))); + object_path = pa_dbusiface_device_get_path(fallback_sink); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_sink; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_sink) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + return; + } + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) + return; + + if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + return; + } + + pa_namereg_set_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)); + + pa_dbus_send_empty_reply(conn, msg); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sources(pa_dbusiface_core *c, unsigned *n) { + const char **sources; + unsigned i; + void *state = NULL; + pa_dbusiface_device *source; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->sources_by_index); + + if (*n == 0) + return NULL; + + sources = pa_xnew(const char *, *n); + + for (i = 0, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL); source; ++i, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL)) + sources[i] = pa_dbusiface_device_get_path(source); + + pa_assert(i == *n); + + return sources; +} + +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **sources; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sources = get_sources(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n); + + pa_xfree(sources); +} + +static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_source; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_source) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + return; + } + + pa_assert_se((fallback_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index)))); + object_path = pa_dbusiface_device_get_path(fallback_source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_source; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_source) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + return; + } + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) + return; + + if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + return; + } + + pa_namereg_set_default_source(c->core, pa_dbusiface_device_get_source(fallback_source)); + + pa_dbus_send_empty_reply(conn, msg); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) { + const char **streams; + unsigned i; + void *state = NULL; + pa_dbusiface_stream *stream; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->playback_streams); + + if (*n == 0) + return NULL; + + streams = pa_xnew(const char *, *n); + + for (i = 0, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL)) + streams[i] = pa_dbusiface_stream_get_path(stream); + + pa_assert(i == *n); + + return streams; +} + +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **playback_streams; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + playback_streams = get_playback_streams(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n); + + pa_xfree(playback_streams); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) { + const char **streams; + unsigned i; + void *state = NULL; + pa_dbusiface_stream *stream; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->record_streams); + + if (*n == 0) + return NULL; + + streams = pa_xnew(const char *, *n); + + for (i = 0, stream = pa_hashmap_iterate(c->record_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->record_streams, &state, NULL)) + streams[i] = pa_dbusiface_stream_get_path(stream); + + pa_assert(i == *n); + + return streams; +} + +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **record_streams; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + record_streams = get_record_streams(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n); + + pa_xfree(record_streams); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_samples(pa_dbusiface_core *c, unsigned *n) { + const char **samples; + unsigned i; + void *state = NULL; + pa_dbusiface_sample *sample; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->samples); + + if (*n == 0) + return NULL; + + samples = pa_xnew(const char *, *n); + + for (i = 0, sample = pa_hashmap_iterate(c->samples, &state, NULL); sample; ++i, sample = pa_hashmap_iterate(c->samples, &state, NULL)) + samples[i] = pa_dbusiface_sample_get_path(sample); + + pa_assert(i == *n); + + return samples; +} + +static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **samples; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + samples = get_samples(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, samples, n); + + pa_xfree(samples); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_modules(pa_dbusiface_core *c, unsigned *n) { + const char **modules; + unsigned i; + void *state = NULL; + pa_dbusiface_module *module; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->modules); + + if (*n == 0) + return NULL; + + modules = pa_xnew(const char *, *n); + + for (i = 0, module = pa_hashmap_iterate(c->modules, &state, NULL); module; ++i, module = pa_hashmap_iterate(c->modules, &state, NULL)) + modules[i] = pa_dbusiface_module_get_path(module); + + pa_assert(i == *n); + + return modules; +} + +static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **modules; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + modules = get_modules(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, modules, n); + + pa_xfree(modules); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_clients(pa_dbusiface_core *c, unsigned *n) { + const char **clients; + unsigned i; + void *state = NULL; + pa_dbusiface_client *client; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->clients); + + if (*n == 0) + return NULL; + + clients = pa_xnew(const char *, *n); + + for (i = 0, client = pa_hashmap_iterate(c->clients, &state, NULL); client; ++i, client = pa_hashmap_iterate(c->clients, &state, NULL)) + clients[i] = pa_dbusiface_client_get_path(client); + + pa_assert(i == *n); + + return clients; +} + +static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **clients; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + clients = get_clients(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, clients, n); + + pa_xfree(clients); +} + +static const char *get_my_client(pa_dbusiface_core *c, DBusConnection *conn) { + pa_client *my_client; + + pa_assert(c); + pa_assert(conn); + + pa_assert_se((my_client = pa_dbus_protocol_get_client(c->dbus_protocol, conn))); + + return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(my_client->index))); +} + +static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char *my_client; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + my_client = get_my_client(c, conn); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &my_client); +} + +static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **extensions; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, extensions, n); + + pa_xfree(extensions); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t interface_revision; + const char *server_name; + const char *version; + dbus_bool_t is_local; + char *username; + char *hostname; + dbus_uint32_t *default_channels; + unsigned n_default_channels; + dbus_uint32_t default_sample_format; + dbus_uint32_t default_sample_rate; + const char **cards; + unsigned n_cards; + const char **sinks; + unsigned n_sinks; + const char *fallback_sink; + const char **sources; + unsigned n_sources; + const char *fallback_source; + const char **playback_streams; + unsigned n_playback_streams; + const char **record_streams; + unsigned n_record_streams; + const char **samples; + unsigned n_samples; + const char **modules; + unsigned n_modules; + const char **clients; + unsigned n_clients; + const char *my_client; + const char **extensions; + unsigned n_extensions; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + interface_revision = INTERFACE_REVISION; + server_name = PACKAGE_NAME; + version = PACKAGE_VERSION; + is_local = get_is_local(conn); + username = pa_get_user_name_malloc(); + hostname = pa_get_host_name_malloc(); + default_channels = get_default_channels(c, &n_default_channels); + default_sample_format = c->core->default_sample_spec.format; + default_sample_rate = c->core->default_sample_spec.rate; + cards = get_cards(c, &n_cards); + sinks = get_sinks(c, &n_sinks); + fallback_sink = c->fallback_sink ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) : NULL; + sources = get_sources(c, &n_sources); + fallback_source = c->fallback_source ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))) : NULL; + playback_streams = get_playback_streams(c, &n_playback_streams); + record_streams = get_record_streams(c, &n_record_streams); + samples = get_samples(c, &n_samples); + modules = get_modules(c, &n_modules); + clients = get_clients(c, &n_clients); + my_client = get_my_client(c, conn); + extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n_extensions); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &server_name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VERSION].property_name, DBUS_TYPE_STRING, &version); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_LOCAL].property_name, DBUS_TYPE_BOOLEAN, &is_local); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_USERNAME].property_name, DBUS_TYPE_STRING, &username); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HOSTNAME].property_name, DBUS_TYPE_STRING, &hostname); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_CHANNELS].property_name, DBUS_TYPE_UINT32, default_channels, n_default_channels); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &default_sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &default_sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARDS].property_name, DBUS_TYPE_OBJECT_PATH, cards, n_cards); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); + + if (fallback_sink) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_sink); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources); + + if (fallback_source) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_source); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLES].property_name, DBUS_TYPE_OBJECT_PATH, samples, n_samples); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MODULES].property_name, DBUS_TYPE_OBJECT_PATH, modules, n_modules); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENTS].property_name, DBUS_TYPE_OBJECT_PATH, clients, n_clients); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MY_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &my_client); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_EXTENSIONS].property_name, DBUS_TYPE_STRING, extensions, n_extensions); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(username); + pa_xfree(hostname); + pa_xfree(default_channels); + pa_xfree(cards); + pa_xfree(sinks); + pa_xfree(sources); + pa_xfree(playback_streams); + pa_xfree(record_streams); + pa_xfree(samples); + pa_xfree(modules); + pa_xfree(clients); + pa_xfree(extensions); +} + +static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *card_name; + pa_card *card; + pa_dbusiface_card *dbus_card; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card."); + return; + } + + pa_assert_se((dbus_card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index)))); + + object_path = pa_dbusiface_card_get_path(dbus_card); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *sink_name; + pa_sink *sink; + pa_dbusiface_device *dbus_sink; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + return; + } + + pa_assert_se((dbus_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index)))); + + object_path = pa_dbusiface_device_get_path(dbus_sink); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *source_name; + pa_source *source; + pa_dbusiface_device *dbus_source; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + return; + } + + pa_assert_se((dbus_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index)))); + + object_path = pa_dbusiface_device_get_path(dbus_source); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *sample_name; + pa_scache_entry *sample; + pa_dbusiface_sample *dbus_sample; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample."); + return; + } + + pa_assert_se((dbus_sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(sample->index)))); + + object_path = pa_dbusiface_sample_get_path(dbus_sample); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + DBusMessageIter msg_iter; + const char *name; + dbus_uint32_t sample_format; + dbus_uint32_t sample_rate; + const dbus_uint32_t *channels; + unsigned n_channels; + const dbus_uint32_t *default_volume; + unsigned n_volume_entries; + pa_proplist *property_list; + const uint8_t *data; + unsigned data_length; + unsigned i; + pa_sample_spec ss; + pa_channel_map map; + pa_memchunk chunk; + uint32_t idx; + pa_dbusiface_sample *dbus_sample = NULL; + pa_scache_entry *sample = NULL; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + chunk.memblock = NULL; + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_format) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_rate) < 0) + return; + + if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &channels, &n_channels) < 0) + return; + + if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &default_volume, &n_volume_entries) < 0) + return; + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_BYTE, &data, &data_length) < 0) + goto finish; + + if (sample_format >= PA_SAMPLE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + goto finish; + } + + if (sample_rate <= 0 || sample_rate > PA_RATE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate."); + goto finish; + } + + if (n_channels == 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map."); + goto finish; + } + + if (n_channels > PA_CHANNELS_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels."); + goto finish; + } + + for (i = 0; i < n_channels; ++i) { + if (channels[i] >= PA_CHANNEL_POSITION_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position."); + goto finish; + } + } + + if (n_volume_entries != 0 && n_volume_entries != n_channels) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The channels and default_volume arguments have different number of elements."); + goto finish; + } + + for (i = 0; i < n_volume_entries; ++i) { + if (default_volume[i] > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + goto finish; + } + } + + if (data_length == 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty data."); + goto finish; + } + + if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too big sample."); + goto finish; + } + + ss.format = sample_format; + ss.rate = sample_rate; + ss.channels = n_channels; + + if (!pa_frame_aligned(data_length, &ss)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The sample length in bytes doesn't align with the sample format and channels."); + goto finish; + } + + map.channels = n_channels; + for (i = 0; i < n_channels; ++i) + map.map[i] = channels[i]; + + chunk.memblock = pa_memblock_new(c->core->mempool, data_length); + chunk.index = 0; + chunk.length = data_length; + + memcpy(pa_memblock_acquire(chunk.memblock), data, data_length); + pa_memblock_release(chunk.memblock); + + if (pa_scache_add_item(c->core, name, &ss, &map, &chunk, property_list, &idx) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Adding the sample failed."); + goto finish; + } + + sample = pa_idxset_get_by_index(c->core->scache, idx); + + if (n_volume_entries > 0) { + sample->volume.channels = n_channels; + for (i = 0; i < n_volume_entries; ++i) + sample->volume.values[i] = default_volume[i]; + sample->volume_is_set = TRUE; + } else { + sample->volume_is_set = FALSE; + } + + dbus_sample = pa_dbusiface_sample_new(sample, OBJECT_PATH); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample); + + object_path = pa_dbusiface_sample_get_path(dbus_sample); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); + +finish: + if (property_list) + pa_proplist_free(property_list); + + if (chunk.memblock) + pa_memblock_unref(chunk.memblock); +} + +static pa_bool_t contains_space(const char *string) { + const char *p; + + pa_assert(string); + + for (p = string; *p; ++p) { + if (isspace(*p)) + return TRUE; + } + + return FALSE; +} + +static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + char *name; + const char *key; + const char *value; + char *escaped_value; + pa_strbuf *arg_buffer = NULL; + pa_module *module; + pa_dbusiface_module *dbus_module; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (c->core->disallow_module_loading) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module loading."); + return; + } + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) + return; + + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + return; + } + + if (dbus_message_iter_get_element_type(&msg_iter) != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + return; + } + + arg_buffer = pa_strbuf_new(); + + dbus_message_iter_recurse(&msg_iter, &dict_iter); + + while (dbus_message_iter_has_next(&dict_iter)) { + if (!pa_strbuf_isempty(arg_buffer)) + pa_strbuf_putc(arg_buffer, ' '); + + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + goto finish; + } + + dbus_message_iter_get_basic(&dict_iter, &key); + + if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name."); + goto finish; + } + + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. A string was expected."); + goto finish; + } + + dbus_message_iter_get_basic(&dict_iter, &value); + + escaped_value = pa_escape(value, "\""); + pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value); + pa_xfree(escaped_value); + } + + if (!(module = pa_module_load(c->core, name, pa_strbuf_tostring(arg_buffer)))) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module."); + goto finish; + } + + dbus_module = pa_dbusiface_module_new(module, OBJECT_PATH); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module); + + object_path = pa_dbusiface_module_get_path(dbus_module); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); + +finish: + if (arg_buffer) + pa_strbuf_free(arg_buffer); +} + +static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (c->core->disallow_exit) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow exiting."); + return; + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_core_exit(c->core, FALSE, 0); +} + +static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char *signal; + char **objects = NULL; + int n_objects; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + goto finish; + } + + pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL, objects, n_objects); + + pa_dbus_send_empty_reply(conn, msg); + +finish: + dbus_free_string_array(objects); +} + +static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char *signal; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_card *card = NULL; + pa_dbusiface_device *device = NULL; + pa_dbusiface_stream *stream = NULL; + pa_dbusiface_sample *sample = NULL; + pa_dbusiface_module *module = NULL; + pa_dbusiface_client *client = NULL; + DBusMessage *signal = NULL; + const char *object_path = NULL; + pa_sink *new_fallback_sink = NULL; + pa_source *new_fallback_source = NULL; + + pa_assert(c); + + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SERVER: + new_fallback_sink = pa_namereg_get_default_sink(core); + new_fallback_source = pa_namereg_get_default_source(core); + + if (c->fallback_sink != new_fallback_sink) { + c->fallback_sink = new_fallback_sink; + + if (new_fallback_sink) { + pa_assert_se((device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))); + + object_path = pa_dbusiface_device_get_path(device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } + + if (c->fallback_source != new_fallback_source) { + c->fallback_source = new_fallback_source; + + if (new_fallback_source) { + pa_assert_se((device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))); + + object_path = pa_dbusiface_device_get_path(device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } + break; + + case PA_SUBSCRIPTION_EVENT_CARD: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + + object_path = pa_dbusiface_card_get_path(card); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((card = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_card_get_path(card); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_card_free(card); + } + break; + + case PA_SUBSCRIPTION_EVENT_SINK: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); + object_path = pa_dbusiface_device_get_path(device); + + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sinks_by_path, object_path, device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))); + object_path = pa_dbusiface_device_get_path(device); + pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_device_free(device); + } + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); + object_path = pa_dbusiface_device_get_path(device); + + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sources_by_path, object_path, device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))); + object_path = pa_dbusiface_device_get_path(device); + pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_device_free(device); + } + break; + + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((stream = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_stream_free(stream); + } + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((stream = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_stream_free(stream); + } + break; + + case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + /* We may have created the pa_dbusiface_sample object already + * in handle_upload_sample. */ + if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { + sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); + } + + object_path = pa_dbusiface_sample_get_path(sample); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((sample = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_sample_get_path(sample); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_sample_free(sample); + } + break; + + case PA_SUBSCRIPTION_EVENT_MODULE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + /* We may have created the pa_dbusiface_module object already + * in handle_load_module. */ + if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { + module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); + } + + object_path = pa_dbusiface_module_get_path(module); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((module = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_module_get_path(module); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_module_free(module); + } + break; + + case PA_SUBSCRIPTION_EVENT_CLIENT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + + object_path = pa_dbusiface_client_get_path(client); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((client = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_client_get_path(client); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_client_free(client); + } + break; + } + + if (signal) { + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + } +} + +pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { + pa_dbusiface_core *c; + pa_card *card; + pa_sink *sink; + pa_source *source; + pa_dbusiface_device *device; + pa_sink_input *sink_input; + pa_source_output *source_output; + pa_scache_entry *sample; + pa_module *module; + pa_client *client; + uint32_t idx; + + pa_assert(core); + + c = pa_xnew(pa_dbusiface_core, 1); + c->core = core; + c->subscription = pa_subscription_new(core, PA_SUBSCRIPTION_MASK_ALL, subscription_cb, c); + c->dbus_protocol = pa_dbus_protocol_get(core); + c->cards = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->sinks_by_index = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->sinks_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->sources_by_index = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->sources_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->playback_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->record_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->samples = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->modules = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->fallback_sink = pa_namereg_get_default_sink(core); + c->fallback_source = pa_namereg_get_default_source(core); + + for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); + + for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) { + device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); + } + + for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) { + device = pa_dbusiface_device_new_source(source, OBJECT_PATH); + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); + } + + for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx)) + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(sink_input, OBJECT_PATH)); + + for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx)) + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(source_output, OBJECT_PATH)); + + for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx)) + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(sample, OBJECT_PATH)); + + for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx)) + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module, OBJECT_PATH)); + + for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx)) + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(client, OBJECT_PATH)); + + pa_dbus_protocol_add_interface(c->dbus_protocol, OBJECT_PATH, &core_interface_info, c); + + return c; +} + +static void free_card_cb(void *p, void *userdata) { + pa_dbusiface_card *c = p; + + pa_assert(c); + + pa_dbusiface_card_free(c); +} + +static void free_device_cb(void *p, void *userdata) { + pa_dbusiface_device *d = p; + + pa_assert(d); + + pa_dbusiface_device_free(d); +} + +static void free_stream_cb(void *p, void *userdata) { + pa_dbusiface_stream *s = p; + + pa_assert(s); + + pa_dbusiface_stream_free(s); +} + +static void free_sample_cb(void *p, void *userdata) { + pa_dbusiface_sample *s = p; + + pa_assert(s); + + pa_dbusiface_sample_free(s); +} + +static void free_module_cb(void *p, void *userdata) { + pa_dbusiface_module *m = p; + + pa_assert(m); + + pa_dbusiface_module_free(m); +} + +static void free_client_cb(void *p, void *userdata) { + pa_dbusiface_client *c = p; + + pa_assert(c); + + pa_dbusiface_client_free(c); +} + +void pa_dbusiface_core_free(pa_dbusiface_core *c) { + pa_assert(c); + + pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, INTERFACE_CORE); + + pa_subscription_free(c->subscription); + pa_hashmap_free(c->cards, free_card_cb, NULL); + pa_hashmap_free(c->sinks_by_index, free_device_cb, NULL); + pa_hashmap_free(c->sinks_by_path, NULL, NULL); + pa_hashmap_free(c->sources_by_index, free_device_cb, NULL); + pa_hashmap_free(c->sources_by_path, NULL, NULL); + pa_hashmap_free(c->playback_streams, free_stream_cb, NULL); + pa_hashmap_free(c->record_streams, free_stream_cb, NULL); + pa_hashmap_free(c->samples, free_sample_cb, NULL); + pa_hashmap_free(c->modules, free_module_cb, NULL); + pa_hashmap_free(c->clients, free_client_cb, NULL); + + pa_dbus_protocol_unref(c->dbus_protocol); + + pa_xfree(c); +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h new file mode 100644 index 00000000..964a37bd --- /dev/null +++ b/src/modules/dbus/iface-core.h @@ -0,0 +1,38 @@ +#ifndef foodbusifacecorehfoo +#define foodbusifacecorehfoo + +/*** + 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 interface org.PulseAudio.Core1. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Core interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_core pa_dbusiface_core; + +pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); +void pa_dbusiface_core_free(pa_dbusiface_core *c); + +#endif diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c new file mode 100644 index 00000000..3b3795eb --- /dev/null +++ b/src/modules/dbus/iface-device.c @@ -0,0 +1,105 @@ +/*** + 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 "iface-device.h" + +#define SINK_OBJECT_NAME "sink" +#define SOURCE_OBJECT_NAME "source" + +enum device_type { + DEVICE_TYPE_SINK, + DEVICE_TYPE_SOURCE +}; + +struct pa_dbusiface_device { + union { + pa_sink *sink; + pa_source *source; + }; + enum device_type type; + char *path; +}; + +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix) { + pa_dbusiface_device *d; + + pa_assert(sink); + pa_assert(path_prefix); + + d = pa_xnew(pa_dbusiface_device, 1); + d->sink = pa_sink_ref(sink); + d->type = DEVICE_TYPE_SINK; + d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SINK_OBJECT_NAME, sink->index); + + return d; +} + +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix) { + pa_dbusiface_device *d; + + pa_assert(source); + pa_assert(path_prefix); + + d = pa_xnew(pa_dbusiface_device, 1); + d->source = pa_source_ref(source); + d->type = DEVICE_TYPE_SOURCE; + d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SOURCE_OBJECT_NAME, source->index); + + return d; +} + +void pa_dbusiface_device_free(pa_dbusiface_device *d) { + pa_assert(d); + + if (d->type == DEVICE_TYPE_SINK) + pa_sink_unref(d->sink); + else + pa_source_unref(d->source); + + pa_xfree(d->path); + pa_xfree(d); +} + +const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d) { + pa_assert(d); + + return d->path; +} + +pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d) { + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SINK); + + return d->sink; +} + +pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d) { + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SOURCE); + + return d->source; +} diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h new file mode 100644 index 00000000..81ad1d84 --- /dev/null +++ b/src/modules/dbus/iface-device.h @@ -0,0 +1,46 @@ +#ifndef foodbusifacedevicehfoo +#define foodbusifacedevicehfoo + +/*** + 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 interfaces org.PulseAudio.Core1.Device, + * org.PulseAudio.Core1.Sink and org.PulseAudio.Core1.Source. + * + * See http://pulseaudio.org/wiki/DBusInterface for the interface + * documentation. + */ + +#include +#include + +typedef struct pa_dbusiface_device pa_dbusiface_device; + +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix); +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix); +void pa_dbusiface_device_free(pa_dbusiface_device *d); + +const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d); + +pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d); +pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d); + +#endif diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c new file mode 100644 index 00000000..1c95f9e6 --- /dev/null +++ b/src/modules/dbus/iface-module.c @@ -0,0 +1,61 @@ +/*** + 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 "iface-module.h" + +#define OBJECT_NAME "module" + +struct pa_dbusiface_module { + pa_module *module; + char *path; +}; + +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix) { + pa_dbusiface_module *m; + + pa_assert(module); + pa_assert(path_prefix); + + m = pa_xnew(pa_dbusiface_module, 1); + m->module = module; + m->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, module->index); + + return m; +} + +void pa_dbusiface_module_free(pa_dbusiface_module *m) { + pa_assert(m); + + pa_xfree(m->path); + pa_xfree(m); +} + +const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m) { + pa_assert(m); + + return m->path; +} diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h new file mode 100644 index 00000000..7f683e6a --- /dev/null +++ b/src/modules/dbus/iface-module.h @@ -0,0 +1,40 @@ +#ifndef foodbusifacemodulehfoo +#define foodbusifacemodulehfoo + +/*** + 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 interface org.PulseAudio.Core1.Module. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Module interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_module pa_dbusiface_module; + +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix); +void pa_dbusiface_module_free(pa_dbusiface_module *m); + +const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m); + +#endif diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c new file mode 100644 index 00000000..b4a308a2 --- /dev/null +++ b/src/modules/dbus/iface-sample.c @@ -0,0 +1,61 @@ +/*** + 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 "iface-sample.h" + +#define OBJECT_NAME "sample" + +struct pa_dbusiface_sample { + pa_scache_entry *sample; + char *path; +}; + +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix) { + pa_dbusiface_sample *s; + + pa_assert(sample); + pa_assert(path_prefix); + + s = pa_xnew(pa_dbusiface_sample, 1); + s->sample = sample; + s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, sample->index); + + return s; +} + +void pa_dbusiface_sample_free(pa_dbusiface_sample *s) { + pa_assert(s); + + pa_xfree(s->path); + pa_xfree(s); +} + +const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *s) { + pa_assert(s); + + return s->path; +} diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h new file mode 100644 index 00000000..1b85404e --- /dev/null +++ b/src/modules/dbus/iface-sample.h @@ -0,0 +1,40 @@ +#ifndef foodbusifacesamplehfoo +#define foodbusifacesamplehfoo + +/*** + 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 interface org.PulseAudio.Core1.Sample. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Sample interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_sample pa_dbusiface_sample; + +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix); +void pa_dbusiface_sample_free(pa_dbusiface_sample *c); + +const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c); + +#endif diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c new file mode 100644 index 00000000..1d9ffee6 --- /dev/null +++ b/src/modules/dbus/iface-stream.c @@ -0,0 +1,91 @@ +/*** + 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 "iface-stream.h" + +#define PLAYBACK_OBJECT_NAME "playback_stream" +#define RECORD_OBJECT_NAME "record_stream" + +enum stream_type { + STREAM_TYPE_PLAYBACK, + STREAM_TYPE_RECORD +}; + +struct pa_dbusiface_stream { + union { + pa_sink_input *sink_input; + pa_source_output *source_output; + }; + enum stream_type type; + char *path; +}; + +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix) { + pa_dbusiface_stream *s; + + pa_assert(sink_input); + pa_assert(path_prefix); + + s = pa_xnew(pa_dbusiface_stream, 1); + s->sink_input = pa_sink_input_ref(sink_input); + s->type = STREAM_TYPE_PLAYBACK; + s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, PLAYBACK_OBJECT_NAME, sink_input->index); + + return s; +} + +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix) { + pa_dbusiface_stream *s; + + pa_assert(source_output); + pa_assert(path_prefix); + + s = pa_xnew(pa_dbusiface_stream, 1); + s->source_output = pa_source_output_ref(source_output); + s->type = STREAM_TYPE_RECORD; + s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, RECORD_OBJECT_NAME, source_output->index); + + return s; +} + +void pa_dbusiface_stream_free(pa_dbusiface_stream *s) { + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + pa_sink_input_unref(s->sink_input); + else + pa_source_output_unref(s->source_output); + + pa_xfree(s->path); + pa_xfree(s); +} + +const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) { + pa_assert(s); + + return s->path; +} diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h new file mode 100644 index 00000000..cc2f3d61 --- /dev/null +++ b/src/modules/dbus/iface-stream.h @@ -0,0 +1,42 @@ +#ifndef foodbusifacestreamhfoo +#define foodbusifacestreamhfoo + +/*** + 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 interface org.PulseAudio.Core1.Stream. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Stream interface + * documentation. + */ + +#include +#include + +typedef struct pa_dbusiface_stream pa_dbusiface_stream; + +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix); +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix); +void pa_dbusiface_stream_free(pa_dbusiface_stream *s); + +const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s); + +#endif diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c new file mode 100644 index 00000000..807d32da --- /dev/null +++ b/src/modules/dbus/module-dbus-protocol.c @@ -0,0 +1,580 @@ +/*** + 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 "iface-core.h" + +#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_dbus_protocol *dbus_protocol; + pa_dbusiface_core *core_iface; +}; + +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_protocol_unregister_connection(c->server->userdata->dbus_protocol, 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_protocol_register_connection(s->userdata->dbus_protocol, new_connection, c->client) >= 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->dbus_protocol = pa_dbus_protocol_get(m->core); + u->core_iface = pa_dbusiface_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_iface) + pa_dbusiface_core_free(u->core_iface); + + 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); + + if (u->dbus_protocol) + pa_dbus_protocol_unref(u->dbus_protocol); + + pa_xfree(u); + m->userdata = NULL; +} -- cgit From 018810ec9a96d63446bdc9a82d6c931779f7a97d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 27 Jul 2009 20:01:39 +0300 Subject: Bug fixing and minor cleanups. --- src/modules/dbus/iface-core.c | 95 ++++++++++++++++++++----------------------- src/pulsecore/dbus-util.c | 73 ++++++++++++++++++++++++++++----- src/pulsecore/dbus-util.h | 9 ++-- 3 files changed, 111 insertions(+), 66 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 31f12603..d17499c1 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -220,7 +220,7 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .receive_cb = handle_upload_sample }, [METHOD_HANDLER_LOAD_MODULE] = { .method_name = "LoadModule", - .arguments = upload_sample_args, + .arguments = load_module_args, .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_load_module }, [METHOD_HANDLER_EXIT] = { @@ -424,11 +424,8 @@ static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_core *c = userdata; pa_channel_map new_channel_map; - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - DBusMessageIter array_iter; dbus_uint32_t *default_channels; - int n_channels; + unsigned n_channels; unsigned i; pa_assert(conn); @@ -437,37 +434,16 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, pa_channel_map_init(&new_channel_map); - pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_channels, &n_channels) < 0) return; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Variant doesn't contain an array."); - return; - } - dbus_message_iter_recurse(&variant_iter, &array_iter); - - if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_UINT32) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Array type is not uint32."); + if (n_channels <= 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); return; } - dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels); - - if (n_channels <= 0) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); + if (n_channels > PA_CHANNELS_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); return; } @@ -475,7 +451,7 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, for (i = 0; i < new_channel_map.channels; ++i) { if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u.", default_channels[i]); return; } @@ -547,7 +523,7 @@ static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *ms return; if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate."); return; } @@ -1149,7 +1125,7 @@ static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1181,7 +1157,7 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1213,7 +1189,7 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1245,7 +1221,7 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1433,6 +1409,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use pa_dbusiface_core *c = userdata; DBusMessageIter msg_iter; DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; char *name; const char *key; const char *value; @@ -1441,6 +1418,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use pa_module *module; pa_dbusiface_module *dbus_module; const char *object_path; + int arg_type; pa_assert(conn); pa_assert(msg); @@ -1459,13 +1437,18 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) return; - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(&msg_iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. A dictionary from strings to strings was expected."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", (char) arg_type); return; } - if (dbus_message_iter_get_element_type(&msg_iter) != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + arg_type = dbus_message_iter_get_element_type(&msg_iter); + if (arg_type != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); return; } @@ -1473,28 +1456,38 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use dbus_message_iter_recurse(&msg_iter, &dict_iter); - while (dbus_message_iter_has_next(&dict_iter)) { + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { if (!pa_strbuf_isempty(arg_buffer)) pa_strbuf_putc(arg_buffer, ' '); - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); + + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_STRING) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); goto finish; } - dbus_message_iter_get_basic(&dict_iter, &key); + dbus_message_iter_get_basic(&dict_entry_iter, &key); + dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key); goto finish; } - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. A string was expected."); + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_STRING) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); goto finish; } - dbus_message_iter_get_basic(&dict_iter, &value); + dbus_message_iter_get_basic(&dict_entry_iter, &value); + + dbus_message_iter_next(&dict_iter); escaped_value = pa_escape(value, "\""); pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value); @@ -1549,7 +1542,7 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); goto finish; } @@ -1574,7 +1567,7 @@ static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage * dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index cfc3e8cb..43dbd6b2 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -446,18 +446,25 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) { } } -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message) { +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) { + va_list ap; + char *message; DBusMessage *reply = NULL; pa_assert(c); pa_assert(in_reply_to); pa_assert(name); - pa_assert(message); + pa_assert(format); + va_start(ap, format); + message = pa_vsprintf_malloc(format, ap); + va_end(ap); pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message))); pa_assert_se(dbus_connection_send(c, reply, NULL)); dbus_message_unref(reply); + + pa_xfree(message); } void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) { @@ -655,36 +662,71 @@ int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int dbus_message_iter_recurse(&msg_iter, &variant_iter); - if (dbus_message_iter_get_arg_type(&variant_iter) != type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Variant has wrong contained type."); + if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0) + return -1; + + return 0; +} + +int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) { + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + + pa_assert(c); + pa_assert(msg); + pa_assert(dbus_type_is_fixed(item_type)); + pa_assert(data); + pa_assert(n); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); return -1; } - dbus_message_iter_get_basic(&variant_iter, data); + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); + return -1; + } + + dbus_message_iter_recurse(&msg_iter, &variant_iter); + + if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0) + return -1; return 0; } int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { + int arg_type; + pa_assert(c); pa_assert(msg); pa_assert(iter); pa_assert(dbus_type_is_basic(type)); pa_assert(data); - if (dbus_message_iter_get_arg_type(iter) != type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != type) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type); return -1; } dbus_message_iter_get_basic(iter, data); + dbus_message_iter_next(iter); + return 0; } int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { DBusMessageIter array_iter; int signed_n; + int arg_type; + int element_type; pa_assert(c); pa_assert(msg); @@ -693,13 +735,18 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage pa_assert(array); pa_assert(n); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type); return -1; } - if (dbus_message_iter_get_element_type(iter) != item_type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type."); + element_type = dbus_message_iter_get_element_type(iter); + if (element_type != item_type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type); return -1; } @@ -707,6 +754,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); + dbus_message_iter_next(iter); + pa_assert(signed_n >= 0); *n = signed_n; @@ -769,6 +818,8 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); } + dbus_message_iter_next(iter); + return proplist; fail: diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 1a8aeac9..97aae372 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -63,7 +63,7 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); void pa_dbus_free_pending_list(pa_dbus_pending **p); /* Sends an error message as the reply to the given message. */ -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message); +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); @@ -76,10 +76,11 @@ void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); -/* Helper function for extracting the value argument of a Set call for a - * property with a basic type. If the message is invalid, an error reply is - * sent and a negative number is returned. */ +/* Helper functions for extracting the value argument of a Set call. If the + * message is invalid, an error reply is sent and a negative number is + * returned. */ int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); +int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n); /* If the arguments can't be read from the iterator, an error reply is sent and * a negative number is returned. */ -- cgit From b061957e57f74d7aa51bde9d24dd5e5c75af9497 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Thu, 30 Jul 2009 13:11:32 +0300 Subject: dbus/iface-core.c: Make sure D-Bus objects are created only once. --- src/modules/dbus/iface-core.c | 48 +++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index d17499c1..cdfd2a3c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1632,8 +1632,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); - pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { + card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + } object_path = pa_dbusiface_card_get_path(card); @@ -1654,11 +1656,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); - object_path = pa_dbusiface_device_get_path(device); + if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { + device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); + } - pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sinks_by_path, object_path, device); + object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); @@ -1677,11 +1681,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); - object_path = pa_dbusiface_device_get_path(device); + if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { + device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); + } - pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sources_by_path, object_path, device); + object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); @@ -1700,8 +1706,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); - pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { + stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + } object_path = pa_dbusiface_stream_get_path(stream); @@ -1722,8 +1730,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); - pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { + stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + } object_path = pa_dbusiface_stream_get_path(stream); @@ -1744,8 +1754,6 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - /* We may have created the pa_dbusiface_sample object already - * in handle_upload_sample. */ if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH); pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); @@ -1770,8 +1778,6 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - /* We may have created the pa_dbusiface_module object already - * in handle_load_module. */ if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); @@ -1796,8 +1802,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); - pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { + client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + } object_path = pa_dbusiface_client_get_path(client); -- cgit From c354a08fe3cf2468688264124b763efb1bf1df85 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 31 Jul 2009 12:05:49 +0300 Subject: dbus-protocol: Implement extension registration. --- src/pulsecore/protocol-dbus.c | 91 ++++++++++++++++++++++++++++++++++--------- src/pulsecore/protocol-dbus.h | 39 +++++++++++++++++-- 2 files changed, 108 insertions(+), 22 deletions(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index fb7d168d..81a6c024 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -41,7 +41,9 @@ struct pa_dbus_protocol { pa_core *core; pa_hashmap *objects; /* Object path -> struct object_entry */ pa_hashmap *connections; /* DBusConnection -> struct connection_entry */ - pa_hashmap *extensions; /* String -> anything */ + pa_idxset *extensions; /* Strings */ + + pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX]; }; struct object_entry { @@ -109,6 +111,7 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { pa_dbus_protocol *p; + unsigned i; pa_assert(c); @@ -117,7 +120,10 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { p->core = c; p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - p->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i) + pa_hook_init(&p->hooks[i], p); pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0); @@ -143,6 +149,8 @@ pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) { } void pa_dbus_protocol_unref(pa_dbus_protocol *p) { + unsigned i; + pa_assert(p); pa_assert(PA_REFCNT_VALUE(p) >= 1); @@ -151,11 +159,14 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) { pa_assert(pa_hashmap_isempty(p->objects)); pa_assert(pa_hashmap_isempty(p->connections)); - pa_assert(pa_hashmap_isempty(p->extensions)); + pa_assert(pa_idxset_isempty(p->extensions)); pa_hashmap_free(p->objects, NULL, NULL); pa_hashmap_free(p->connections, NULL, NULL); - pa_hashmap_free(p->extensions, NULL, NULL); + pa_idxset_free(p->extensions, NULL, NULL); + + for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i) + pa_hook_done(&p->hooks[i]); pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); @@ -660,6 +671,8 @@ static void property_handler_free_cb(void *p, void *userdata) { pa_xfree((char *) h->property_name); pa_xfree((char *) h->type); + + pa_xfree(h); } int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) { @@ -792,6 +805,18 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection * return 0; } +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + + if (!(conn_entry = pa_hashmap_get(p->connections, conn))) + return NULL; + + return conn_entry->client; +} + void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { struct connection_entry *conn_entry; pa_idxset *object_set; @@ -893,18 +918,6 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { } } -pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { - struct connection_entry *conn_entry; - - pa_assert(p); - pa_assert(conn); - - if (!(conn_entry = pa_hashmap_get(p->connections, conn))) - return NULL; - - return conn_entry->client; -} - const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { const char **extensions; const char *ext_name; @@ -914,17 +927,59 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { pa_assert(p); pa_assert(n); - *n = pa_hashmap_size(p->extensions); + *n = pa_idxset_size(p->extensions); if (*n <= 0) return NULL; extensions = pa_xnew(const char *, *n); - while (pa_hashmap_iterate(p->extensions, &state, (const void **) &ext_name)) { + while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) { extensions[i] = ext_name; ++i; } return extensions; } + +int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) { + char *internal_name; + + pa_assert(p); + pa_assert(name); + + internal_name = pa_xstrdup(name); + + if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) { + pa_xfree(internal_name); + return -1; + } + + pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name); + + return 0; +} + +int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) { + char *internal_name; + + pa_assert(p); + pa_assert(name); + + if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL))) + return -1; + + pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name); + + pa_xfree(internal_name); + + return 0; +} + +pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) { + pa_assert(p); + pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX); + pa_assert(cb); + + return pa_hook_connect(&p->hooks[hook], prio, cb, data); +} diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index 27198f48..f2b1b50b 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -119,9 +119,12 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con * registered. */ int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client); -/* Returns a negative number if the connection wasn't registered. */ +/* Returns a negative number if the connection isn't registered. */ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn); +/* Returns NULL if the connection isn't registered. */ +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); + /* Enables signal receiving for the given connection. The connection must have * been registered earlier. * @@ -145,9 +148,6 @@ void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); -/* Returns NULL if the connection isn't registered. */ -pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); - /* Returns an array of extension identifier strings. The strings pointers point * to the internal copies, so don't free the strings. The caller must free the * array, however. Also, do not save the returned pointer or any of the string @@ -155,4 +155,35 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn * need to save the array, copy it. */ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n); +/* Modules that want to provide a D-Bus interface for clients should register + * an identifier that the clients can use to check whether the additional + * functionality is available. + * + * This function registers the extension with the given name. It is recommended + * that the name follows the D-Bus interface naming convention, so that the + * names remain unique in case there will be at some point in the future + * extensions that aren't included with the main PulseAudio source tree. For + * in-tree extensions the convention is to use the org.PulseAudio.Ext + * namespace. + * + * It is suggested that the name contains a version number, and whenever the + * extension interface is modified in non-backwards compatible way, the version + * number is incremented. + * + * Fails and returns a negative number if the extension is already registered. + */ +int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name); + +/* Returns a negative number if the extension isn't registered. */ +int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name); + +/* All hooks have the pa_dbus_protocol object as hook data. */ +typedef enum pa_dbus_protocol_hook { + PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, /* Extension name as call data. */ + PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, /* Extension name as call data. */ + PA_DBUS_PROTOCOL_HOOK_MAX +} pa_dbus_protocol_hook_t; + +pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); + #endif -- cgit From 68cb63c0d9b1493ebc4274f9ea1fb05a8c273574 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 31 Jul 2009 12:06:53 +0300 Subject: dbusiface-core: Send signals whenever extensions are registered and unregistered. --- src/modules/dbus/iface-core.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index cdfd2a3c..e2e3be2c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -112,6 +112,9 @@ struct pa_dbusiface_core { pa_sink *fallback_sink; pa_source *fallback_source; + + pa_hook_slot *extension_registered_slot; + pa_hook_slot *extension_unregistered_slot; }; enum property_handler_index { @@ -259,6 +262,8 @@ enum signal_index { SIGNAL_MODULE_REMOVED, SIGNAL_NEW_CLIENT, SIGNAL_CLIENT_REMOVED, + SIGNAL_NEW_EXTENSION, + SIGNAL_EXTENSION_REMOVED, SIGNAL_MAX }; @@ -280,6 +285,8 @@ static pa_dbus_arg_info new_module_args[] = { { "module", static pa_dbus_arg_info module_removed_args[] = { { "module", "o", NULL } }; static pa_dbus_arg_info new_client_args[] = { { "client", "o", NULL } }; static pa_dbus_arg_info client_removed_args[] = { { "client", "o", NULL } }; +static pa_dbus_arg_info new_extension_args[] = { { "extension", "s", NULL } }; +static pa_dbus_arg_info extension_removed_args[] = { { "extension", "s", NULL } }; static pa_dbus_signal_info signals[SIGNAL_MAX] = { [SIGNAL_NEW_CARD] = { .name = "NewCard", .arguments = new_card_args, .n_arguments = 1 }, @@ -300,6 +307,8 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { [SIGNAL_MODULE_REMOVED] = { .name = "ModuleRemoved", .arguments = module_removed_args, .n_arguments = 1 }, [SIGNAL_NEW_CLIENT] = { .name = "NewClient", .arguments = new_client_args, .n_arguments = 1 }, [SIGNAL_CLIENT_REMOVED] = { .name = "ClientRemoved", .arguments = client_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_EXTENSION] = { .name = "NewExtension", .arguments = new_extension_args, .n_arguments = 1 }, + [SIGNAL_EXTENSION_REMOVED] = { .name = "ExtensionRemoved", .arguments = extension_removed_args, .n_arguments = 1 } }; static pa_dbus_interface_info core_interface_info = { @@ -1831,6 +1840,40 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } } +static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data, void *slot_data) { + pa_dbusiface_core *c = slot_data; + const char *ext_name = call_data; + DBusMessage *signal = NULL; + + pa_assert(c); + pa_assert(ext_name); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + + return PA_HOOK_OK; +} + +static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_data, void *slot_data) { + pa_dbusiface_core *c = slot_data; + const char *ext_name = call_data; + DBusMessage *signal = NULL; + + pa_assert(c); + pa_assert(ext_name); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + + return PA_HOOK_OK; +} + pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { pa_dbusiface_core *c; pa_card *card; @@ -1862,6 +1905,8 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); c->fallback_sink = pa_namereg_get_default_sink(core); c->fallback_source = pa_namereg_get_default_source(core); + c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); + c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); @@ -1962,6 +2007,8 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hashmap_free(c->samples, free_sample_cb, NULL); pa_hashmap_free(c->modules, free_module_cb, NULL); pa_hashmap_free(c->clients, free_client_cb, NULL); + pa_hook_slot_free(c->extension_registered_slot); + pa_hook_slot_free(c->extension_unregistered_slot); pa_dbus_protocol_unref(c->dbus_protocol); -- cgit From a1ba80bc4e715b8ce77f55676bc63b02c1a82d3c Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 1 Aug 2009 08:26:51 +0300 Subject: dbusiface-core: Don't die if we get a default sink/source change event before the new default device is actually created. --- src/modules/dbus/iface-core.c | 50 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e2e3be2c..69c1bd25 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1609,9 +1609,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_sink != new_fallback_sink) { c->fallback_sink = new_fallback_sink; - if (new_fallback_sink) { - pa_assert_se((device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))); - + if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); @@ -1625,9 +1623,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_source != new_fallback_source) { c->fallback_source = new_fallback_source; - if (new_fallback_source) { - pa_assert_se((device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))); - + if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); @@ -1665,8 +1661,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx); + if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); + device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } @@ -1676,6 +1674,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + + if (c->fallback_sink && pa_streq(c->fallback_sink->name, sink->name)) { + /* We have got default sink change event, but at that point + * the D-Bus sink object wasn't created yet. Now that the + * object is created, let's send the fallback sink change + * signal. */ + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))); object_path = pa_dbusiface_device_get_path(device); @@ -1690,8 +1705,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_source *source = pa_idxset_get_by_index(core->sources, idx); + if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); + device = pa_dbusiface_device_new_source(source, OBJECT_PATH); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } @@ -1701,6 +1718,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + + if (c->fallback_source && pa_streq(c->fallback_source->name, source->name)) { + /* We have got default source change event, but at that + * point the D-Bus source object wasn't created yet. Now + * that the object is created, let's send the fallback + * source change signal. */ + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))); object_path = pa_dbusiface_device_get_path(device); -- cgit From 8c840572c7e2e560efcefe66707527a1dc4d16a4 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 2 Aug 2009 11:12:21 +0300 Subject: dbus-protocol: Add debugging output (temporary change). --- src/pulsecore/protocol-dbus.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 81a6c024..475b952f 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -306,6 +306,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, return FOUND_METHOD; } + pa_log("find_handler_by_method() failed."); return NO_SUCH_METHOD; } @@ -327,8 +328,10 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry if (*interface) { if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) return FOUND_GET_ALL; - else + else { + pa_log("GetAll message has unknown interface: %s", interface); return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ + } } else { pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); return FOUND_GET_ALL; @@ -378,8 +381,10 @@ static enum find_result_t find_handler(struct object_entry *obj_entry, if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) return FOUND_METHOD; - else + else { + pa_log("Message has unknown interface or there's no method handler."); return NO_SUCH_METHOD; + } } else { /* The method call doesn't contain an interface. */ if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { @@ -411,6 +416,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message)); + pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || @@ -624,6 +631,8 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, if (obj_entry_created) register_object(p, obj_entry); + pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no"); + return 0; fail: -- cgit From 805af5e8010228bee7521fbd1148e167cfb21e77 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 3 Aug 2009 19:36:19 +0300 Subject: dbus-util: Fix broken proplist reading logic. --- src/pulsecore/dbus-util.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index 43dbd6b2..5db7f218 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -765,6 +765,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + int arg_type; pa_proplist *proplist = NULL; const char *key; const uint8_t *value; @@ -774,13 +776,18 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM pa_assert(msg); pa_assert(iter); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); return NULL; } - if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + arg_type = dbus_message_iter_get_element_type(iter); + if (arg_type != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type); return NULL; } @@ -788,34 +795,45 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM dbus_message_iter_recurse(iter, &dict_iter); - while (dbus_message_iter_has_next(&dict_iter)) { - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { + dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); + + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_STRING) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); goto fail; } - dbus_message_iter_get_basic(&dict_iter, &key); + dbus_message_iter_get_basic(&dict_entry_iter, &key); + dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key)) { pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); goto fail; } - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type); goto fail; } - if (dbus_message_iter_get_element_type(&dict_iter) != DBUS_TYPE_BYTE) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type. A byte was expected."); + arg_type = dbus_message_iter_get_element_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_BYTE) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type); goto fail; } - dbus_message_iter_get_fixed_array(&dict_iter, &value, &value_length); + dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length); pa_assert(value_length >= 0); pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); + + dbus_message_iter_next(&dict_iter); } dbus_message_iter_next(iter); -- cgit From d9d166a691cd5ecc786b73f1377244b8d51b4327 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 3 Aug 2009 19:38:02 +0300 Subject: stream-restore: Expose module to D-Bus. --- src/Makefile.am | 5 + src/modules/module-stream-restore.c | 1055 ++++++++++++++++++++++++++++++++++- 2 files changed, 1055 insertions(+), 5 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 47eb6479..07f81a6b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1536,6 +1536,11 @@ module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS) module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_stream_restore_la_CFLAGS = $(AM_CFLAGS) +if HAVE_DBUS +module_stream_restore_la_LIBADD += $(DBUS_LIBS) +module_stream_restore_la_CFLAGS += $(DBUS_CFLAGS) +endif + # Card profile restore module module_card_restore_la_SOURCES = modules/module-card-restore.c module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index e60cc735..bccdf938 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -2,6 +2,7 @@ This file is part of PulseAudio. Copyright 2008 Lennart Poettering + 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 @@ -51,6 +52,11 @@ #include #include +#ifdef HAVE_DBUS +#include +#include +#endif + #include "module-stream-restore-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering"); @@ -100,6 +106,12 @@ struct userdata { pa_native_protocol *protocol; pa_idxset *subscribed; + +#ifdef HAVE_DBUS + pa_dbus_protocol *dbus_protocol; + pa_hashmap *dbus_entries; + uint32_t next_index; /* For generating object paths for entries. */ +#endif }; #define ENTRY_VERSION 2 @@ -122,6 +134,883 @@ enum { SUBCOMMAND_EVENT }; +static struct entry *read_entry(struct userdata *u, const char *name); +static void apply_entry(struct userdata *u, const char *name, struct entry *e); +static void trigger_save(struct userdata *u); + +#ifdef HAVE_DBUS + +#define OBJECT_PATH "/org/pulseaudio/stream_restore1" +#define ENTRY_OBJECT_NAME "entry" +#define INTERFACE_STREAM_RESTORE "org.PulseAudio.Ext.StreamRestore1" +#define INTERFACE_ENTRY INTERFACE_STREAM_RESTORE ".RestoreEntry" + +#define DBUS_INTERFACE_REVISION 0 + +struct dbus_entry { + struct userdata *userdata; + + char *entry_name; + uint32_t index; + char *object_path; +}; + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata); + +enum property_handler_index { + PROPERTY_HANDLER_INTERFACE_REVISION, + PROPERTY_HANDLER_ENTRIES, + PROPERTY_HANDLER_MAX +}; + +enum entry_property_handler_index { + ENTRY_PROPERTY_HANDLER_INDEX, + ENTRY_PROPERTY_HANDLER_NAME, + ENTRY_PROPERTY_HANDLER_DEVICE, + ENTRY_PROPERTY_HANDLER_VOLUME, + ENTRY_PROPERTY_HANDLER_IS_MUTED, + ENTRY_PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INTERFACE_REVISION] = { .property_name = "InterfaceRevision", .type = "u", .get_cb = handle_get_interface_revision, .set_cb = NULL }, + [PROPERTY_HANDLER_ENTRIES] = { .property_name = "Entries", .type = "ao", .get_cb = handle_get_entries, .set_cb = NULL } +}; + +static pa_dbus_property_handler entry_property_handlers[ENTRY_PROPERTY_HANDLER_MAX] = { + [ENTRY_PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_entry_get_index, .set_cb = NULL }, + [ENTRY_PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_entry_get_name, .set_cb = NULL }, + [ENTRY_PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "s", .get_cb = handle_entry_get_device, .set_cb = handle_entry_set_device }, + [ENTRY_PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "a(uu)", .get_cb = handle_entry_get_volume, .set_cb = handle_entry_set_volume }, + [ENTRY_PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_entry_get_is_muted, .set_cb = handle_entry_set_is_muted } +}; + +enum method_handler_index { + METHOD_HANDLER_ADD_ENTRY, + METHOD_HANDLER_GET_ENTRY_BY_NAME, + METHOD_HANDLER_MAX +}; + +enum entry_method_handler_index { + ENTRY_METHOD_HANDLER_REMOVE, + ENTRY_METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info add_entry_args[] = { { "name", "s", "in" }, + { "device", "s", "in" }, + { "volume", "a(uu)", "in" }, + { "is_muted", "b", "in" }, + { "entry", "o", "out" } }; +static pa_dbus_arg_info get_entry_by_name_args[] = { { "name", "s", "in" }, { "entry", "o", "out" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_ADD_ENTRY] = { + .method_name = "AddEntry", + .arguments = add_entry_args, + .n_arguments = sizeof(add_entry_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_add_entry }, + [METHOD_HANDLER_GET_ENTRY_BY_NAME] = { + .method_name = "GetEntryByName", + .arguments = get_entry_by_name_args, + .n_arguments = sizeof(get_entry_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_entry_by_name } +}; + +static pa_dbus_method_handler entry_method_handlers[ENTRY_METHOD_HANDLER_MAX] = { + [ENTRY_METHOD_HANDLER_REMOVE] = { + .method_name = "Remove", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_entry_remove } +}; + +enum signal_index { + SIGNAL_NEW_ENTRY, + SIGNAL_ENTRY_REMOVED, + SIGNAL_MAX +}; + +enum entry_signal_index { + ENTRY_SIGNAL_DEVICE_UPDATED, + ENTRY_SIGNAL_VOLUME_UPDATED, + ENTRY_SIGNAL_MUTE_UPDATED, + ENTRY_SIGNAL_MAX +}; + +static pa_dbus_arg_info new_entry_args[] = { { "entry", "o", NULL } }; +static pa_dbus_arg_info entry_removed_args[] = { { "entry", "o", NULL } }; + +static pa_dbus_arg_info entry_device_updated_args[] = { { "device", "s", NULL } }; +static pa_dbus_arg_info entry_volume_updated_args[] = { { "volume", "a(uu)", NULL } }; +static pa_dbus_arg_info entry_mute_updated_args[] = { { "muted", "b", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_NEW_ENTRY] = { .name = "NewEntry", .arguments = new_entry_args, .n_arguments = 1 }, + [SIGNAL_ENTRY_REMOVED] = { .name = "EntryRemoved", .arguments = entry_removed_args, .n_arguments = 1 } +}; + +static pa_dbus_signal_info entry_signals[ENTRY_SIGNAL_MAX] = { + [ENTRY_SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = entry_device_updated_args, .n_arguments = 1 }, + [ENTRY_SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = entry_volume_updated_args, .n_arguments = 1 }, + [ENTRY_SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = entry_mute_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info stream_restore_interface_info = { + .name = INTERFACE_STREAM_RESTORE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static pa_dbus_interface_info entry_interface_info = { + .name = INTERFACE_ENTRY, + .method_handlers = entry_method_handlers, + .n_method_handlers = ENTRY_METHOD_HANDLER_MAX, + .property_handlers = entry_property_handlers, + .n_property_handlers = ENTRY_PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_entry_get_all, + .signals = entry_signals, + .n_signals = ENTRY_SIGNAL_MAX +}; + +static struct dbus_entry *dbus_entry_new(struct userdata *u, const char *entry_name) { + struct dbus_entry *de; + + pa_assert(u); + pa_assert(entry_name); + pa_assert(*entry_name); + + de = pa_xnew(struct dbus_entry, 1); + de->userdata = u; + de->entry_name = pa_xstrdup(entry_name); + de->index = u->next_index++; + de->object_path = pa_sprintf_malloc("%s/%s%u", OBJECT_PATH, ENTRY_OBJECT_NAME, de->index); + + pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, de->object_path, &entry_interface_info, u) >= 0); + + return de; +} + +static void dbus_entry_free(struct dbus_entry *de) { + pa_assert(de); + + pa_assert_se(pa_dbus_protocol_remove_interface(de->userdata->dbus_protocol, de->object_path, entry_interface_info.name) >= 0); + + pa_xfree(de->entry_name); + pa_xfree(de->object_path); +} + +/* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are + * are a channel position and a volume value, respectively. The result is + * stored in the map and vol arguments. If the volume can't be read from the + * iterator, an error reply is sent and a negative number is returned. In case + * of a failure we make no guarantees about the state of map and vol. In case + * of an empty array the channels field of both map and vol are set to 0. */ +static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, pa_channel_map *map, pa_cvolume *vol) { + DBusMessageIter array_iter; + DBusMessageIter struct_iter; + int arg_type; + + pa_assert(conn); + pa_assert(msg); + pa_assert(iter); + pa_assert(map); + pa_assert(vol); + + pa_channel_map_init(map); + pa_cvolume_init(vol); + + map->channels = 0; + vol->channels = 0; + + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); + return -1; + } + + arg_type = dbus_message_iter_get_element_type(iter); + if (arg_type != DBUS_TYPE_STRUCT) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A struct was expected.", (char) arg_type); + return -1; + } + + dbus_message_iter_recurse(iter, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) { + dbus_uint32_t chan_pos; + dbus_uint32_t chan_vol; + + dbus_message_iter_recurse(&array_iter, &struct_iter); + + arg_type = dbus_message_iter_get_arg_type(&struct_iter); + if (arg_type != DBUS_TYPE_UINT32) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong channel position type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); + return -1; + } + + dbus_message_iter_get_basic(&struct_iter, &chan_pos); + dbus_message_iter_next(&struct_iter); + + if (chan_pos >= PA_CHANNEL_POSITION_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u", chan_pos); + return -1; + } + + arg_type = dbus_message_iter_get_arg_type(&struct_iter); + if (arg_type != DBUS_TYPE_UINT32) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Channel volume missing."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong volume value type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); + return -1; + } + + dbus_message_iter_get_basic(&struct_iter, &chan_vol); + + if (chan_vol > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", chan_vol); + return -1; + } + + if (map->channels < PA_CHANNELS_MAX) { + map->map[map->channels] = chan_pos; + vol->values[map->channels] = chan_vol; + } + ++map->channels; + ++vol->channels; + + dbus_message_iter_next(&array_iter); + } + + if (map->channels > PA_CHANNELS_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum is %u.", map->channels, PA_CHANNELS_MAX); + return -1; + } + + dbus_message_iter_next(iter); + + return 0; +} + +static void append_volume(DBusMessageIter *iter, struct entry *e) { + DBusMessageIter array_iter; + DBusMessageIter struct_iter; + unsigned i; + + pa_assert(iter); + pa_assert(e); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(uu)", &array_iter)); + + if (!e->volume_valid) { + pa_assert_se(dbus_message_iter_close_container(iter, &array_iter)); + return; + } + + for (i = 0; i < e->channel_map.channels; ++i) { + pa_assert_se(dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->channel_map.map[i])); + pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->volume.values[i])); + + pa_assert_se(dbus_message_iter_close_container(&array_iter, &struct_iter)); + } + + pa_assert_se(dbus_message_iter_close_container(iter, &array_iter)); +} + +static void append_volume_variant(DBusMessageIter *iter, struct entry *e) { + DBusMessageIter variant_iter; + + pa_assert(iter); + pa_assert(e); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(uu)", &variant_iter)); + + append_volume(&variant_iter, e); + + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); +} + +static void send_new_entry_signal(struct dbus_entry *entry) { + DBusMessage *signal; + + pa_assert(entry); + + pa_assert_se(signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_NEW_ENTRY].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_entry_removed_signal(struct dbus_entry *entry) { + DBusMessage *signal; + + pa_assert(entry); + + pa_assert_se(signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_ENTRY_REMOVED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_device_updated_signal(struct dbus_entry *de, struct entry *e) { + DBusMessage *signal; + const char *device; + + pa_assert(de); + pa_assert(e); + + device = e->device_valid ? e->device : ""; + + pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_DEVICE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_volume_updated_signal(struct dbus_entry *de, struct entry *e) { + DBusMessage *signal; + DBusMessageIter msg_iter; + + pa_assert(de); + pa_assert(e); + + pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_VOLUME_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + append_volume(&msg_iter, e); + pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_mute_updated_signal(struct dbus_entry *de, struct entry *e) { + DBusMessage *signal; + dbus_bool_t muted; + + pa_assert(de); + pa_assert(e); + + pa_assert(e->muted_valid); + + muted = e->muted; + + pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &muted, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) { + dbus_uint32_t interface_revision = DBUS_INTERFACE_REVISION; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_entries(struct userdata *u, unsigned *n) { + const char **entries; + unsigned i = 0; + void *state = NULL; + struct dbus_entry *de; + + pa_assert(u); + pa_assert(n); + + *n = pa_hashmap_size(u->dbus_entries); + + if (*n == 0) + return NULL; + + entries = pa_xnew(const char *, *n); + + while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { + entries[i] = de->object_path; + ++i; + } + + return entries; +} + +static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + const char **entries; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + entries = get_entries(u, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, entries, n); + + pa_xfree(entries); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t interface_revision; + const char **entries; + unsigned n_entries; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + interface_revision = DBUS_INTERFACE_REVISION; + entries = get_entries(u, &n_entries); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ENTRIES].property_name, DBUS_TYPE_OBJECT_PATH, entries, n_entries); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(entries); +} + +static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + DBusMessageIter msg_iter; + const char *name; + const char *device; + pa_channel_map map; + pa_cvolume vol; + dbus_bool_t muted; + dbus_bool_t apply_immediately; + pa_datum key; + pa_datum value; + struct dbus_entry *dbus_entry; + struct entry *e; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &device) < 0) + return; + + if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &muted) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &apply_immediately) < 0) + return; + + if (!*name) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "An empty string was given as the entry name."); + return; + } + + if ((dbus_entry = pa_hashmap_get(u->dbus_entries, name))) { + pa_bool_t mute_updated = FALSE; + pa_bool_t volume_updated = FALSE; + pa_bool_t device_updated = FALSE; + + pa_assert_se(e = read_entry(u, name)); + mute_updated = e->muted != muted; + e->muted = muted; + e->muted_valid = TRUE; + + volume_updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol); + e->volume = vol; + e->channel_map = map; + e->volume_valid = !!map.channels; + + device_updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device); + pa_strlcpy(e->device, device, sizeof(e->device)); + e->device_valid = !!device[0]; + + if (mute_updated) + send_mute_updated_signal(dbus_entry, e); + if (volume_updated) + send_volume_updated_signal(dbus_entry, e); + if (device_updated) + send_device_updated_signal(dbus_entry, e); + + } else { + dbus_entry = dbus_entry_new(u, name); + pa_assert(pa_hashmap_put(u->dbus_entries, dbus_entry->entry_name, dbus_entry) >= 0); + + e->muted_valid = TRUE; + e->volume_valid = !!map.channels; + e->device_valid = !!device[0]; + e->muted = muted; + e->volume = vol; + e->channel_map = map; + pa_strlcpy(e->device, device, sizeof(e->device)); + + send_new_entry_signal(dbus_entry); + } + + key.data = (char *) name; + key.size = strlen(name); + + value.data = e; + value.size = sizeof(struct entry); + + pa_assert_se(pa_database_set(u->database, &key, &value, TRUE) == 0); + if (apply_immediately) + apply_entry(u, name, e); + + trigger_save(u); + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + const char *name; + struct dbus_entry *de; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (!(de = pa_hashmap_get(u->dbus_entries, name))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such stream restore entry."); + return; + } + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &de->object_path); +} + +static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &de->index); +} + +static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &de->entry_name); +} + +static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + struct entry *e; + const char *device; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + device = e->device_valid ? e->device : ""; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &device); + + pa_xfree(e); +} + +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + const char *device; + struct entry *e; + pa_bool_t updated; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_STRING, &device) < 0) + return; + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device); + + if (updated) { + pa_datum key; + pa_datum value; + + pa_strlcpy(e->device, device, sizeof(e->device)); + e->device_valid = !!device[0]; + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + value.data = e; + value.size = sizeof(struct entry); + pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0); + + send_device_updated_signal(de, e); + trigger_save(de->userdata); + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + DBusMessage *reply; + DBusMessageIter msg_iter; + struct entry *e; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + pa_assert_se(reply = dbus_message_new_method_return(msg)); + + dbus_message_iter_init_append(reply, &msg_iter); + append_volume_variant(&msg_iter, e); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + pa_xfree(e); +} + +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + DBusMessageIter msg_iter; + pa_channel_map map; + pa_cvolume vol; + struct entry *e; + pa_bool_t updated; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + /* 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(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) + return; + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol); + + if (updated) { + pa_datum key; + pa_datum value; + + e->volume = vol; + e->channel_map = map; + e->volume_valid = !!map.channels; + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + value.data = e; + value.size = sizeof(struct entry); + pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0); + + send_volume_updated_signal(de, e); + trigger_save(de->userdata); + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + struct entry *e; + dbus_bool_t muted; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + muted = e->muted_valid ? e->muted : FALSE; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &muted); + + pa_xfree(e); +} + +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + pa_bool_t muted; + struct entry *e; + pa_bool_t updated; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &muted) < 0) + return; + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + updated = !e->muted_valid || e->muted != muted; + + if (updated) { + pa_datum key; + pa_datum value; + + e->muted = muted; + e->muted_valid = TRUE; + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + value.data = e; + value.size = sizeof(struct entry); + pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0); + + send_mute_updated_signal(de, e); + trigger_save(de->userdata); + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + struct entry *e; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + const char *device; + dbus_bool_t muted; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + device = e->device_valid ? e->device : ""; + muted = e->muted_valid ? e->muted : FALSE; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &de->index); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &de->entry_name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_STRING, &device); + + 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, &entry_property_handlers[ENTRY_PROPERTY_HANDLER_VOLUME].property_name)); + append_volume_variant(&dict_entry_iter, e); + + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &muted); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(e); +} + +static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + pa_datum key; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + + pa_assert_se(pa_database_unset(de->userdata->database, &key) == 0); + + send_entry_removed_signal(de); + trigger_save(de->userdata); + + pa_assert_se(pa_hashmap_remove(de->userdata->dbus_entries, de->entry_name)); + dbus_entry_free(de); + + pa_dbus_send_empty_reply(conn, msg); +} + +#endif /* HAVE_DBUS */ + static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; @@ -162,7 +1051,7 @@ static char *get_name(pa_proplist *p, const char *prefix) { return t; } -static struct entry* read_entry(struct userdata *u, const char *name) { +static struct entry *read_entry(struct userdata *u, const char *name) { pa_datum key, data; struct entry *e; @@ -270,6 +1159,17 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 char *name; pa_datum key, data; + /* These are only used when D-Bus is enabled, but in order to reduce ifdef + * clutter these are defined here unconditionally. */ + pa_bool_t created_new_entry = TRUE; + pa_bool_t device_updated = FALSE; + pa_bool_t volume_updated = FALSE; + pa_bool_t mute_updated = FALSE; + +#ifdef HAVE_DBUS + struct dbus_entry *de = NULL; +#endif + pa_assert(c); pa_assert(u); @@ -291,23 +1191,34 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (!(name = get_name(sink_input->proplist, "sink-input"))) return; - if ((old = read_entry(u, name))) + if ((old = read_entry(u, name))) { entry = *old; + created_new_entry = FALSE; + } if (sink_input->save_volume) { entry.channel_map = sink_input->channel_map; pa_sink_input_get_volume(sink_input, &entry.volume, FALSE); entry.volume_valid = TRUE; + + volume_updated = !created_new_entry + && (!old->volume_valid + || !pa_channel_map_equal(&entry.channel_map, &old->channel_map) + || !pa_cvolume_equal(&entry.volume, &old->volume)); } if (sink_input->save_muted) { entry.muted = pa_sink_input_get_mute(sink_input); entry.muted_valid = TRUE; + + mute_updated = !created_new_entry && (!old->muted_valid || entry.muted != old->muted); } if (sink_input->save_sink) { pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device)); entry.device_valid = TRUE; + + device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device)); } } else { @@ -321,12 +1232,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (!(name = get_name(source_output->proplist, "source-output"))) return; - if ((old = read_entry(u, name))) + if ((old = read_entry(u, name))) { entry = *old; + created_new_entry = FALSE; + } if (source_output->save_source) { pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device)); entry.device_valid = source_output->save_source; + + device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device)); } } @@ -351,6 +1266,23 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 pa_database_set(u->database, &key, &data, TRUE); +#ifdef HAVE_DBUS + if (created_new_entry) { + de = dbus_entry_new(u, name); + pa_hashmap_put(u->dbus_entries, de->entry_name, de); + send_new_entry_signal(de); + } else { + pa_assert((de = pa_hashmap_get(u->dbus_entries, name))); + + if (device_updated) + send_device_updated_signal(de, &entry); + if (volume_updated) + send_volume_updated_signal(de, &entry); + if (mute_updated) + send_mute_updated_signal(de, &entry); + } +#endif + pa_xfree(name); trigger_save(u); @@ -810,14 +1742,27 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio mode != PA_UPDATE_SET) goto fail; - if (mode == PA_UPDATE_SET) + if (mode == PA_UPDATE_SET) { +#ifdef HAVE_DBUS + struct dbus_entry *de; + void *state = NULL; + + while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { + send_entry_removed_signal(de); + dbus_entry_free(pa_hashmap_remove(u->dbus_entries, de->entry_name)); + } +#endif pa_database_clear(u->database); + } while (!pa_tagstruct_eof(t)) { const char *name, *device; pa_bool_t muted; struct entry entry; pa_datum key, data; +#ifdef HAVE_DBUS + struct entry *old; +#endif pa_zero(entry); entry.version = ENTRY_VERSION; @@ -849,15 +1794,50 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio !pa_namereg_is_valid_name(entry.device)) goto fail; +#ifdef HAVE_DBUS + old = read_entry(u, name); +#endif + key.data = (char*) name; key.size = strlen(name); data.data = &entry; data.size = sizeof(entry); - if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) + if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) { +#ifdef HAVE_DBUS + struct dbus_entry *de; + + if (old) { + pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name))); + + if ((old->device_valid != entry.device_valid) + || (entry.device_valid && !pa_streq(entry.device, old->device))) + send_device_updated_signal(de, &entry); + + if ((old->volume_valid != entry.volume_valid) + || (entry.volume_valid + && (!pa_cvolume_equal(&entry.volume, &old->volume) || !pa_channel_map_equal(&entry.channel_map, &old->channel_map)))) + send_volume_updated_signal(de, &entry); + + if (!old->muted_valid || (entry.muted != old->muted)) + send_mute_updated_signal(de, &entry); + + } else { + de = dbus_entry_new(u, name); + pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de)); + send_new_entry_signal(de); + } +#endif + if (apply_immediately) apply_entry(u, name, &entry); + } + +#ifdef HAVE_DBUS + if (old) + pa_xfree(old); +#endif } trigger_save(u); @@ -870,10 +1850,20 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio while (!pa_tagstruct_eof(t)) { const char *name; pa_datum key; +#ifdef HAVE_DBUS + struct dbus_entry *de; +#endif if (pa_tagstruct_gets(t, &name) < 0) goto fail; +#ifdef HAVE_DBUS + if ((de = pa_hashmap_get(u->dbus_entries, name))) { + send_entry_removed_signal(de); + dbus_entry_free(pa_hashmap_remove(u->dbus_entries, name)); + } +#endif + key.data = (char*) name; key.size = strlen(name); @@ -932,6 +1922,10 @@ int pa__init(pa_module*m) { pa_source_output *so; uint32_t idx; pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE; +#ifdef HAVE_DBUS + pa_datum key; + pa_bool_t done; +#endif pa_assert(m); @@ -1002,6 +1996,34 @@ int pa__init(pa_module*m) { pa_log_info("Sucessfully opened database file '%s'.", fname); pa_xfree(fname); +#ifdef HAVE_DBUS + u->dbus_protocol = pa_dbus_protocol_get(u->core); + u->dbus_entries = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, OBJECT_PATH, &stream_restore_interface_info, u) >= 0); + pa_assert_se(pa_dbus_protocol_register_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0); + + /* Create the initial dbus entries. */ + done = !pa_database_first(u->database, &key, NULL); + while (!done) { + pa_datum next_key; + char *name; + struct dbus_entry *de; + + done = !pa_database_next(u->database, &key, &next_key, NULL); + + name = pa_xstrndup(key.data, key.size); + pa_datum_free(&key); + + de = dbus_entry_new(u, name); + pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) >= 0); + + pa_xfree(name); + + key = next_key; + } +#endif + PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u); @@ -1020,6 +2042,16 @@ fail: return -1; } +#ifdef HAVE_DBUS +static void free_dbus_entry_cb(void *p, void *userdata) { + struct dbus_entry *de = p; + + pa_assert(de); + + dbus_entry_free(de); +} +#endif + void pa__done(pa_module*m) { struct userdata* u; @@ -1028,6 +2060,19 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; +#ifdef HAVE_DBUS + if (u->dbus_protocol) { + pa_assert(u->dbus_entries); + + pa_assert_se(pa_dbus_protocol_unregister_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0); + pa_assert_se(pa_dbus_protocol_remove_interface(u->dbus_protocol, OBJECT_PATH, stream_restore_interface_info.name) >= 0); + + pa_hashmap_free(u->dbus_entries, free_dbus_entry_cb, NULL); + + pa_dbus_protocol_unref(u->dbus_protocol); + } +#endif + if (u->subscription) pa_subscription_free(u->subscription); -- cgit From 8966c61d3343e13502cfa210bb7123b7d7e7b27e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:50:18 +0300 Subject: dbusiface-core: Make the interface string a public constant. --- src/modules/dbus/iface-core.c | 49 +++++++++++++++++++++---------------------- src/modules/dbus/iface-core.h | 2 ++ 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 69c1bd25..695e4a3c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -48,7 +48,6 @@ #include "iface-core.h" #define OBJECT_PATH "/org/pulseaudio/core1" -#define INTERFACE_CORE "org.PulseAudio.Core1" #define INTERFACE_REVISION 0 @@ -312,7 +311,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { }; static pa_dbus_interface_info core_interface_info = { - .name = INTERFACE_CORE, + .name = PA_DBUSIFACE_CORE_INTERFACE, .method_handlers = method_handlers, .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, @@ -1612,7 +1611,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1626,7 +1625,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1644,7 +1643,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1652,7 +1651,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_card_free(card); @@ -1671,7 +1670,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1683,7 +1682,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * the D-Bus sink object wasn't created yet. Now that the * object is created, let's send the fallback sink change * signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1696,7 +1695,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1715,7 +1714,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1727,7 +1726,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * point the D-Bus source object wasn't created yet. Now * that the object is created, let's send the fallback * source change signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1740,7 +1739,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1756,7 +1755,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1764,7 +1763,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1780,7 +1779,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1788,7 +1787,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1804,7 +1803,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1812,7 +1811,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_sample_free(sample); @@ -1828,7 +1827,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1836,7 +1835,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_module_free(module); @@ -1852,7 +1851,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1860,7 +1859,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_client_free(client); @@ -1882,7 +1881,7 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1899,7 +1898,7 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -2028,7 +2027,7 @@ static void free_client_cb(void *p, void *userdata) { void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_assert(c); - pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, INTERFACE_CORE); + pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, core_interface_info.name); pa_subscription_free(c->subscription); pa_hashmap_free(c->cards, free_card_cb, NULL); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 964a37bd..6c5191b9 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -30,6 +30,8 @@ #include +#define PA_DBUSIFACE_CORE_INTERFACE "org.PulseAudio.Core1" + typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); -- cgit From b1578e27b62e7332111bb706f79858b0866029e3 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:55:10 +0300 Subject: dbus-protocol, dbusiface-core: Take a reference when storing the core pointer. --- src/modules/dbus/iface-core.c | 3 ++- src/pulsecore/protocol-dbus.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 695e4a3c..ca9ba582 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1923,7 +1923,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { pa_assert(core); c = pa_xnew(pa_dbusiface_core, 1); - c->core = core; + c->core = pa_core_ref(core); c->subscription = pa_subscription_new(core, PA_SUBSCRIPTION_MASK_ALL, subscription_cb, c); c->dbus_protocol = pa_dbus_protocol_get(core); c->cards = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); @@ -2044,6 +2044,7 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hook_slot_free(c->extension_unregistered_slot); pa_dbus_protocol_unref(c->dbus_protocol); + pa_core_unref(c->core); pa_xfree(c); } diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 475b952f..8fc08032 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -117,7 +117,7 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { p = pa_xnew(pa_dbus_protocol, 1); PA_REFCNT_INIT(p); - p->core = c; + p->core = pa_core_ref(c); p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); @@ -170,6 +170,8 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) { pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); + pa_core_unref(p->core); + pa_xfree(p); } -- cgit From 9eeb8eb2729cdc77fa98c704eb8fc7fcca15336b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:57:44 +0300 Subject: dbus-protocol: Make debug logging saner. --- src/pulsecore/protocol-dbus.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 8fc08032..c82bb5fa 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -308,7 +308,6 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, return FOUND_METHOD; } - pa_log("find_handler_by_method() failed."); return NO_SUCH_METHOD; } @@ -331,7 +330,6 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) return FOUND_GET_ALL; else { - pa_log("GetAll message has unknown interface: %s", interface); return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ } } else { @@ -418,7 +416,10 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message)); + pa_log_debug("Received message: destination = %s, interface = %s, member = %s", + dbus_message_get_path(message), + dbus_message_get_interface(message), + dbus_message_get_member(message)); pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); @@ -428,8 +429,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); pa_assert_se(dbus_connection_send(connection, reply, NULL)); - pa_log_debug("%s.Introspect handled.", obj_entry->path); - goto finish; } @@ -633,7 +632,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, if (obj_entry_created) register_object(p, obj_entry); - pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no"); + pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path); return 0; -- cgit From 0fc055226c60fa7429abf80e38f40a565f9e7922 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 18:00:08 +0300 Subject: dbus-protocol: Remove erroneous protocol object unref. --- src/pulsecore/protocol-dbus.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index c82bb5fa..06098673 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -639,7 +639,6 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, fail: if (obj_entry_created) { pa_hashmap_remove(p->objects, path); - pa_dbus_protocol_unref(p); pa_xfree(obj_entry); } -- cgit From 44770c59e92f49288341afe8646d8bc39eb9f589 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 18:01:26 +0300 Subject: dbusiface-memstats: Implement the Memstats D-Bus interface. --- src/Makefile.am | 1 + src/modules/dbus/iface-core.c | 5 + src/modules/dbus/iface-memstats.c | 231 ++++++++++++++++++++++++++++++++++++++ src/modules/dbus/iface-memstats.h | 44 ++++++++ 4 files changed, 281 insertions(+) create mode 100644 src/modules/dbus/iface-memstats.c create mode 100644 src/modules/dbus/iface-memstats.h diff --git a/src/Makefile.am b/src/Makefile.am index 07f81a6b..e10b4ab6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1281,6 +1281,7 @@ module_dbus_protocol_la_SOURCES = \ 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-memstats.c modules/dbus/iface-memstats.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 \ diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index ca9ba582..ad729f93 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -41,6 +41,7 @@ #include "iface-card.h" #include "iface-client.h" #include "iface-device.h" +#include "iface-memstats.h" #include "iface-module.h" #include "iface-sample.h" #include "iface-stream.h" @@ -114,6 +115,8 @@ struct pa_dbusiface_core { pa_hook_slot *extension_registered_slot; pa_hook_slot *extension_unregistered_slot; + + pa_dbusiface_memstats *memstats; }; enum property_handler_index { @@ -1940,6 +1943,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->fallback_source = pa_namereg_get_default_source(core); c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); + c->memstats = pa_dbusiface_memstats_new(core, OBJECT_PATH); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); @@ -2042,6 +2046,7 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hashmap_free(c->clients, free_client_cb, NULL); pa_hook_slot_free(c->extension_registered_slot); pa_hook_slot_free(c->extension_unregistered_slot); + pa_dbusiface_memstats_free(c->memstats); pa_dbus_protocol_unref(c->dbus_protocol); pa_core_unref(c->core); diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c new file mode 100644 index 00000000..d3412a25 --- /dev/null +++ b/src/modules/dbus/iface-memstats.c @@ -0,0 +1,231 @@ +/*** + 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 "iface-memstats.h" + +#define OBJECT_NAME "memstats" + +static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +struct pa_dbusiface_memstats { + pa_core *core; + char *path; + pa_dbus_protocol *dbus_protocol; +}; + +enum property_handler_index { + PROPERTY_HANDLER_CURRENT_MEMBLOCKS, + PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE, + PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS, + PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE, + PROPERTY_HANDLER_SAMPLE_CACHE_SIZE, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_CURRENT_MEMBLOCKS] = { .property_name = "CurrentMemblocks", .type = "u", .get_cb = handle_get_current_memblocks, .set_cb = NULL }, + [PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE] = { .property_name = "CurrentMemblocksSize", .type = "u", .get_cb = handle_get_current_memblocks_size, .set_cb = NULL }, + [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS] = { .property_name = "AccumulatedMemblocks", .type = "u", .get_cb = handle_get_accumulated_memblocks, .set_cb = NULL }, + [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE] = { .property_name = "AccumulatedMemblocksSize", .type = "u", .get_cb = handle_get_accumulated_memblocks_size, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_CACHE_SIZE] = { .property_name = "SampleCacheSize", .type = "u", .get_cb = handle_get_sample_cache_size, .set_cb = NULL } +}; + +static pa_dbus_interface_info memstats_interface_info = { + .name = PA_DBUSIFACE_MEMSTATS_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t current_memblocks; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + current_memblocks = pa_atomic_load(&stat->n_allocated); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, ¤t_memblocks); +} + +static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t current_memblocks_size; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + current_memblocks_size = pa_atomic_load(&stat->allocated_size); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, ¤t_memblocks_size); +} + +static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t accumulated_memblocks; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + accumulated_memblocks = pa_atomic_load(&stat->n_accumulated); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks); +} + +static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t accumulated_memblocks_size; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks_size); +} + +static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + dbus_uint32_t sample_cache_size; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + sample_cache_size = pa_scache_total_size(m->core); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_cache_size); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t current_memblocks; + dbus_uint32_t current_memblocks_size; + dbus_uint32_t accumulated_memblocks; + dbus_uint32_t accumulated_memblocks_size; + dbus_uint32_t sample_cache_size; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + current_memblocks = pa_atomic_load(&stat->n_allocated); + current_memblocks_size = pa_atomic_load(&stat->allocated_size); + accumulated_memblocks = pa_atomic_load(&stat->n_accumulated); + accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size); + sample_cache_size = pa_scache_total_size(m->core); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, ¤t_memblocks); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, ¤t_memblocks_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_CACHE_SIZE].property_name, DBUS_TYPE_UINT32, &sample_cache_size); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix) { + pa_dbusiface_memstats *m; + + pa_assert(core); + pa_assert(path_prefix); + + m = pa_xnew(pa_dbusiface_memstats, 1); + m->core = pa_core_ref(core); + m->path = pa_sprintf_malloc("%s/%s", path_prefix, OBJECT_NAME); + m->dbus_protocol = pa_dbus_protocol_get(core); + + pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0); + + return m; +} + +void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m) { + pa_assert(m); + + pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, memstats_interface_info.name) >= 0); + + pa_xfree(m->path); + + pa_dbus_protocol_unref(m->dbus_protocol); + pa_core_unref(m->core); + + pa_xfree(m); +} + +const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m) { + pa_assert(m); + + return m->path; +} diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h new file mode 100644 index 00000000..d7773ee0 --- /dev/null +++ b/src/modules/dbus/iface-memstats.h @@ -0,0 +1,44 @@ +#ifndef foodbusifacememstatshfoo +#define foodbusifacememstatshfoo + +/*** + 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 interface org.PulseAudio.Core1.Memstats. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Memstats interface + * documentation. + */ + +#include + +#include "iface-core.h" + +#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUSIFACE_CORE_INTERFACE ".Memstats" + +typedef struct pa_dbusiface_memstats pa_dbusiface_memstats; + +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix); +void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m); + +const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m); + +#endif -- cgit From 1457df40eee692834d1c5faf95ca0057d74f86d1 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 07:59:06 +0300 Subject: proplist: New function: pa_proplist_equal() --- src/map-file | 1 + src/pulse/proplist.c | 26 ++++++++++++++++++++++++++ src/pulse/proplist.h | 3 +++ 3 files changed, 30 insertions(+) diff --git a/src/map-file b/src/map-file index a1d0a061..3db3a2d7 100644 --- a/src/map-file +++ b/src/map-file @@ -180,6 +180,7 @@ pa_path_get_filename; pa_proplist_clear; pa_proplist_contains; pa_proplist_copy; +pa_proplist_equal; pa_proplist_free; pa_proplist_from_string; pa_proplist_get; diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index c904f533..4f0d6a6d 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -680,3 +680,29 @@ int pa_proplist_isempty(pa_proplist *p) { return pa_hashmap_isempty(MAKE_HASHMAP(p)); } + +int pa_proplist_equal(pa_proplist *a, pa_proplist *b) { + const void *key = NULL; + struct property *a_prop = NULL; + struct property *b_prop = NULL; + void *state = NULL; + + pa_assert(a); + pa_assert(b); + + if (pa_proplist_size(a) != pa_proplist_size(b)) + return 0; + + while ((a_prop = pa_hashmap_iterate(MAKE_HASHMAP(a), &state, &key))) { + if (!(b_prop = pa_hashmap_get(MAKE_HASHMAP(b), key))) + return 0; + + if (a_prop->nbytes != b_prop->nbytes) + return 0; + + if (memcmp(a_prop->value, b_prop->value, a_prop->nbytes) != 0) + return 0; + } + + return 1; +} diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index bc4dbd8a..a585944a 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -354,6 +354,9 @@ unsigned pa_proplist_size(pa_proplist *t); /** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */ int pa_proplist_isempty(pa_proplist *t); +/** Return non-zero when a and b have the same keys and values. */ +int pa_proplist_equal(pa_proplist *a, pa_proplist *b); + PA_C_DECL_END #endif -- cgit From fcf68752e687123a171b1144f78f8eba1625a204 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 08:37:33 +0300 Subject: dbus: Three entangled changes: * Make the dbus object constructors take a pa_dbusiface_core pointer as an argument. Remove the path_prefix argument. * Expose the core object path as a constant in protocol-dbus.h. * Move the core interface name constant from iface-core.h to protocol-dbus.h. --- src/modules/dbus/iface-card.c | 15 ++++--- src/modules/dbus/iface-card.h | 6 ++- src/modules/dbus/iface-client.c | 7 +-- src/modules/dbus/iface-client.h | 4 +- src/modules/dbus/iface-core.c | 92 +++++++++++++++++++-------------------- src/modules/dbus/iface-core.h | 2 - src/modules/dbus/iface-device.c | 13 +++--- src/modules/dbus/iface-device.h | 6 ++- src/modules/dbus/iface-memstats.c | 6 +-- src/modules/dbus/iface-memstats.h | 5 ++- src/modules/dbus/iface-module.c | 7 +-- src/modules/dbus/iface-module.h | 4 +- src/modules/dbus/iface-sample.c | 7 +-- src/modules/dbus/iface-sample.h | 4 +- src/modules/dbus/iface-stream.c | 13 +++--- src/modules/dbus/iface-stream.h | 6 ++- src/pulsecore/protocol-dbus.h | 7 ++- 17 files changed, 113 insertions(+), 91 deletions(-) diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index db6aa26f..e203c395 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -24,25 +24,30 @@ #endif #include +#include #include "iface-card.h" #define OBJECT_NAME "card" struct pa_dbusiface_card { + pa_dbusiface_core *core; + pa_card *card; char *path; }; -pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix) { - pa_dbusiface_card *c; +pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) { + pa_dbusiface_card *c = NULL; + + pa_assert(core); pa_assert(card); - pa_assert(path_prefix); - c = pa_xnew(pa_dbusiface_card, 1); + c = pa_xnew0(pa_dbusiface_card, 1); + c->core = core; c->card = card; - c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, card->index); + c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index); return c; } diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h index 54db610c..57ad4e31 100644 --- a/src/modules/dbus/iface-card.h +++ b/src/modules/dbus/iface-card.h @@ -30,9 +30,13 @@ #include +#include "iface-core.h" + +#define PA_DBUSIFACE_CARD_INTERFACE PA_DBUS_CORE_INTERFACE ".Card" + typedef struct pa_dbusiface_card pa_dbusiface_card; -pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix); +pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card); void pa_dbusiface_card_free(pa_dbusiface_card *c); const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c); diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index cfa36d0c..d9c8653f 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-client.h" @@ -34,15 +35,15 @@ struct pa_dbusiface_client { char *path; }; -pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix) { +pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { pa_dbusiface_client *c; + pa_assert(core); pa_assert(client); - pa_assert(path_prefix); c = pa_xnew(pa_dbusiface_client, 1); c->client = client; - c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, client->index); + c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); return c; } diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h index 62cca7f8..ff906256 100644 --- a/src/modules/dbus/iface-client.h +++ b/src/modules/dbus/iface-client.h @@ -30,9 +30,11 @@ #include +#include "iface-core.h" + typedef struct pa_dbusiface_client pa_dbusiface_client; -pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix); +pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client); void pa_dbusiface_client_free(pa_dbusiface_client *c); const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c); diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index ad729f93..e40cb979 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -48,12 +48,8 @@ #include "iface-core.h" -#define OBJECT_PATH "/org/pulseaudio/core1" - #define INTERFACE_REVISION 0 - - static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -314,7 +310,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { }; static pa_dbus_interface_info core_interface_info = { - .name = PA_DBUSIFACE_CORE_INTERFACE, + .name = PA_DBUS_CORE_INTERFACE, .method_handlers = method_handlers, .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, @@ -1388,7 +1384,7 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u sample->volume_is_set = FALSE; } - dbus_sample = pa_dbusiface_sample_new(sample, OBJECT_PATH); + dbus_sample = pa_dbusiface_sample_new(c, sample); pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample); object_path = pa_dbusiface_sample_get_path(dbus_sample); @@ -1510,7 +1506,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use goto finish; } - dbus_module = pa_dbusiface_module_new(module, OBJECT_PATH); + dbus_module = pa_dbusiface_module_new(c, module); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module); object_path = pa_dbusiface_module_get_path(dbus_module); @@ -1614,7 +1610,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1628,7 +1624,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1640,13 +1636,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { - card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); + card = pa_dbusiface_card_new(c, pa_idxset_get_by_index(core->cards, idx)); pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); } object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1654,7 +1650,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_card_free(card); @@ -1666,14 +1662,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx); if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); + device = pa_dbusiface_device_new_sink(c, sink); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1685,7 +1681,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * the D-Bus sink object wasn't created yet. Now that the * object is created, let's send the fallback sink change * signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1698,7 +1694,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1710,14 +1706,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_source *source = pa_idxset_get_by_index(core->sources, idx); if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_source(source, OBJECT_PATH); + device = pa_dbusiface_device_new_source(c, source); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1729,7 +1725,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * point the D-Bus source object wasn't created yet. Now * that the object is created, let's send the fallback * source change signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1742,7 +1738,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1752,13 +1748,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); + stream = pa_dbusiface_stream_new_playback(c, pa_idxset_get_by_index(core->sink_inputs, idx)); pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); } object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1766,7 +1762,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1776,13 +1772,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); + stream = pa_dbusiface_stream_new_record(c, pa_idxset_get_by_index(core->source_outputs, idx)); pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); } object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1790,7 +1786,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1800,13 +1796,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { - sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH); + sample = pa_dbusiface_sample_new(c, pa_idxset_get_by_index(core->scache, idx)); pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); } object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1814,7 +1810,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_sample_free(sample); @@ -1824,13 +1820,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { - module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH); + module = pa_dbusiface_module_new(c, pa_idxset_get_by_index(core->modules, idx)); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); } object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1838,7 +1834,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_module_free(module); @@ -1848,13 +1844,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { - client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); + client = pa_dbusiface_client_new(c, pa_idxset_get_by_index(core->clients, idx)); pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); } object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1862,7 +1858,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_client_free(client); @@ -1884,7 +1880,7 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1901,7 +1897,7 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1943,39 +1939,39 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->fallback_source = pa_namereg_get_default_source(core); c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); - c->memstats = pa_dbusiface_memstats_new(core, OBJECT_PATH); + c->memstats = pa_dbusiface_memstats_new(c, core); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) - pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card)); for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) { - device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); + device = pa_dbusiface_device_new_sink(c, sink); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) { - device = pa_dbusiface_device_new_source(source, OBJECT_PATH); + device = pa_dbusiface_device_new_source(c, source); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx)) - pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(sink_input, OBJECT_PATH)); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input)); for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx)) - pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(source_output, OBJECT_PATH)); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output)); for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx)) - pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(sample, OBJECT_PATH)); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample)); for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx)) - pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module, OBJECT_PATH)); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module)); for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx)) - pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(client, OBJECT_PATH)); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); - pa_dbus_protocol_add_interface(c->dbus_protocol, OBJECT_PATH, &core_interface_info, c); + pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c); return c; } @@ -2031,7 +2027,7 @@ static void free_client_cb(void *p, void *userdata) { void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_assert(c); - pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, core_interface_info.name); + pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name); pa_subscription_free(c->subscription); pa_hashmap_free(c->cards, free_card_cb, NULL); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 6c5191b9..964a37bd 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -30,8 +30,6 @@ #include -#define PA_DBUSIFACE_CORE_INTERFACE "org.PulseAudio.Core1" - typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 3b3795eb..1a64f43c 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-device.h" @@ -44,30 +45,30 @@ struct pa_dbusiface_device { char *path; }; -pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix) { +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) { pa_dbusiface_device *d; + pa_assert(core); pa_assert(sink); - pa_assert(path_prefix); d = pa_xnew(pa_dbusiface_device, 1); d->sink = pa_sink_ref(sink); d->type = DEVICE_TYPE_SINK; - d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SINK_OBJECT_NAME, sink->index); + d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); return d; } -pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix) { +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) { pa_dbusiface_device *d; + pa_assert(core); pa_assert(source); - pa_assert(path_prefix); d = pa_xnew(pa_dbusiface_device, 1); d->source = pa_source_ref(source); d->type = DEVICE_TYPE_SOURCE; - d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SOURCE_OBJECT_NAME, source->index); + d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index); return d; } diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h index 81ad1d84..1e9af83a 100644 --- a/src/modules/dbus/iface-device.h +++ b/src/modules/dbus/iface-device.h @@ -32,10 +32,12 @@ #include #include +#include "iface-core.h" + typedef struct pa_dbusiface_device pa_dbusiface_device; -pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix); -pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix); +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink); +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source); void pa_dbusiface_device_free(pa_dbusiface_device *d); const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d); diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c index d3412a25..73a84be8 100644 --- a/src/modules/dbus/iface-memstats.c +++ b/src/modules/dbus/iface-memstats.c @@ -195,15 +195,15 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat dbus_message_unref(reply); } -pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix) { +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core) { pa_dbusiface_memstats *m; + pa_assert(dbus_core); pa_assert(core); - pa_assert(path_prefix); m = pa_xnew(pa_dbusiface_memstats, 1); m->core = pa_core_ref(core); - m->path = pa_sprintf_malloc("%s/%s", path_prefix, OBJECT_NAME); + m->path = pa_sprintf_malloc("%s/%s", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME); m->dbus_protocol = pa_dbus_protocol_get(core); pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0); diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h index d7773ee0..0820e8fe 100644 --- a/src/modules/dbus/iface-memstats.h +++ b/src/modules/dbus/iface-memstats.h @@ -29,14 +29,15 @@ */ #include +#include #include "iface-core.h" -#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUSIFACE_CORE_INTERFACE ".Memstats" +#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUS_CORE_INTERFACE ".Memstats" typedef struct pa_dbusiface_memstats pa_dbusiface_memstats; -pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix); +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core); void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m); const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m); diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index 1c95f9e6..788d104b 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-module.h" @@ -34,15 +35,15 @@ struct pa_dbusiface_module { char *path; }; -pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix) { +pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module) { pa_dbusiface_module *m; + pa_assert(core); pa_assert(module); - pa_assert(path_prefix); m = pa_xnew(pa_dbusiface_module, 1); m->module = module; - m->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, module->index); + m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index); return m; } diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h index 7f683e6a..c4f29210 100644 --- a/src/modules/dbus/iface-module.h +++ b/src/modules/dbus/iface-module.h @@ -30,9 +30,11 @@ #include +#include "iface-core.h" + typedef struct pa_dbusiface_module pa_dbusiface_module; -pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix); +pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module); void pa_dbusiface_module_free(pa_dbusiface_module *m); const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m); diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index b4a308a2..44cfb031 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-sample.h" @@ -34,15 +35,15 @@ struct pa_dbusiface_sample { char *path; }; -pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix) { +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) { pa_dbusiface_sample *s; + pa_assert(core); pa_assert(sample); - pa_assert(path_prefix); s = pa_xnew(pa_dbusiface_sample, 1); s->sample = sample; - s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, sample->index); + s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index); return s; } diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h index 1b85404e..1b82648d 100644 --- a/src/modules/dbus/iface-sample.h +++ b/src/modules/dbus/iface-sample.h @@ -30,9 +30,11 @@ #include +#include "iface-core.h" + typedef struct pa_dbusiface_sample pa_dbusiface_sample; -pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix); +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample); void pa_dbusiface_sample_free(pa_dbusiface_sample *c); const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c); diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 1d9ffee6..b5a17894 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-stream.h" @@ -44,30 +45,30 @@ struct pa_dbusiface_stream { char *path; }; -pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix) { +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { pa_dbusiface_stream *s; + pa_assert(core); pa_assert(sink_input); - pa_assert(path_prefix); s = pa_xnew(pa_dbusiface_stream, 1); s->sink_input = pa_sink_input_ref(sink_input); s->type = STREAM_TYPE_PLAYBACK; - s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, PLAYBACK_OBJECT_NAME, sink_input->index); + s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); return s; } -pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix) { +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) { pa_dbusiface_stream *s; + pa_assert(core); pa_assert(source_output); - pa_assert(path_prefix); s = pa_xnew(pa_dbusiface_stream, 1); s->source_output = pa_source_output_ref(source_output); s->type = STREAM_TYPE_RECORD; - s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, RECORD_OBJECT_NAME, source_output->index); + s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index); return s; } diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h index cc2f3d61..b1b1854b 100644 --- a/src/modules/dbus/iface-stream.h +++ b/src/modules/dbus/iface-stream.h @@ -31,10 +31,12 @@ #include #include +#include "iface-core.h" + typedef struct pa_dbusiface_stream pa_dbusiface_stream; -pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix); -pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix); +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input); +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output); void pa_dbusiface_stream_free(pa_dbusiface_stream *s); const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s); diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index f2b1b50b..c6b630a1 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -32,8 +32,11 @@ #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME -#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" -#define PA_DBUS_ERROR_NOT_FOUND "org.PulseAudio.Core1.NotFoundError" +#define PA_DBUS_CORE_INTERFACE "org.PulseAudio.Core1" +#define PA_DBUS_CORE_OBJECT_PATH "/org/pulseaudio/core1" + +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY PA_DBUS_CORE_INTERFACE ".NoSuchPropertyError" +#define PA_DBUS_ERROR_NOT_FOUND PA_DBUS_CORE_INTERFACE ".NotFoundError" /* Returns the default address of the server type in the escaped form. For * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the -- cgit From 06232e2965ee02d62ca566fcbf5e805c571b574a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:04:15 +0300 Subject: dbus: Take advantage of the PA_HASHMAP_FOREACH macro. --- src/modules/dbus/iface-core.c | 64 ++++++++++++++----------------------- src/modules/module-stream-restore.c | 8 ++--- src/pulsecore/protocol-dbus.c | 30 +++++++---------- 3 files changed, 38 insertions(+), 64 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e40cb979..685ba63b 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -542,7 +542,7 @@ static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *ms /* The caller frees the array, but not the strings. */ static const char **get_cards(pa_dbusiface_core *c, unsigned *n) { const char **cards; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_card *card; @@ -556,10 +556,8 @@ static const char **get_cards(pa_dbusiface_core *c, unsigned *n) { cards = pa_xnew(const char *, *n); - for (i = 0, card = pa_hashmap_iterate(c->cards, &state, NULL); card; ++i, card = pa_hashmap_iterate(c->cards, &state, NULL)) - cards[i] = pa_dbusiface_card_get_path(card); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(card, c->cards, state) + cards[i++] = pa_dbusiface_card_get_path(card); return cards; } @@ -583,7 +581,7 @@ static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userd /* The caller frees the array, but not the strings. */ static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) { const char **sinks; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_device *sink; @@ -597,10 +595,8 @@ static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) { sinks = pa_xnew(const char *, *n); - for (i = 0, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL); sink; ++i, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL)) - sinks[i] = pa_dbusiface_device_get_path(sink); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(sink, c->sinks_by_index, state) + sinks[i++] = pa_dbusiface_device_get_path(sink); return sinks; } @@ -671,7 +667,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi /* The caller frees the array, but not the strings. */ static const char **get_sources(pa_dbusiface_core *c, unsigned *n) { const char **sources; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_device *source; @@ -685,10 +681,8 @@ static const char **get_sources(pa_dbusiface_core *c, unsigned *n) { sources = pa_xnew(const char *, *n); - for (i = 0, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL); source; ++i, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL)) - sources[i] = pa_dbusiface_device_get_path(source); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(source, c->sources_by_index, state) + sources[i++] = pa_dbusiface_device_get_path(source); return sources; } @@ -759,7 +753,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v /* The caller frees the array, but not the strings. */ static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) { const char **streams; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_stream *stream; @@ -773,10 +767,8 @@ static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) { streams = pa_xnew(const char *, *n); - for (i = 0, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL)) - streams[i] = pa_dbusiface_stream_get_path(stream); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(stream, c->playback_streams, state) + streams[i++] = pa_dbusiface_stream_get_path(stream); return streams; } @@ -800,7 +792,7 @@ static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, /* The caller frees the array, but not the strings. */ static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) { const char **streams; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_stream *stream; @@ -814,10 +806,8 @@ static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) { streams = pa_xnew(const char *, *n); - for (i = 0, stream = pa_hashmap_iterate(c->record_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->record_streams, &state, NULL)) - streams[i] = pa_dbusiface_stream_get_path(stream); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(stream, c->record_streams, state) + streams[i++] = pa_dbusiface_stream_get_path(stream); return streams; } @@ -841,7 +831,7 @@ static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, vo /* The caller frees the array, but not the strings. */ static const char **get_samples(pa_dbusiface_core *c, unsigned *n) { const char **samples; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_sample *sample; @@ -855,10 +845,8 @@ static const char **get_samples(pa_dbusiface_core *c, unsigned *n) { samples = pa_xnew(const char *, *n); - for (i = 0, sample = pa_hashmap_iterate(c->samples, &state, NULL); sample; ++i, sample = pa_hashmap_iterate(c->samples, &state, NULL)) - samples[i] = pa_dbusiface_sample_get_path(sample); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(sample, c->samples, state) + samples[i++] = pa_dbusiface_sample_get_path(sample); return samples; } @@ -882,7 +870,7 @@ static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *use /* The caller frees the array, but not the strings. */ static const char **get_modules(pa_dbusiface_core *c, unsigned *n) { const char **modules; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_module *module; @@ -896,10 +884,8 @@ static const char **get_modules(pa_dbusiface_core *c, unsigned *n) { modules = pa_xnew(const char *, *n); - for (i = 0, module = pa_hashmap_iterate(c->modules, &state, NULL); module; ++i, module = pa_hashmap_iterate(c->modules, &state, NULL)) - modules[i] = pa_dbusiface_module_get_path(module); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(module, c->modules, state) + modules[i++] = pa_dbusiface_module_get_path(module); return modules; } @@ -923,7 +909,7 @@ static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *use /* The caller frees the array, but not the strings. */ static const char **get_clients(pa_dbusiface_core *c, unsigned *n) { const char **clients; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_client *client; @@ -937,10 +923,8 @@ static const char **get_clients(pa_dbusiface_core *c, unsigned *n) { clients = pa_xnew(const char *, *n); - for (i = 0, client = pa_hashmap_iterate(c->clients, &state, NULL); client; ++i, client = pa_hashmap_iterate(c->clients, &state, NULL)) - clients[i] = pa_dbusiface_client_get_path(client); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(client, c->clients, state) + clients[i++] = pa_dbusiface_client_get_path(client); return clients; } diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index bccdf938..e9063031 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -553,10 +553,8 @@ static const char **get_entries(struct userdata *u, unsigned *n) { entries = pa_xnew(const char *, *n); - while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { - entries[i] = de->object_path; - ++i; - } + PA_HASHMAP_FOREACH(de, u->dbus_entries, state) + entries[i++] = de->object_path; return entries; } @@ -1747,7 +1745,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio struct dbus_entry *de; void *state = NULL; - while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { + PA_HASHMAP_FOREACH(de, u->dbus_entries, state) { send_entry_removed_signal(de); dbus_entry_free(pa_hashmap_remove(u->dbus_entries, de->entry_name)); } diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 06098673..5cbd0d91 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -186,7 +186,7 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); pa_strbuf_puts(buf, "\n"); - while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &interfaces_state, NULL))) { + PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) { pa_dbus_method_handler *method_handler; pa_dbus_property_handler *property_handler; void *handlers_state = NULL; @@ -195,7 +195,7 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_printf(buf, " \n", iface_entry->name); - while ((method_handler = pa_hashmap_iterate(iface_entry->method_handlers, &handlers_state, NULL))) { + PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) { pa_strbuf_printf(buf, " \n", method_handler->method_name); for (i = 0; i < method_handler->n_arguments; ++i) @@ -278,7 +278,7 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr pa_assert(iface_entry); pa_assert(property_handler); - while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { if (dbus_message_has_member(msg, "Get")) return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; @@ -303,7 +303,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, pa_assert(iface_entry); pa_assert(method_handler); - while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) return FOUND_METHOD; } @@ -497,7 +497,7 @@ static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) pa_assert(p); pa_assert(obj_entry); - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p)); } @@ -652,7 +652,7 @@ static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entr pa_assert(p); pa_assert(obj_entry); - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path)); } @@ -742,7 +742,7 @@ static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { pa_assert(p); pa_assert(conn); - while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + PA_HASHMAP_FOREACH(obj_entry, p->objects, state) pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p)); } @@ -777,7 +777,7 @@ static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { pa_assert(p); pa_assert(conn); - while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + PA_HASHMAP_FOREACH(obj_entry, p->objects, state) pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path)); } @@ -904,13 +904,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { pa_assert(signal); pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); - /* XXX: We have to do some linear searching to find connections that want - * to receive the signal. This shouldn't be very significant performance - * problem, and adding an (object path, signal name) -> connection mapping - * would be likely to create substantial complexity. */ - - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) { - + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) { if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL) || pa_idxset_isempty(conn_entry->all_signals_objects))) @@ -943,10 +937,8 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { extensions = pa_xnew(const char *, *n); - while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) { - extensions[i] = ext_name; - ++i; - } + while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) + extensions[i++] = ext_name; return extensions; } -- cgit From 0b6662023bfb121b0e553ce00b4229705c8e7aef Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:06:21 +0300 Subject: dbusiface-core: Generate more informative error messages. --- src/modules/dbus/iface-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 685ba63b..be07648c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -655,7 +655,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi return; if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path); return; } @@ -741,7 +741,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v return; if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path); return; } @@ -1154,7 +1154,7 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void } if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name); return; } @@ -1186,7 +1186,7 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo } if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name); return; } -- cgit From 5ece8e8833ff40a4c535fc7d6705377fcf7fd0fd Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:10:05 +0300 Subject: dbusiface-core: Add functions for getting various object paths. --- src/modules/dbus/iface-core.c | 21 +++++++++++++++++++++ src/modules/dbus/iface-core.h | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index be07648c..e8ea50ba 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2033,3 +2033,24 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_xfree(c); } + +const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) { + pa_assert(c); + pa_assert(sink); + + return pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index))); +} + +const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source) { + pa_assert(c); + pa_assert(source); + + return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index))); +} + +const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) { + pa_assert(c); + pa_assert(module); + + return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index))); +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 964a37bd..1b73782f 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -35,4 +35,8 @@ typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); void pa_dbusiface_core_free(pa_dbusiface_core *c); +const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink); +const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source); +const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); + #endif -- cgit From 3e9de1a36c3f85f558f08167cd82163b0cbf3484 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:12:31 +0300 Subject: dbus-util: Add helpers for proplist handling. --- src/pulsecore/dbus-util.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/dbus-util.h | 4 +++ 2 files changed, 75 insertions(+) diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index 5db7f218..903acadb 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -564,6 +564,21 @@ void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_r dbus_message_unref(reply); } +void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(proplist); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_dbus_append_proplist_variant(&msg_iter, proplist); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) { DBusMessageIter array_iter; unsigned i; @@ -640,6 +655,62 @@ void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, c pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } +void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) { + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter array_iter; + void *state = NULL; + const char *key; + + pa_assert(iter); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter)); + + while ((key = pa_proplist_iterate(proplist, state))) { + const void *value = NULL; + size_t nbytes; + + pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0); + + pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + + pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter)); + pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes)); + pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter)); + + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + } + + pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter)); +} + +void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) { + DBusMessageIter variant_iter; + + pa_assert(iter); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter)); + pa_dbus_append_proplist(&variant_iter, proplist); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); +} + +void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) { + DBusMessageIter dict_entry_iter; + + pa_assert(dict_iter); + pa_assert(key); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_dbus_append_proplist_variant(&dict_entry_iter, proplist); + pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); +} + int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { DBusMessageIter msg_iter; DBusMessageIter variant_iter; diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 97aae372..5443a4c1 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -69,12 +69,16 @@ void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); +void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist); void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); +void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist); +void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist); +void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist); /* Helper functions for extracting the value argument of a Set call. If the * message is invalid, an error reply is sent and a negative number is -- cgit From 76bd03bddb7967c56d68ead22e5d4eae5a82625a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:14:27 +0300 Subject: dbus-util: Trivial comment punctuation fix. --- src/pulsecore/dbus-util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 5443a4c1..9cee293c 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -91,8 +91,8 @@ int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); -/* Returns a new proplist, that the caller has to free. If the proplist can't - * be read from the iterator, an error reply is sent and NULL is returned. */ +/* Returns a new proplist that the caller has to free. If the proplist can't be + * read from the iterator, an error reply is sent and NULL is returned. */ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); #endif -- cgit From 7699cfd4c041eedd2dae124cac75da0879b87f1e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:18:03 +0300 Subject: dbus-protocol: Split some overly long lines. --- src/pulsecore/protocol-dbus.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 5cbd0d91..2461e4ba 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -199,19 +199,21 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_printf(buf, " \n", method_handler->method_name); for (i = 0; i < method_handler->n_arguments; ++i) - pa_strbuf_printf(buf, " \n", method_handler->arguments[i].name, - method_handler->arguments[i].type, - method_handler->arguments[i].direction); + pa_strbuf_printf(buf, " \n", + method_handler->arguments[i].name, + method_handler->arguments[i].type, + method_handler->arguments[i].direction); pa_strbuf_puts(buf, " \n"); } handlers_state = NULL; - while ((property_handler = pa_hashmap_iterate(iface_entry->property_handlers, &handlers_state, NULL))) - pa_strbuf_printf(buf, " \n", property_handler->property_name, - property_handler->type, - property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); + PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state) + pa_strbuf_printf(buf, " \n", + property_handler->property_name, + property_handler->type, + property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); for (i = 0; i < iface_entry->n_signals; ++i) { pa_strbuf_printf(buf, " \n", iface_entry->signals[i].name); -- cgit From 16dce8d7cbe4dcad56c3fbd1f47af6b50d2581a7 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:19:33 +0300 Subject: dbus-protocol: Take advantage of the helpers in dbus-util. --- src/pulsecore/protocol-dbus.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 2461e4ba..62f74a56 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -408,7 +409,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_dbus_method_handler *method_handler = NULL; pa_dbus_property_handler *property_handler = NULL; const char *attempted_property = NULL; - DBusMessage *reply = NULL; pa_assert(connection); pa_assert(message); @@ -427,10 +427,7 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - pa_assert_se((reply = dbus_message_new_method_return(message))); - pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); - + pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection); goto finish; } @@ -450,26 +447,23 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa case FOUND_GET_ALL: if (iface_entry->get_all_properties_cb) iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); + /* TODO: Write an else branch where a dummy response is sent. */ break; case PROPERTY_ACCESS_DENIED: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property); break; case NO_SUCH_METHOD: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)); break; case NO_SUCH_PROPERTY: - pa_assert_se((reply = dbus_message_new_error_printf(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property); break; case INVALID_MESSAGE_ARGUMENTS: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)); break; default: @@ -477,9 +471,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa } finish: - if (reply) - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; } -- cgit From acad5063284f37315c8208916f1ac151791d7220 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:20:22 +0300 Subject: dbusiface-card: Implement the Card D-Bus interface. --- src/Makefile.am | 1 + src/modules/dbus/iface-card-profile.c | 67 +++++ src/modules/dbus/iface-card-profile.h | 43 +++ src/modules/dbus/iface-card.c | 490 ++++++++++++++++++++++++++++++++++ 4 files changed, 601 insertions(+) create mode 100644 src/modules/dbus/iface-card-profile.c create mode 100644 src/modules/dbus/iface-card-profile.h diff --git a/src/Makefile.am b/src/Makefile.am index e10b4ab6..e605ad78 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1278,6 +1278,7 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMI module_dbus_protocol_la_SOURCES = \ modules/dbus/iface-card.c modules/dbus/iface-card.h \ + modules/dbus/iface-card-profile.c modules/dbus/iface-card-profile.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 \ diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c new file mode 100644 index 00000000..79524945 --- /dev/null +++ b/src/modules/dbus/iface-card-profile.c @@ -0,0 +1,67 @@ +/*** + 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 "iface-card-profile.h" + +#define OBJECT_NAME "profile" + +struct pa_dbusiface_card_profile { + pa_card_profile *profile; + char *path; +}; + +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx) { + pa_dbusiface_card_profile *p = NULL; + + pa_assert(card); + pa_assert(profile); + + p = pa_xnew(pa_dbusiface_card_profile, 1); + p->profile = profile; + p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx); + + return p; +} + +void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) { + pa_assert(p); + + pa_xfree(p->path); + pa_xfree(p); +} + +const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p) { + pa_assert(p); + + return p->path; +} + +const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p) { + pa_assert(p); + + return p->profile->name; +} diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h new file mode 100644 index 00000000..e90313cd --- /dev/null +++ b/src/modules/dbus/iface-card-profile.h @@ -0,0 +1,43 @@ +#ifndef foodbusifacecardprofilehfoo +#define foodbusifacecardprofilehfoo + +/*** + 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 interface org.PulseAudio.Core1.CardProfile. + * + * See http://pulseaudio.org/wiki/DBusInterface for the CardProfile interface + * documentation. + */ + +#include + +#include "iface-card.h" + +typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile; + +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx); +void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p); + +const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p); +const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p); + +#endif diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index e203c395..4ede718a 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -23,20 +23,476 @@ #include #endif +#include + #include +#include #include +#include "iface-card-profile.h" + #include "iface-card.h" #define OBJECT_NAME "card" +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); + struct pa_dbusiface_card { pa_dbusiface_core *core; pa_card *card; char *path; + pa_hashmap *profiles; + uint32_t next_profile_index; + pa_card_profile *active_profile; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_SINKS, + PROPERTY_HANDLER_SOURCES, + PROPERTY_HANDLER_PROFILES, + PROPERTY_HANDLER_ACTIVE_PROFILE, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "ao", .get_cb = handle_get_sinks, .set_cb = NULL }, + [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "ao", .get_cb = handle_get_sources, .set_cb = NULL }, + [PROPERTY_HANDLER_PROFILES] = { .property_name = "Profiles", .type = "ao", .get_cb = handle_get_profiles, .set_cb = NULL }, + [PROPERTY_HANDLER_ACTIVE_PROFILE] = { .property_name = "ActiveProfile", .type = "o", .get_cb = handle_get_active_profile, .set_cb = handle_set_active_profile }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_GET_PROFILE_BY_NAME, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info get_profile_by_name_args[] = { { "name", "s", "in" }, { "profile", "o", "out" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_GET_PROFILE_BY_NAME] = { + .method_name = "GetProfileByName", + .arguments = get_profile_by_name_args, + .n_arguments = sizeof(get_profile_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_profile_by_name } +}; + +enum signal_index { + SIGNAL_ACTIVE_PROFILE_UPDATED, + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info card_interface_info = { + .name = PA_DBUSIFACE_CARD_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + dbus_uint32_t idx; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->card->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->name); +} + +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char *owner_module; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->card->module) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Card %s doesn't have an owner module.", c->card->name); + return; + } + + owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sinks(pa_dbusiface_card *c, unsigned *n) { + const char **sinks = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_sink *sink = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->card->sinks); + + if (*n == 0) + return NULL; + + sinks = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(sink, c->card->sinks, idx) { + sinks[i] = pa_dbusiface_core_get_sink_path(c->core, sink); + ++i; + } + + return sinks; +} + +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char **sinks; + unsigned n_sinks; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sinks = get_sinks(c, &n_sinks); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); + + pa_xfree(sinks); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sources(pa_dbusiface_card *c, unsigned *n) { + const char **sources = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_source *source = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->card->sources); + + if (*n == 0) + return NULL; + + sources = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(source, c->card->sinks, idx) { + sources[i] = pa_dbusiface_core_get_source_path(c->core, source); + ++i; + } + + return sources; +} + +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char **sources; + unsigned n_sources; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sources = get_sources(c, &n_sources); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n_sources); + + pa_xfree(sources); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) { + const char **profiles; + unsigned i = 0; + void *state = NULL; + pa_dbusiface_card_profile *profile; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->profiles); + + if (*n == 0) + return NULL; + + profiles = pa_xnew(const char *, *n); + + PA_HASHMAP_FOREACH(profile, c->profiles, state) { + profiles[i] = pa_dbusiface_card_profile_get_path(profile); + ++i; + } + + return profiles; +} + +static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char **profiles; + unsigned n_profiles; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + profiles = get_profiles(c, &n_profiles); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles); + + pa_xfree(profiles); +} + +static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char *active_profile; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->active_profile) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); + return; + } + + active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile); +} + +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char *new_active_path; + pa_dbusiface_card_profile *new_active; + int r; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) + return; + + if (!c->active_profile) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The card %s has no profiles, and therefore there's no active profile either.", + c->card->name); + return; + } + + if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path); + return; + } + + if ((r = pa_card_set_profile(c->card, pa_dbusiface_card_profile_get_name(new_active), TRUE)) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Internal error in PulseAudio: pa_card_set_profile() failed with error code %i.", r); + return; + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_proplist_variant_reply(conn, msg, c->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx; + const char *owner_module = NULL; + const char **sinks = NULL; + unsigned n_sinks = 0; + const char **sources = NULL; + unsigned n_sources = 0; + const char **profiles = NULL; + unsigned n_profiles = 0; + const char *active_profile = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->card->index; + if (c->card->module) + owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module); + sinks = get_sinks(c, &n_sinks); + sources = get_sources(c, &n_sources); + profiles = get_profiles(c, &n_profiles); + if (c->active_profile) + active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &c->card->name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_STRING, &owner_module); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROFILES].property_name, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles); + + if (active_profile) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PROFILE].property_name, DBUS_TYPE_OBJECT_PATH, &active_profile); + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(sinks); + pa_xfree(sources); + pa_xfree(profiles); +} + +static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + DBusError error; + const char *profile_name = NULL; + pa_dbusiface_card_profile *profile = NULL; + const char *profile_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (!(profile = pa_hashmap_get(c->profiles, profile_name))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name); + return; + } + + profile_path = pa_dbusiface_card_profile_get_path(profile); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &profile_path); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(core); + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD); + pa_assert(c); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + + if (c->active_profile != c->card->active_profile) { + const char *object_path; + + c->active_profile = c->card->active_profile; + object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + + pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + if (!pa_proplist_equal(c->proplist, c->card->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); + + pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, c->proplist); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) { pa_dbusiface_card *c = NULL; @@ -48,13 +504,47 @@ pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) c->core = core; c->card = card; c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index); + c->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->next_profile_index = 0; + c->active_profile = NULL; + c->proplist = pa_proplist_copy(card->proplist); + c->dbus_protocol = pa_dbus_protocol_get(card->core); + c->subscription = pa_subscription_new(card->core, PA_SUBSCRIPTION_MASK_CARD, subscription_cb, c); + + if (card->profiles) { + pa_card_profile *profile; + void *state = NULL; + + PA_HASHMAP_FOREACH(profile, card->profiles, state) { + pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, profile, c->next_profile_index++); + pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p); + } + pa_assert_se(c->active_profile = card->active_profile); + } + + pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &card_interface_info, c) >= 0); return c; } +static void profile_free_cb(void *p, void *userdata) { + pa_dbusiface_card_profile *profile = p; + + pa_assert(profile); + + pa_dbusiface_card_profile_free(profile); +} + void pa_dbusiface_card_free(pa_dbusiface_card *c) { pa_assert(c); + pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, card_interface_info.name) >= 0); + + pa_hashmap_free(c->profiles, profile_free_cb, NULL); + pa_proplist_free(c->proplist); + pa_dbus_protocol_unref(c->dbus_protocol); + pa_subscription_free(c->subscription); + pa_xfree(c->path); pa_xfree(c); } -- cgit From 8c8df77d2a774a65741539381496d02f04747974 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 10:36:20 +0300 Subject: dbusiface-card-profile: Implement the CardProfile D-Bus interface. --- src/modules/dbus/iface-card-profile.c | 158 +++++++++++++++++++++++++++++++++- src/modules/dbus/iface-card-profile.h | 5 +- src/modules/dbus/iface-card.c | 2 +- src/modules/dbus/iface-card.h | 1 + 4 files changed, 163 insertions(+), 3 deletions(-) diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c index 79524945..2b85a5fc 100644 --- a/src/modules/dbus/iface-card-profile.c +++ b/src/modules/dbus/iface-card-profile.c @@ -23,26 +23,178 @@ #include #endif +#include + #include +#include #include "iface-card-profile.h" #define OBJECT_NAME "profile" +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + struct pa_dbusiface_card_profile { + uint32_t index; pa_card_profile *profile; char *path; + pa_dbus_protocol *dbus_protocol; +}; + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DESCRIPTION, + PROPERTY_HANDLER_SINKS, + PROPERTY_HANDLER_SOURCES, + PROPERTY_HANDLER_PRIORITY, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL }, + [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "u", .get_cb = handle_get_sinks, .set_cb = NULL }, + [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "u", .get_cb = handle_get_sources, .set_cb = NULL }, + [PROPERTY_HANDLER_PRIORITY] = { .property_name = "Priority", .type = "u", .get_cb = handle_get_priority, .set_cb = NULL }, }; -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx) { +static pa_dbus_interface_info profile_interface_info = { + .name = PA_DBUSIFACE_CARD_PROFILE_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->name); +} + +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->description); +} + +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + dbus_uint32_t sinks = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + sinks = p->profile->n_sinks; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sinks); +} + +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + dbus_uint32_t sources = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + sources = p->profile->n_sources; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sources); +} + +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + priority = p->profile->priority; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t sinks = 0; + dbus_uint32_t sources = 0; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + sinks = p->profile->n_sinks; + sources = p->profile->n_sources; + priority = p->profile->priority; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->profile->name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->profile->description); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_UINT32, &sinks); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_UINT32, &sources); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx) { pa_dbusiface_card_profile *p = NULL; pa_assert(card); pa_assert(profile); p = pa_xnew(pa_dbusiface_card_profile, 1); + p->index = idx; p->profile = profile; p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx); + p->dbus_protocol = pa_dbus_protocol_get(core); + + pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &profile_interface_info, p) >= 0); return p; } @@ -50,6 +202,10 @@ pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) { pa_assert(p); + pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, profile_interface_info.name) >= 0); + + pa_dbus_protocol_unref(p->dbus_protocol); + pa_xfree(p->path); pa_xfree(p); } diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h index e90313cd..9edcde73 100644 --- a/src/modules/dbus/iface-card-profile.h +++ b/src/modules/dbus/iface-card-profile.h @@ -29,12 +29,15 @@ */ #include +#include #include "iface-card.h" +#define PA_DBUSIFACE_CARD_PROFILE_INTERFACE PA_DBUS_CORE_INTERFACE ".CardProfile" + typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile; -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx); +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx); void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p); const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p); diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 4ede718a..64ec12a9 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -516,7 +516,7 @@ pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) void *state = NULL; PA_HASHMAP_FOREACH(profile, card->profiles, state) { - pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, profile, c->next_profile_index++); + pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, card->core, profile, c->next_profile_index++); pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p); } pa_assert_se(c->active_profile = card->active_profile); diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h index 57ad4e31..e2c08a3b 100644 --- a/src/modules/dbus/iface-card.h +++ b/src/modules/dbus/iface-card.h @@ -29,6 +29,7 @@ */ #include +#include #include "iface-core.h" -- cgit From 7cfda56af99b6d6bbb92fd6de208edd4c6991240 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 10 Aug 2009 10:38:01 +0300 Subject: dbus-protocol: Add a note for _send_signal that by default the signal isn't actually sent. --- src/pulsecore/protocol-dbus.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index c6b630a1..38ba8a18 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -149,6 +149,10 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c * do anything in that case either. */ void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal); +/* Sends the given signal to all interested clients. By default no signals are + * sent - clients have to explicitly to request signals by calling + * .Core1.ListenForSignal. That method's handler then calls + * pa_dbus_protocol_add_signal_listener(). */ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); /* Returns an array of extension identifier strings. The strings pointers point -- cgit From 31117fe99e07b11b55564a6df455211db712c2f3 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 10 Aug 2009 10:40:40 +0300 Subject: dbus-protocol: Fix signal sending for the case when the client doesn't listen for all signals. --- src/pulsecore/protocol-dbus.c | 11 ++++++++++- src/pulsecore/protocol-dbus.h | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 62f74a56..01285705 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -848,6 +848,8 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c for (i = 0; i < n_objects; ++i) pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL); + pa_hashmap_put(conn_entry->listening_signals, signal, object_set); + } else { conn_entry->listening_for_all_signals = TRUE; @@ -892,10 +894,15 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { void *state = NULL; pa_idxset *object_set; DBusMessage *signal_copy; + char *signal_string; pa_assert(p); pa_assert(signal); pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); + pa_assert_se(dbus_message_get_interface(signal)); + pa_assert_se(dbus_message_get_member(signal)); + + signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal), dbus_message_get_member(signal)); PA_HASHMAP_FOREACH(conn_entry, p->connections, state) { if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ @@ -903,7 +910,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { || pa_idxset_isempty(conn_entry->all_signals_objects))) || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */ - && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal)) + && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal_string)) && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL) || pa_idxset_isempty(object_set)))) { @@ -912,6 +919,8 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { dbus_message_unref(signal_copy); } } + + pa_xfree(signal_string); } const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index 38ba8a18..d771b4fc 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -129,7 +129,8 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection * pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); /* Enables signal receiving for the given connection. The connection must have - * been registered earlier. + * been registered earlier. The signal string must contain both the signal + * interface and the signal name, concatenated using a period as the separator. * * If the signal argument is NULL, all signals will be sent to the connection, * otherwise calling this function only adds the given signal to the list of -- cgit From 8b5550dba32857918fd8e70b93be9094c11cf0c8 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 05:51:55 +0300 Subject: dbusiface-card: Split some overly long lines. --- src/modules/dbus/iface-card-profile.c | 6 +++++- src/modules/dbus/iface-card-profile.h | 6 +++++- src/modules/dbus/iface-card.c | 13 ++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c index 2b85a5fc..4a1696c1 100644 --- a/src/modules/dbus/iface-card-profile.c +++ b/src/modules/dbus/iface-card-profile.c @@ -182,7 +182,11 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat dbus_message_unref(reply); } -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx) { +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new( + pa_dbusiface_card *card, + pa_core *core, + pa_card_profile *profile, + uint32_t idx) { pa_dbusiface_card_profile *p = NULL; pa_assert(card); diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h index 9edcde73..a09767f8 100644 --- a/src/modules/dbus/iface-card-profile.h +++ b/src/modules/dbus/iface-card-profile.h @@ -37,7 +37,11 @@ typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile; -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx); +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new( + pa_dbusiface_card *card, + pa_core *core, + pa_card_profile *profile, + uint32_t idx); void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p); const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p); diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 64ec12a9..40031d2e 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -312,7 +312,10 @@ static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, vo pa_assert(c); if (!c->active_profile) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); + pa_assert(pa_hashmap_isempty(c->profiles)); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); return; } @@ -470,7 +473,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 c->active_profile = c->card->active_profile; object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); - pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -483,7 +488,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); - pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); dbus_message_iter_init_append(signal, &msg_iter); pa_dbus_append_proplist(&msg_iter, c->proplist); -- cgit From afb79ee83e95b3c492abe55ea0c26a9fe902b075 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 05:53:36 +0300 Subject: dbusiface-card-profile: Assert the core argument isn't NULL. --- src/modules/dbus/iface-card-profile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c index 4a1696c1..004e2e88 100644 --- a/src/modules/dbus/iface-card-profile.c +++ b/src/modules/dbus/iface-card-profile.c @@ -190,6 +190,7 @@ pa_dbusiface_card_profile *pa_dbusiface_card_profile_new( pa_dbusiface_card_profile *p = NULL; pa_assert(card); + pa_assert(core); pa_assert(profile); p = pa_xnew(pa_dbusiface_card_profile, 1); -- cgit From 18f9f1b5d17c83280205083760edd4dccb37ed6a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 05:58:24 +0300 Subject: dbusiface-card: Use the ++ operator like it's meant to be used. --- src/modules/dbus/iface-card.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 40031d2e..e3606c79 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -279,10 +279,8 @@ static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) { profiles = pa_xnew(const char *, *n); - PA_HASHMAP_FOREACH(profile, c->profiles, state) { - profiles[i] = pa_dbusiface_card_profile_get_path(profile); - ++i; - } + PA_HASHMAP_FOREACH(profile, c->profiles, state) + profiles[i++] = pa_dbusiface_card_profile_get_path(profile); return profiles; } -- cgit From 31c544d8439edee56ecc641c07012042ab38bb80 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:00:46 +0300 Subject: dbusiface-card: Assert that the profiles list is empty if there's no active profile. --- src/modules/dbus/iface-card.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index e3606c79..924bea9c 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -336,6 +336,8 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo return; if (!c->active_profile) { + pa_assert(pa_hashmap_isempty(c->profiles)); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); -- cgit From 90c73db449e67cf999a354c7e3ccad3edd44a829 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:09:35 +0300 Subject: dbusiface-card: Fix the OwnerModule property type in handle_get_all(). --- src/modules/dbus/iface-card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 924bea9c..5a797948 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -406,7 +406,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver); if (owner_module) - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_STRING, &owner_module); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources); -- cgit From 1e65d8d35b23d3c76a30155c42bec282257579e5 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:11:38 +0300 Subject: dbusiface-core: New function: pa_dbusiface_core_get_card_path(). --- src/modules/dbus/iface-core.c | 7 +++++++ src/modules/dbus/iface-core.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e8ea50ba..81e709f5 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2034,6 +2034,13 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_xfree(c); } +const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card) { + pa_assert(c); + pa_assert(card); + + return pa_dbusiface_card_get_path(pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index))); +} + const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) { pa_assert(c); pa_assert(sink); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 1b73782f..70102054 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -35,6 +35,7 @@ typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); void pa_dbusiface_core_free(pa_dbusiface_core *c); +const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card); const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink); const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source); const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); -- cgit From 22ab1414507f84d6a42c5255d66ba15d8097d35f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:13:17 +0300 Subject: dbus-protocol: Use pa_hashmap_remove() instead of _get(). --- src/pulsecore/protocol-dbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 01285705..cf561c83 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -841,7 +841,7 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c conn_entry->listening_for_all_signals = FALSE; /* Replace the old object list with a new one. */ - if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal))) + if ((object_set = pa_hashmap_remove(conn_entry->listening_signals, signal))) pa_idxset_free(object_set, free_listened_object_name_cb, NULL); object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -- cgit From 91f626f777402fa6485658b04cbcdf2a7aac3f01 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:18:41 +0300 Subject: dbusiface-device: Implement the Device and DevicePort D-Bus interfaces. --- src/Makefile.am | 1 + src/modules/dbus/iface-device-port.c | 190 ++++++ src/modules/dbus/iface-device-port.h | 50 ++ src/modules/dbus/iface-device.c | 1208 +++++++++++++++++++++++++++++++++- src/modules/dbus/iface-device.h | 5 + 5 files changed, 1448 insertions(+), 6 deletions(-) create mode 100644 src/modules/dbus/iface-device-port.c create mode 100644 src/modules/dbus/iface-device-port.h diff --git a/src/Makefile.am b/src/Makefile.am index e605ad78..8722f978 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1282,6 +1282,7 @@ module_dbus_protocol_la_SOURCES = \ 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-device-port.c modules/dbus/iface-device-port.h \ modules/dbus/iface-memstats.c modules/dbus/iface-memstats.h \ modules/dbus/iface-module.c modules/dbus/iface-module.h \ modules/dbus/iface-sample.c modules/dbus/iface-sample.h \ diff --git a/src/modules/dbus/iface-device-port.c b/src/modules/dbus/iface-device-port.c new file mode 100644 index 00000000..d403b6a2 --- /dev/null +++ b/src/modules/dbus/iface-device-port.c @@ -0,0 +1,190 @@ +/*** + 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 "iface-device-port.h" + +#define OBJECT_NAME "port" + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +struct pa_dbusiface_device_port { + uint32_t index; + pa_device_port *port; + char *path; + pa_dbus_protocol *dbus_protocol; +}; + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DESCRIPTION, + PROPERTY_HANDLER_PRIORITY, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL }, + [PROPERTY_HANDLER_PRIORITY] = { .property_name = "Priority", .type = "u", .get_cb = handle_get_priority, .set_cb = NULL }, +}; + +static pa_dbus_interface_info port_interface_info = { + .name = PA_DBUSIFACE_DEVICE_PORT_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->name); +} + +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->description); +} + +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + priority = p->port->priority; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + priority = p->port->priority; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->port->name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->port->description); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +pa_dbusiface_device_port *pa_dbusiface_device_port_new( + pa_dbusiface_device *device, + pa_core *core, + pa_device_port *port, + uint32_t idx) { + pa_dbusiface_device_port *p = NULL; + + pa_assert(device); + pa_assert(core); + pa_assert(port); + + p = pa_xnew(pa_dbusiface_device_port, 1); + p->index = idx; + p->port = port; + p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_device_get_path(device), OBJECT_NAME, idx); + p->dbus_protocol = pa_dbus_protocol_get(core); + + pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &port_interface_info, p) >= 0); + + return p; +} + +void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p) { + pa_assert(p); + + pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, port_interface_info.name) >= 0); + + pa_dbus_protocol_unref(p->dbus_protocol); + + pa_xfree(p->path); + pa_xfree(p); +} + +const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p) { + pa_assert(p); + + return p->path; +} + +const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p) { + pa_assert(p); + + return p->port->name; +} diff --git a/src/modules/dbus/iface-device-port.h b/src/modules/dbus/iface-device-port.h new file mode 100644 index 00000000..0461e2ff --- /dev/null +++ b/src/modules/dbus/iface-device-port.h @@ -0,0 +1,50 @@ +#ifndef foodbusifacedeviceporthfoo +#define foodbusifacedeviceporthfoo + +/*** + 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 interface org.PulseAudio.Core1.DevicePort. + * + * See http://pulseaudio.org/wiki/DBusInterface for the DevicePort interface + * documentation. + */ + +#include +#include + +#include "iface-device.h" + +#define PA_DBUSIFACE_DEVICE_PORT_INTERFACE PA_DBUS_CORE_INTERFACE ".DevicePort" + +typedef struct pa_dbusiface_device_port pa_dbusiface_device_port; + +pa_dbusiface_device_port *pa_dbusiface_device_port_new( + pa_dbusiface_device *device, + pa_core *core, + pa_device_port *port, + uint32_t idx); +void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p); + +const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p); +const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p); + +#endif diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 1a64f43c..3cf9d19c 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -24,62 +24,1258 @@ #endif #include +#include #include +#include "iface-device-port.h" + #include "iface-device.h" #define SINK_OBJECT_NAME "sink" #define SOURCE_OBJECT_NAME "source" +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + enum device_type { DEVICE_TYPE_SINK, DEVICE_TYPE_SOURCE }; struct pa_dbusiface_device { + pa_dbusiface_core *core; + union { pa_sink *sink; pa_source *source; }; enum device_type type; char *path; + pa_cvolume volume; + pa_bool_t is_muted; + union { + pa_sink_state_t sink_state; + pa_source_state_t source_state; + }; + pa_hashmap *ports; + uint32_t next_port_index; + pa_device_port *active_port; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_CARD, + PROPERTY_HANDLER_SAMPLE_FORMAT, + PROPERTY_HANDLER_SAMPLE_RATE, + PROPERTY_HANDLER_CHANNELS, + PROPERTY_HANDLER_VOLUME, + PROPERTY_HANDLER_HAS_FLAT_VOLUME, + PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME, + PROPERTY_HANDLER_BASE_VOLUME, + PROPERTY_HANDLER_VOLUME_STEPS, + PROPERTY_HANDLER_IS_MUTED, + PROPERTY_HANDLER_HAS_HARDWARE_VOLUME, + PROPERTY_HANDLER_HAS_HARDWARE_MUTE, + PROPERTY_HANDLER_CONFIGURED_LATENCY, + PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY, + PROPERTY_HANDLER_LATENCY, + PROPERTY_HANDLER_IS_HARDWARE_DEVICE, + PROPERTY_HANDLER_IS_NETWORK_DEVICE, + PROPERTY_HANDLER_STATE, + PROPERTY_HANDLER_PORTS, + PROPERTY_HANDLER_ACTIVE_PORT, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +enum sink_property_handler_index { + SINK_PROPERTY_HANDLER_MONITOR_SOURCE, + SINK_PROPERTY_HANDLER_MAX +}; + +enum source_property_handler_index { + SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK, + SOURCE_PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_CARD] = { .property_name = "Card", .type = "o", .get_cb = handle_get_card, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, + [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, + [PROPERTY_HANDLER_HAS_FLAT_VOLUME] = { .property_name = "HasFlatVolume", .type = "b", .get_cb = handle_get_has_flat_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b", .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_BASE_VOLUME] = { .property_name = "BaseVolume", .type = "u", .get_cb = handle_get_base_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_VOLUME_STEPS] = { .property_name = "VolumeSteps", .type = "u", .get_cb = handle_get_volume_steps, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, + [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME] = { .property_name = "HasHardwareVolume", .type = "b", .get_cb = handle_get_has_hardware_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_HAS_HARDWARE_MUTE] = { .property_name = "HasHardwareMute", .type = "b", .get_cb = handle_get_has_hardware_mute, .set_cb = NULL }, + [PROPERTY_HANDLER_CONFIGURED_LATENCY] = { .property_name = "ConfiguredLatency", .type = "t", .get_cb = handle_get_configured_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY] = { .property_name = "HasDynamicLatency", .type = "b", .get_cb = handle_get_has_dynamic_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_LATENCY] = { .property_name = "Latency", .type = "t", .get_cb = handle_get_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_HARDWARE_DEVICE] = { .property_name = "IsHardwareDevice", .type = "b", .get_cb = handle_get_is_hardware_device, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_NETWORK_DEVICE] = { .property_name = "IsNetworkDevice", .type = "b", .get_cb = handle_get_is_network_device, .set_cb = NULL }, + [PROPERTY_HANDLER_STATE] = { .property_name = "State", .type = "u", .get_cb = handle_get_state, .set_cb = NULL }, + [PROPERTY_HANDLER_PORTS] = { .property_name = "Ports", .type = "ao", .get_cb = handle_get_ports, .set_cb = NULL }, + [PROPERTY_HANDLER_ACTIVE_PORT] = { .property_name = "ActivePort", .type = "o", .get_cb = handle_get_active_port, .set_cb = handle_set_active_port }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +static pa_dbus_property_handler sink_property_handlers[SINK_PROPERTY_HANDLER_MAX] = { + [SINK_PROPERTY_HANDLER_MONITOR_SOURCE] = { .property_name = "MonitorSource", .type = "o", .get_cb = handle_sink_get_monitor_source, .set_cb = NULL } +}; + +static pa_dbus_property_handler source_property_handlers[SOURCE_PROPERTY_HANDLER_MAX] = { + [SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK] = { .property_name = "MonitorOfSink", .type = "o", .get_cb = handle_source_get_monitor_of_sink, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_SUSPEND, + METHOD_HANDLER_GET_PORT_BY_NAME, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info suspend_args[] = { { "suspend", "b", "in" } }; +static pa_dbus_arg_info get_port_by_name_args[] = { { "name", "s", "in" }, { "port", "o", "out" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_SUSPEND] = { + .method_name = "Suspend", + .arguments = suspend_args, + .n_arguments = sizeof(suspend_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_suspend }, + [METHOD_HANDLER_GET_PORT_BY_NAME] = { + .method_name = "GetPortByName", + .arguments = get_port_by_name_args, + .n_arguments = sizeof(get_port_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_port_by_name } +}; + +enum signal_index { + SIGNAL_VOLUME_UPDATED, + SIGNAL_MUTE_UPDATED, + SIGNAL_STATE_UPDATED, + SIGNAL_ACTIVE_PORT_UPDATED, + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } }; +static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } }; +static pa_dbus_arg_info state_updated_args[] = { { "state", "u", NULL } }; +static pa_dbus_arg_info active_port_updated_args[] = { { "port", "o", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 }, + [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 }, + [SIGNAL_STATE_UPDATED] = { .name = "StateUpdated", .arguments = state_updated_args, .n_arguments = 1 }, + [SIGNAL_ACTIVE_PORT_UPDATED] = { .name = "ActivePortUpdated", .arguments = active_port_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info device_interface_info = { + .name = PA_DBUSIFACE_DEVICE_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static pa_dbus_interface_info sink_interface_info = { + .name = PA_DBUSIFACE_SINK_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = sink_property_handlers, + .n_property_handlers = SINK_PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_sink_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static pa_dbus_interface_info source_interface_info = { + .name = PA_DBUSIFACE_SOURCE_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = source_property_handlers, + .n_property_handlers = SOURCE_PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_source_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *name = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &name); +} + +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *driver = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + pa_module *owner_module = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module; + + if (!owner_module) { + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't have an owner module.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't have an owner module.", d->source->name); + return; + } + + object_path = pa_dbusiface_core_get_module_path(d->core, owner_module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + pa_card *card = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card; + + if (!card) { + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't belong to any card.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't belong to any card.", d->source->name); + return; + } + + object_path = pa_dbusiface_core_get_card_path(d->core, card); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t sample_format = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); +} + +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t sample_rate = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate); +} + +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map; + + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels); +} + +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels); +} + +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + unsigned device_channels = 0; + dbus_uint32_t *volume = NULL; + unsigned n_volume_entries = 0; + pa_cvolume new_vol; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + pa_cvolume_init(&new_vol); + + device_channels = (d->type == DEVICE_TYPE_SINK) ? d->sink->channel_map.channels : d->source->channel_map.channels; + + new_vol.channels = device_channels; + + if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) + return; + + if (n_volume_entries != device_channels) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", device_channels, n_volume_entries); + return; + } + + for (i = 0; i < n_volume_entries; ++i) { + if (volume[i] > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]); + return; + } + new_vol.values[i] = volume[i]; + } + + if (d->type == DEVICE_TYPE_SINK) + pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE, TRUE, TRUE); + else + pa_source_set_volume(d->source, &new_vol, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_flat_volume = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_flat_volume); +} + +static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_convertible_to_decibel_volume = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME) + : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume); +} + +static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t base_volume; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &base_volume); +} + +static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t volume_steps; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps); +} + +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted); +} + +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t is_muted = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) + return; + + if (d->type == DEVICE_TYPE_SINK) + pa_sink_set_mute(d->sink, is_muted, TRUE); + else + pa_source_set_mute(d->source, is_muted, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_hardware_volume = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_hardware_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL) + : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_volume); +} + +static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_hardware_mute = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_hardware_mute = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL) + : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_mute); +} + +static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint64_t configured_latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + configured_latency = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_requested_latency(d->sink) + : pa_source_get_requested_latency(d->source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &configured_latency); +} + +static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_dynamic_latency = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_dynamic_latency = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY) + : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_dynamic_latency); +} + +static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint64_t latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (d->type == DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY)) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't support latency querying.", d->sink->name); + else if (d->type == DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY)) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't support latency querying.", d->source->name); + return; + + latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &latency); +} + +static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t is_hardware_device = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + is_hardware_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HARDWARE) + : (d->source->flags & PA_SOURCE_HARDWARE); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_hardware_device); +} + +static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t is_network_device = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + is_network_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_NETWORK) + : (d->source->flags & PA_SOURCE_NETWORK); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_network_device); +} + +static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t state; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &state); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_ports(pa_dbusiface_device *d, unsigned *n) { + const char **ports; + unsigned i = 0; + void *state = NULL; + pa_dbusiface_device_port *port = NULL; + + pa_assert(d); + pa_assert(n); + + *n = pa_hashmap_size(d->ports); + + if (*n == 0) + return NULL; + + ports = pa_xnew(const char *, *n); + + PA_HASHMAP_FOREACH(port, d->ports, state) + ports[i++] = pa_dbusiface_device_port_get_path(port); + + return ports; +} + +static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char **ports = NULL; + unsigned n_ports = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + ports = get_ports(d, &n_ports); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, ports, n_ports); + + pa_xfree(ports); +} + +static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *active_port; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (!d->active_port) { + pa_assert(pa_hashmap_isempty(d->ports)); + + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The sink %s has no ports, and therefore there's no active port either.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The source %s has no ports, and therefore there's no active port either.", d->source->name); + return; + } + + active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port); +} + +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *new_active_path; + pa_dbusiface_device_port *new_active; + int r; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) + return; + + if (!d->active_port) { + pa_assert(pa_hashmap_isempty(d->ports)); + + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The sink %s has no ports, and therefore there's no active port either.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The source %s has no ports, and therefore there's no active port either.", d->source->name); + return; + } + + if (!(new_active = pa_hashmap_get(d->ports, new_active_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such port.", new_active_path); + return; + } + + if (d->type == DEVICE_TYPE_SINK) { + if ((r = pa_sink_set_port(d->sink, pa_dbusiface_device_port_get_name(new_active), TRUE)) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Internal error in PulseAudio: pa_sink_set_port() failed with error code %i.", r); + return; + } + } else { + if ((r = pa_source_set_port(d->source, pa_dbusiface_device_port_get_name(new_active), TRUE)) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Internal error in PulseAudio: pa_source_set_port() failed with error code %i.", r); + return; + } + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + pa_dbus_send_proplist_variant_reply(conn, msg, d->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx = 0; + const char *name = NULL; + const char *driver = NULL; + pa_module *owner_module = NULL; + const char *owner_module_path = NULL; + pa_card *card = NULL; + const char *card_path = NULL; + dbus_uint32_t sample_format = 0; + dbus_uint32_t sample_rate = 0; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_bool_t has_flat_volume = FALSE; + dbus_bool_t has_convertible_to_decibel_volume = FALSE; + dbus_uint32_t base_volume = 0; + dbus_uint32_t volume_steps = 0; + dbus_bool_t has_hardware_volume = FALSE; + dbus_bool_t has_hardware_mute = FALSE; + dbus_uint64_t configured_latency = 0; + dbus_bool_t has_dynamic_latency = FALSE; + dbus_uint64_t latency = 0; + dbus_bool_t is_hardware_device = FALSE; + dbus_bool_t is_network_device = FALSE; + dbus_uint32_t state = 0; + const char **ports = NULL; + unsigned n_ports = 0; + const char *active_port = NULL; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index; + name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name; + driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver; + owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module; + if (owner_module) + owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module); + card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card; + if (card) + card_path = pa_dbusiface_core_get_card_path(d->core, card); + sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format; + sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate; + channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map; + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; + has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE; + has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME) + : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME); + base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume; + volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps; + has_hardware_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL) + : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL); + has_hardware_mute = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL) + : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL); + configured_latency = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_requested_latency(d->sink) + : pa_source_get_requested_latency(d->source); + has_dynamic_latency = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY) + : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY); + latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); + is_hardware_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HARDWARE) + : (d->source->flags & PA_SOURCE_HARDWARE); + is_network_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_NETWORK) + : (d->source->flags & PA_SOURCE_NETWORK); + state = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_state(d->sink) : pa_source_get_state(d->source); + ports = get_ports(d, &n_ports); + if (d->active_port) + active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path); + + if (card) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARD].property_name, DBUS_TYPE_OBJECT_PATH, &card_path); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, d->volume.channels); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_FLAT_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_flat_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &d->is_muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY].property_name, DBUS_TYPE_BOOLEAN, &has_dynamic_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_LATENCY].property_name, DBUS_TYPE_UINT64, &latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_HARDWARE_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_hardware_device); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_NETWORK_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_network_device); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_STATE].property_name, DBUS_TYPE_UINT32, &state); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PORTS].property_name, DBUS_TYPE_OBJECT_PATH, ports, n_ports); + + if (active_port) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PORT].property_name, DBUS_TYPE_OBJECT_PATH, &active_port); + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, d->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(ports); +} + +static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t suspend = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &suspend) < 0) + return; + + if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed."); + return; + } else if ((d->type == DEVICE_TYPE_SOURCE) && (pa_source_suspend(d->source, suspend, PA_SUSPEND_USER) < 0)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_source_suspend() failed."); + return; + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusError error; + const char *port_name = NULL; + pa_dbusiface_device_port *port = NULL; + const char *port_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (!(port = pa_hashmap_get(d->ports, port_name))) { + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, + "%s: No such port on sink %s.", port_name, d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, + "%s: No such port on source %s.", port_name, d->source->name); + return; + } + + port_path = pa_dbusiface_device_port_get_path(port); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &port_path); +} + +static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *monitor_source = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SINK); + + monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_source); +} + +static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + const char *monitor_source = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SINK); + + monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SINK_PROPERTY_HANDLER_MONITOR_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_source); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *monitor_of_sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SOURCE); + + if (!d->source->monitor_of) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s is not a monitor source.", d->source->name); + return; + } + + monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink); +} + +static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + const char *monitor_of_sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SOURCE); + + if (d->source->monitor_of) + monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + if (monitor_of_sink) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_device *d = userdata; + + pa_assert(c); + pa_assert(d); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + const pa_cvolume *new_volume = NULL; + pa_bool_t new_muted = FALSE; + pa_sink_state_t new_sink_state = 0; + pa_source_state_t new_source_state = 0; + pa_device_port *new_active_port = NULL; + pa_proplist *new_proplist = NULL; + unsigned i = 0; + + pa_assert(((d->type == DEVICE_TYPE_SINK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)) + || ((d->type == DEVICE_TYPE_SOURCE) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); + + new_volume = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_volume(d->sink, FALSE, FALSE) + : pa_source_get_volume(d->source, FALSE); + + if (!pa_cvolume_equal(&d->volume, new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; + + d->volume = *new_volume; + + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels, + DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); + + if (d->is_muted != new_muted) { + d->is_muted = new_muted; + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + if (d->type == DEVICE_TYPE_SINK) + new_sink_state = pa_sink_get_state(d->sink); + else + new_source_state = pa_source_get_state(d->source); + + if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state) + || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) { + dbus_uint32_t state = 0; + + if (d->type == DEVICE_TYPE_SINK) + d->sink_state = new_sink_state; + else + d->source_state = new_source_state; + + state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_STATE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port; + + if (d->active_port != new_active_port) { + const char *object_path = NULL; + + d->active_port = new_active_port; + object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_ACTIVE_PORT_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist; + + if (!pa_proplist_equal(d->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, d->proplist); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} + pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) { - pa_dbusiface_device *d; + pa_dbusiface_device *d = NULL; pa_assert(core); pa_assert(sink); - d = pa_xnew(pa_dbusiface_device, 1); + d = pa_xnew0(pa_dbusiface_device, 1); + d->core = core; d->sink = pa_sink_ref(sink); d->type = DEVICE_TYPE_SINK; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); + d->volume = *pa_sink_get_volume(sink, FALSE, FALSE); + d->is_muted = pa_sink_get_mute(sink, FALSE); + d->sink_state = pa_sink_get_state(sink); + d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + d->next_port_index = 0; + d->active_port = NULL; + d->proplist = pa_proplist_copy(sink->proplist); + d->dbus_protocol = pa_dbus_protocol_get(sink->core); + d->subscription = pa_subscription_new(sink->core, PA_SUBSCRIPTION_MASK_SINK, subscription_cb, d); + + if (sink->ports) { + pa_device_port *port; + void *state = NULL; + + PA_HASHMAP_FOREACH(port, sink->ports, state) { + pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, sink->core, port, d->next_port_index++); + pa_hashmap_put(d->ports, pa_dbusiface_device_port_get_name(p), p); + } + pa_assert_se(d->active_port = sink->active_port); + } + + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0); + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &sink_interface_info, d) >= 0); return d; } pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) { - pa_dbusiface_device *d; + pa_dbusiface_device *d = NULL; pa_assert(core); pa_assert(source); - d = pa_xnew(pa_dbusiface_device, 1); + d = pa_xnew0(pa_dbusiface_device, 1); + d->core = core; d->source = pa_source_ref(source); d->type = DEVICE_TYPE_SOURCE; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index); + d->volume = *pa_source_get_volume(source, FALSE); + d->is_muted = pa_source_get_mute(source, FALSE); + d->source_state = pa_source_get_state(source); + d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + d->next_port_index = 0; + d->active_port = NULL; + d->proplist = pa_proplist_copy(source->proplist); + d->dbus_protocol = pa_dbus_protocol_get(source->core); + d->subscription = pa_subscription_new(source->core, PA_SUBSCRIPTION_MASK_SOURCE, subscription_cb, d); + + if (source->ports) { + pa_device_port *port; + void *state = NULL; + + PA_HASHMAP_FOREACH(port, source->ports, state) { + pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, source->core, port, d->next_port_index++); + pa_hashmap_put(d->ports, pa_dbusiface_device_port_get_name(p), p); + } + pa_assert_se(d->active_port = source->active_port); + } + + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0); + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &source_interface_info, d) >= 0); return d; } +static void port_free_cb(void *p, void *userdata) { + pa_dbusiface_device_port *port = p; + + pa_assert(port); + + pa_dbusiface_device_port_free(port); +} + void pa_dbusiface_device_free(pa_dbusiface_device *d) { pa_assert(d); - if (d->type == DEVICE_TYPE_SINK) + pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, device_interface_info.name) >= 0); + + if (d->type == DEVICE_TYPE_SINK) { + pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, sink_interface_info.name) >= 0); pa_sink_unref(d->sink); - else + + } else { + pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, source_interface_info.name) >= 0); pa_source_unref(d->source); + } + pa_hashmap_free(d->ports, port_free_cb, NULL); + pa_dbus_protocol_unref(d->dbus_protocol); + pa_subscription_free(d->subscription); pa_xfree(d->path); pa_xfree(d); diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h index 1e9af83a..62e05e9a 100644 --- a/src/modules/dbus/iface-device.h +++ b/src/modules/dbus/iface-device.h @@ -29,11 +29,16 @@ * documentation. */ +#include #include #include #include "iface-core.h" +#define PA_DBUSIFACE_DEVICE_INTERFACE PA_DBUS_CORE_INTERFACE ".Device" +#define PA_DBUSIFACE_SINK_INTERFACE PA_DBUS_CORE_INTERFACE ".Sink" +#define PA_DBUSIFACE_SOURCE_INTERFACE PA_DBUS_CORE_INTERFACE ".Source" + typedef struct pa_dbusiface_device pa_dbusiface_device; pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink); -- cgit From f663d13acd306feafe2526c6a9258f14fd5d2ffc Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 16:54:11 +0300 Subject: dbusiface-core: Two new functions: pa_dbusiface_core_get_playback/record_stream_path(). --- src/modules/dbus/iface-core.c | 14 ++++++++++++++ src/modules/dbus/iface-core.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 81e709f5..2b5cf0b9 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2055,6 +2055,20 @@ const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_sou return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index))); } +const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input) { + pa_assert(c); + pa_assert(sink_input); + + return pa_dbusiface_stream_get_path(pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index))); +} + +const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output) { + pa_assert(c); + pa_assert(source_output); + + return pa_dbusiface_stream_get_path(pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(source_output->index))); +} + const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) { pa_assert(c); pa_assert(module); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 70102054..14dff7eb 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -38,6 +38,8 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c); const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card); const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink); const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source); +const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input); +const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output); const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); #endif -- cgit From 9ed25d7388bacfb0948d21d9aefa4fe92dbea0b5 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 16:55:29 +0300 Subject: dbusiface-client: Implement the properties of the Client D-Bus interface. Based on a patch from Vincent Filali-Ansary. --- src/modules/dbus/iface-client.c | 279 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 278 insertions(+), 1 deletion(-) diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index d9c8653f..dd4e57ae 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -2,6 +2,7 @@ This file is part of PulseAudio. Copyright 2009 Tanu Kaskinen + Copyright 2009 Vincent Filali-Ansary PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -23,7 +24,10 @@ #include #endif +#include + #include +#include #include #include "iface-client.h" @@ -31,19 +35,288 @@ #define OBJECT_NAME "client" struct pa_dbusiface_client { + pa_dbusiface_core *core; + pa_client *client; char *path; + + pa_dbus_protocol *dbus_protocol; +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +/*static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_PLAYBACK_STREAMS, + PROPERTY_HANDLER_RECORD_STREAMS, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao", .get_cb = handle_get_playback_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_RECORD_STREAMS] = { .property_name = "RecordStreams", .type = "ao", .get_cb = handle_get_record_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +/*enum method_handler_index { + METHOD_HANDLER_KILL, + METHOD_HANDLER_UPDATE_PROPERTIES, + METHOD_HANDLER_REMOVE_PROPERTIES, + METHOD_HANDLER_MAX }; +static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } }; +static pa_dbus_arg_info update_properties_args[] = { { "keys", "as", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_KILL] = { + .method_name = "Kill", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_kill }, + [METHOD_HANDLER_UPDATE_PROPERTIES] = { + .method_name = "UpdateProperties", + .arguments = update_propertes_args, + .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_update_properties }, + [METHOD_HANDLER_REMOVE_PROPERTIES] = { + .method_name = "RemoveProperties", + .arguments = remove_propertes_args, + .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_update_properties } +}; + +enum signal_index { + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_CLIENT_EVENT, + SIGNAL_MAX +}; + +static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +};*/ + +static pa_dbus_interface_info client_interface_info = { + .name = OBJECT_NAME, + .method_handlers = /*method_handlers*/ NULL, + .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = /*signals*/ NULL, + .n_signals = /*SIGNAL_MAX*/ 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->client->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->client->driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + const char *owner_module = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->client->module) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Client %d doesn't have an owner module.", c->client->index); + return; + } + + owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_playback_streams(pa_dbusiface_client *c, unsigned *n) { + const char **playback_streams = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_sink_input *sink_input = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->client->sink_inputs); + + if (*n == 0) + return NULL; + + playback_streams = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(sink_input, c->client->sink_inputs, idx) + playback_streams[i++] = pa_dbusiface_core_get_playback_stream_path(c->core, sink_input); + + return playback_streams; +} + +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + const char **playback_streams = NULL; + unsigned n_playback_streams = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + playback_streams = get_playback_streams(c, &n_playback_streams); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams); + + pa_xfree(playback_streams); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_record_streams(pa_dbusiface_client *c, unsigned *n) { + const char **record_streams = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_source_output *source_output = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->client->source_outputs); + + if (*n == 0) + return NULL; + + record_streams = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(source_output, c->client->source_outputs, idx) + record_streams[i++] = pa_dbusiface_core_get_record_stream_path(c->core, source_output); + + return record_streams; +} + +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + const char **record_streams = NULL; + unsigned n_record_streams = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + record_streams = get_record_streams(c, &n_record_streams); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams); + + pa_xfree(record_streams); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_proplist_variant_reply(conn, msg, c->client->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx = 0; + const char *owner_module = NULL; + const char **playback_streams = NULL; + unsigned n_playback_streams = 0; + const char **record_streams = NULL; + unsigned n_record_streams = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->client->index; + if (c->client->module) + owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module); + playback_streams = get_playback_streams(c, &n_playback_streams); + record_streams = get_record_streams(c, &n_record_streams); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->client->driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams); + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->client->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(playback_streams); + pa_xfree(record_streams); +} + pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { - pa_dbusiface_client *c; + pa_dbusiface_client *c = NULL; pa_assert(core); pa_assert(client); c = pa_xnew(pa_dbusiface_client, 1); + c->core = core; c->client = client; c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); + c->dbus_protocol = pa_dbus_protocol_get(client->core); + + pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0); return c; } @@ -51,6 +324,10 @@ pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client void pa_dbusiface_client_free(pa_dbusiface_client *c) { pa_assert(c); + pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, client_interface_info.name) >= 0); + + pa_dbus_protocol_unref(c->dbus_protocol); + pa_xfree(c->path); pa_xfree(c); } -- cgit From c7f4ed3c7a0453ff29181fe03666e0c7d3822317 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 17:08:21 +0300 Subject: dbusiface-client: Fix the interface name. --- src/modules/dbus/iface-client.c | 2 +- src/modules/dbus/iface-client.h | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index dd4e57ae..186e2d35 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -118,7 +118,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { };*/ static pa_dbus_interface_info client_interface_info = { - .name = OBJECT_NAME, + .name = PA_DBUSIFACE_CLIENT_INTERFACE, .method_handlers = /*method_handlers*/ NULL, .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, .property_handlers = property_handlers, diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h index ff906256..e8f151cd 100644 --- a/src/modules/dbus/iface-client.h +++ b/src/modules/dbus/iface-client.h @@ -22,16 +22,19 @@ USA. ***/ -/* This object implements the D-Bus interface org.PulseAudio.Core1.Card. +/* This object implements the D-Bus interface org.PulseAudio.Core1.Client. * - * See http://pulseaudio.org/wiki/DBusInterface for the Card interface + * See http://pulseaudio.org/wiki/DBusInterface for the Client interface * documentation. */ #include +#include #include "iface-core.h" +#define PA_DBUSIFACE_CLIENT_INTERFACE PA_DBUS_CORE_INTERFACE ".Client" + typedef struct pa_dbusiface_client pa_dbusiface_client; pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client); -- cgit From a72bba18ea4dd273371179844a0a964e706fd489 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 16 Aug 2009 19:39:39 +0300 Subject: dbusiface-client: Fix indentation. --- src/modules/dbus/iface-client.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index 186e2d35..587d5c42 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -129,16 +129,16 @@ static pa_dbus_interface_info client_interface_info = { }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { - pa_dbusiface_client *c = userdata; - dbus_uint32_t idx = 0; + pa_dbusiface_client *c = userdata; + dbus_uint32_t idx = 0; - pa_assert(conn); - pa_assert(msg); - pa_assert(c); + pa_assert(conn); + pa_assert(msg); + pa_assert(c); - idx = c->client->index; + idx = c->client->index; - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); } static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { -- cgit From f0db081223dd056d6ddbc2a97c557254198a0eaf Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 16 Aug 2009 19:41:43 +0300 Subject: dbusiface-device: Free the copied proplist. --- src/modules/dbus/iface-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 3cf9d19c..15ece83b 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -1274,6 +1274,7 @@ void pa_dbusiface_device_free(pa_dbusiface_device *d) { pa_source_unref(d->source); } pa_hashmap_free(d->ports, port_free_cb, NULL); + pa_proplist_free(d->proplist); pa_dbus_protocol_unref(d->dbus_protocol); pa_subscription_free(d->subscription); -- cgit From 2bb3eef414f80189cf6af6cd66c519630e4c0a43 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 16 Aug 2009 19:42:56 +0300 Subject: dbusiface-stream: Implement about a half of the Stream D-Bus interface. --- src/modules/dbus/iface-stream.c | 383 ++++++++++++++++++++++++++++++++++++++++ src/modules/dbus/iface-stream.h | 3 + 2 files changed, 386 insertions(+) diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index b5a17894..4333c16f 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -2,6 +2,7 @@ This file is part of PulseAudio. Copyright 2009 Tanu Kaskinen + Copyright 2009 Vincent Filali-Ansary PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -24,6 +25,7 @@ #endif #include +#include #include #include "iface-stream.h" @@ -43,8 +45,369 @@ struct pa_dbusiface_stream { }; enum stream_type type; char *path; + pa_cvolume volume; + pa_bool_t is_muted; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +/*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +/*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +/*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, +/* PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_CLIENT, + PROPERTY_HANDLER_DEVICE, + PROPERTY_HANDLER_SAMPLE_FORMAT, + PROPERTY_HANDLER_SAMPLE_RATE, + PROPERTY_HANDLER_CHANNELS,*/ + PROPERTY_HANDLER_VOLUME, + PROPERTY_HANDLER_IS_MUTED, +/* PROPERTY_HANDLER_BUFFER_LATENCY, + PROPERTY_HANDLER_DEVICE_LATENCY, + PROPERTY_HANDLER_RESAMPLE_METHOD,*/ + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, +/* [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_CLIENT] = { .property_name = "Client", .type = "o", .get_cb = handle_get_client, .set_cb = NULL }, + [PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "o", .get_cb = handle_get_device, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },*/ + [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, + [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, +/* [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL },*/ + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +/*enum method_handler_index { + METHOD_HANDLER_MOVE, + METHOD_HANDLER_KILL, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_MOVE] = { + .method_name = "Move", + .arguments = move_args, + .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_move }, + [METHOD_HANDLER_KILL] = { + .method_name = "Kill", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_kill } +};*/ + +enum signal_index { +/* SIGNAL_DEVICE_UPDATED, + SIGNAL_SAMPLE_RATE_UPDATED,*/ + SIGNAL_VOLUME_UPDATED, + SIGNAL_MUTE_UPDATED, + SIGNAL_PROPERTY_LIST_UPDATED, +/* SIGNAL_STREAM_EVENT,*/ + SIGNAL_MAX +}; + +/*static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } }; +static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } };*/ +static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } }; +static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +/*static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } };*/ + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { +/* [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 }, + [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 },*/ + [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 }, + [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*, + [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/ }; +static pa_dbus_interface_info stream_interface_info = { + .name = PA_DBUSIFACE_STREAM_INTERFACE, + .method_handlers = /*method_handlers*/ NULL, + .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t idx; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + return; + } + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels); +} + +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + unsigned stream_channels = 0; + dbus_uint32_t *volume = NULL; + unsigned n_volume_entries = 0; + pa_cvolume new_vol; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + return; + } + + pa_cvolume_init(&new_vol); + + stream_channels = s->sink_input->channel_map.channels; + + new_vol.channels = stream_channels; + + if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) + return; + + if (n_volume_entries != stream_channels) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); + return; + } + + for (i = 0; i < n_volume_entries; ++i) { + if (volume[i] > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]); + return; + } + new_vol.values[i] = volume[i]; + } + + pa_sink_input_set_volume(s->sink_input, &new_vol, TRUE, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); + return; + } + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted); +} + +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_bool_t is_muted = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) + return; + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); + return; + } + + pa_sink_input_set_mute(s->sink_input, is_muted, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +}; + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; + if (s->type == STREAM_TYPE_PLAYBACK) { + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + } + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted); + } + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(c); + pa_assert(s); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + pa_proplist *new_proplist = NULL; + unsigned i = 0; + + pa_assert(((s->type == STREAM_TYPE_PLAYBACK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT)) + || ((s->type == STREAM_TYPE_RECORD) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT))); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_cvolume new_volume; + pa_bool_t new_muted = FALSE; + + pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + + if (!pa_cvolume_equal(&s->volume, &new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; + + s->volume = new_volume; + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, + DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_muted = pa_sink_input_get_mute(s->sink_input); + + if (s->is_muted != new_muted) { + s->is_muted = new_muted; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } + + new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist; + + if (!pa_proplist_equal(s->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, s->proplist); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} + pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { pa_dbusiface_stream *s; @@ -55,6 +418,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p s->sink_input = pa_sink_input_ref(sink_input); s->type = STREAM_TYPE_PLAYBACK; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); + pa_sink_input_get_volume(sink_input, &s->volume, TRUE); + s->is_muted = pa_sink_input_get_mute(sink_input); + s->proplist = pa_proplist_copy(sink_input->proplist); + s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); + s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); return s; } @@ -69,6 +439,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ s->source_output = pa_source_output_ref(source_output); s->type = STREAM_TYPE_RECORD; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index); + pa_cvolume_init(&s->volume); + s->is_muted = FALSE; + s->proplist = pa_proplist_copy(source_output->proplist); + s->dbus_protocol = pa_dbus_protocol_get(source_output->core); + s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); return s; } @@ -76,11 +453,17 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ void pa_dbusiface_stream_free(pa_dbusiface_stream *s) { pa_assert(s); + pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0); + if (s->type == STREAM_TYPE_PLAYBACK) pa_sink_input_unref(s->sink_input); else pa_source_output_unref(s->source_output); + pa_proplist_free(s->proplist); + pa_dbus_protocol_unref(s->dbus_protocol); + pa_subscription_free(s->subscription); + pa_xfree(s->path); pa_xfree(s); } diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h index b1b1854b..036b4e7e 100644 --- a/src/modules/dbus/iface-stream.h +++ b/src/modules/dbus/iface-stream.h @@ -28,11 +28,14 @@ * documentation. */ +#include #include #include #include "iface-core.h" +#define PA_DBUSIFACE_STREAM_INTERFACE PA_DBUS_CORE_INTERFACE ".Stream" + typedef struct pa_dbusiface_stream pa_dbusiface_stream; pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input); -- cgit From f48684e4dbc00d102ed17700fb693726a2676566 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 08:26:06 +0300 Subject: namereg: Revert default device handling back to the upstream version. --- src/pulsecore/namereg.c | 57 ++++++++----------------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index d7d83c5e..e26923d4 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -149,55 +149,21 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0); - if (type == PA_NAMEREG_SINK && !c->default_sink) - pa_namereg_set_default_sink(c, data); - else if (type == PA_NAMEREG_SOURCE && !c->default_source) - pa_namereg_set_default_source(c, data); - return e->name; } void pa_namereg_unregister(pa_core *c, const char *name) { struct namereg_entry *e; - uint32_t idx; pa_assert(c); pa_assert(name); pa_assert_se(e = pa_hashmap_remove(c->namereg, name)); - if (c->default_sink == e->data) { - pa_sink *new_default = NULL; - - /* FIXME: the selection here should be based priority values on - * the sinks */ - - PA_IDXSET_FOREACH(new_default, c->sinks, idx) { - if (new_default != e->data && PA_SINK_IS_LINKED(pa_sink_get_state(new_default))) - break; - } - - pa_namereg_set_default_sink(c, new_default); - - } else if (c->default_source == e->data) { - pa_source *new_default = NULL; - - /* First, try to find one that isn't a monitor */ - PA_IDXSET_FOREACH(new_default, c->sources, idx) { - if (new_default != e->data && !new_default->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default))) - break; - } - - if (!new_default) { - /* Then, fallback to a monitor */ - PA_IDXSET_FOREACH(new_default, c->sources, idx) { - if (new_default != e->data && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default))) - break; - } - } - - pa_namereg_set_default_source(c, new_default); - } + if (c->default_sink == e->data) + pa_namereg_set_default_sink(c, NULL); + else if (c->default_source == e->data) + pa_namereg_set_default_source(c, NULL); pa_xfree(e->name); pa_xfree(e); @@ -225,6 +191,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK))) return s->monitor_source; + } if (!name) @@ -281,18 +248,15 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { return s; } -/* XXX: After removing old functionality, has this function become useless? */ pa_sink *pa_namereg_get_default_sink(pa_core *c) { pa_sink *s; uint32_t idx; pa_assert(c); - if (!c->default_sink || PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) + if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) return c->default_sink; - /* The old default sink has become unlinked, set a new one. */ - /* FIXME: the selection here should be based priority values on * the sinks */ @@ -300,21 +264,18 @@ pa_sink *pa_namereg_get_default_sink(pa_core *c) { if (PA_SINK_IS_LINKED(pa_sink_get_state(s))) return pa_namereg_set_default_sink(c, s); - return pa_namereg_set_default_sink(c, NULL); + return NULL; } -/* XXX: After removing old functionality, has this function become useless? */ pa_source *pa_namereg_get_default_source(pa_core *c) { pa_source *s; uint32_t idx; pa_assert(c); - if (!c->default_source || PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) + if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) return c->default_source; - /* The old default source has become unlinked, set a new one. */ - /* First, try to find one that isn't a monitor */ PA_IDXSET_FOREACH(s, c->sources, idx) if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) @@ -325,5 +286,5 @@ pa_source *pa_namereg_get_default_source(pa_core *c) { if (PA_SOURCE_IS_LINKED(pa_source_get_state(s))) return pa_namereg_set_default_source(c, s); - return pa_namereg_set_default_source(c, NULL); + return NULL; } -- cgit From a10e8360d72626635de1242cfc2c77207f13d56f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:42:06 +0300 Subject: dbusiface-core: New function: pa_dbusiface_core_get_client_path(). --- src/modules/dbus/iface-core.c | 7 +++++++ src/modules/dbus/iface-core.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 2b5cf0b9..ec87158f 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2075,3 +2075,10 @@ const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_mod return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index))); } + +const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client) { + pa_assert(c); + pa_assert(client); + + return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index))); +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 14dff7eb..cf2a3b20 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -41,5 +41,6 @@ const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_sou const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input); const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output); const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); +const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client); #endif -- cgit From efec274b6dcf239f580713f889957c370ac7ffc7 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:42:58 +0300 Subject: dbusiface-core: Two new functions: pa_dbusiface_core_get_sink/source(). --- src/modules/dbus/iface-core.c | 28 ++++++++++++++++++++++++++++ src/modules/dbus/iface-core.h | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index ec87158f..86a8fc7b 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2082,3 +2082,31 @@ const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_cli return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index))); } + +pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path) { + pa_dbusiface_device *device = NULL; + + pa_assert(c); + pa_assert(object_path); + + device = pa_hashmap_get(c->sinks_by_path, object_path); + + if (device) + return pa_dbusiface_device_get_sink(device); + else + return NULL; +} + +pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path) { + pa_dbusiface_device *device = NULL; + + pa_assert(c); + pa_assert(object_path); + + device = pa_hashmap_get(c->sources_by_path, object_path); + + if (device) + return pa_dbusiface_device_get_source(device); + else + return NULL; +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index cf2a3b20..900b6d1c 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -43,4 +43,10 @@ const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client); +/* Returns NULL if there's no sink with the given path. */ +pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path); + +/* Returns NULL if there's no source with the given path. */ +pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path); + #endif -- cgit From 150cd1684a1e67d3a3797b34b45256afb0fa7f53 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:50:29 +0300 Subject: dbusiface-device: Split some overly long lines. --- src/modules/dbus/iface-device.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 15ece83b..486a0946 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -310,9 +310,11 @@ static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void if (!owner_module) { if (d->type == DEVICE_TYPE_SINK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't have an owner module.", d->sink->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sink %s doesn't have an owner module.", d->sink->name); else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't have an owner module.", d->source->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Source %s doesn't have an owner module.", d->source->name); return; } @@ -334,9 +336,11 @@ static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userda if (!card) { if (d->type == DEVICE_TYPE_SINK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't belong to any card.", d->sink->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sink %s doesn't belong to any card.", d->sink->name); else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't belong to any card.", d->source->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Source %s doesn't belong to any card.", d->source->name); return; } @@ -426,7 +430,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user return; if (n_volume_entries != device_channels) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", device_channels, n_volume_entries); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Expected %u volume entries, got %u.", device_channels, n_volume_entries); return; } @@ -598,9 +603,11 @@ static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *use pa_assert(d); if (d->type == DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY)) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't support latency querying.", d->sink->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sink %s doesn't support latency querying.", d->sink->name); else if (d->type == DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY)) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't support latency querying.", d->source->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Source %s doesn't support latency querying.", d->source->name); return; latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); -- cgit From bce6af18a3cc761375acebe589c821fc2abf9d1e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:52:10 +0300 Subject: dbusiface-device: Use a single if-else section instead of ternary operator overuse. --- src/modules/dbus/iface-device.c | 77 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 486a0946..078ae74a 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -817,48 +817,57 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_assert(msg); pa_assert(d); - idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index; - name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name; - driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver; - owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module; + if (d->type == DEVICE_TYPE_SINK) { + idx = d->sink->index; + name = d->sink->name; + driver = d->sink->driver; + owner_module = d->sink->module; + card = d->sink->card; + sample_format = d->sink->sample_spec.format; + sample_rate = d->sink->sample_spec.rate; + channel_map = &d->sink->channel_map; + has_flat_volume = d->sink->flags & PA_SINK_FLAT_VOLUME; + has_convertible_to_decibel_volume = d->sink->flags & PA_SINK_DECIBEL_VOLUME; + base_volume = d->sink->base_volume; + volume_steps = d->sink->n_volume_steps; + has_hardware_volume = d->sink->flags & PA_SINK_HW_VOLUME_CTRL; + has_hardware_mute = d->sink->flags & PA_SINK_HW_MUTE_CTRL; + configured_latency = pa_sink_get_requested_latency(d->sink); + has_dynamic_latency = d->sink->flags & PA_SINK_DYNAMIC_LATENCY; + latency = pa_sink_get_latency(d->sink); + is_hardware_device = d->sink->flags & PA_SINK_HARDWARE; + is_network_device = d->sink->flags & PA_SINK_NETWORK; + state = pa_sink_get_state(d->sink); + } else { + idx = d->source->index; + name = d->source->name; + driver = d->source->driver; + owner_module = d->source->module; + card = d->source->card; + sample_format = d->source->sample_spec.format; + sample_rate = d->source->sample_spec.rate; + channel_map = &d->source->channel_map; + has_flat_volume = FALSE; + has_convertible_to_decibel_volume = d->source->flags & PA_SOURCE_DECIBEL_VOLUME; + base_volume = d->source->base_volume; + volume_steps = d->source->n_volume_steps; + has_hardware_volume = d->source->flags & PA_SOURCE_HW_VOLUME_CTRL; + has_hardware_mute = d->source->flags & PA_SOURCE_HW_MUTE_CTRL; + configured_latency = pa_source_get_requested_latency(d->source); + has_dynamic_latency = d->source->flags & PA_SOURCE_DYNAMIC_LATENCY; + latency = pa_source_get_latency(d->source); + is_hardware_device = d->source->flags & PA_SOURCE_HARDWARE; + is_network_device = d->source->flags & PA_SOURCE_NETWORK; + state = pa_source_get_state(d->source); + } if (owner_module) owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module); - card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card; if (card) card_path = pa_dbusiface_core_get_card_path(d->core, card); - sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format; - sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate; - channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map; for (i = 0; i < channel_map->channels; ++i) channels[i] = channel_map->map[i]; for (i = 0; i < d->volume.channels; ++i) volume[i] = d->volume.values[i]; - has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE; - has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME) - : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME); - base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume; - volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps; - has_hardware_volume = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL) - : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL); - has_hardware_mute = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL) - : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL); - configured_latency = (d->type == DEVICE_TYPE_SINK) - ? pa_sink_get_requested_latency(d->sink) - : pa_source_get_requested_latency(d->source); - has_dynamic_latency = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY) - : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY); - latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); - is_hardware_device = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_HARDWARE) - : (d->source->flags & PA_SOURCE_HARDWARE); - is_network_device = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_NETWORK) - : (d->source->flags & PA_SOURCE_NETWORK); - state = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_state(d->sink) : pa_source_get_state(d->source); ports = get_ports(d, &n_ports); if (d->active_port) active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); -- cgit From b52871517944d55cec549af362398a5012f0b8b8 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:53:58 +0300 Subject: dbusiface-device: Fix argument reading in handle_suspend(). --- src/modules/dbus/iface-device.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 078ae74a..2e5940c3 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -923,13 +923,19 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; dbus_bool_t suspend = FALSE; + DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(d); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &suspend) < 0) + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); return; + } if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed."); -- cgit From 70ff96b8ab100abb4969b882f66518ce739ae655 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:55:08 +0300 Subject: dbusiface-device: Save one level of identation by returning early. --- src/modules/dbus/iface-device.c | 195 ++++++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 96 deletions(-) diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 2e5940c3..8dc0b2c4 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -1075,126 +1075,129 @@ static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void * static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_device *d = userdata; + DBusMessage *signal = NULL; + const pa_cvolume *new_volume = NULL; + pa_bool_t new_muted = FALSE; + pa_sink_state_t new_sink_state = 0; + pa_source_state_t new_source_state = 0; + pa_device_port *new_active_port = NULL; + pa_proplist *new_proplist = NULL; + unsigned i = 0; pa_assert(c); pa_assert(d); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; - const pa_cvolume *new_volume = NULL; - pa_bool_t new_muted = FALSE; - pa_sink_state_t new_sink_state = 0; - pa_source_state_t new_source_state = 0; - pa_device_port *new_active_port = NULL; - pa_proplist *new_proplist = NULL; - unsigned i = 0; - - pa_assert(((d->type == DEVICE_TYPE_SINK) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)) - || ((d->type == DEVICE_TYPE_SOURCE) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); - - new_volume = (d->type == DEVICE_TYPE_SINK) - ? pa_sink_get_volume(d->sink, FALSE, FALSE) - : pa_source_get_volume(d->source, FALSE); - - if (!pa_cvolume_equal(&d->volume, new_volume)) { - dbus_uint32_t volume[PA_CHANNELS_MAX]; - dbus_uint32_t *volume_ptr = volume; - - d->volume = *new_volume; - - for (i = 0; i < d->volume.channels; ++i) - volume[i] = d->volume.values[i]; - - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_VOLUME_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels, - DBUS_TYPE_INVALID)); - - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if ((d->type == DEVICE_TYPE_SINK && idx != d->sink->index) || (d->type == DEVICE_TYPE_SOURCE && idx != d->source->index)) + return; - new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - if (d->is_muted != new_muted) { - d->is_muted = new_muted; + pa_assert(((d->type == DEVICE_TYPE_SINK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)) + || ((d->type == DEVICE_TYPE_SOURCE) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); + new_volume = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_volume(d->sink, FALSE, FALSE) + : pa_source_get_volume(d->source, FALSE); - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (!pa_cvolume_equal(&d->volume, new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; - if (d->type == DEVICE_TYPE_SINK) - new_sink_state = pa_sink_get_state(d->sink); - else - new_source_state = pa_source_get_state(d->source); + d->volume = *new_volume; - if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state) - || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) { - dbus_uint32_t state = 0; + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; - if (d->type == DEVICE_TYPE_SINK) - d->sink_state = new_sink_state; - else - d->source_state = new_source_state; + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels, + DBUS_TYPE_INVALID)); - state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_STATE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)); + new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (d->is_muted != new_muted) { + d->is_muted = new_muted; - new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port; + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); - if (d->active_port != new_active_port) { - const char *object_path = NULL; + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - d->active_port = new_active_port; - object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + if (d->type == DEVICE_TYPE_SINK) + new_sink_state = pa_sink_get_state(d->sink); + else + new_source_state = pa_source_get_state(d->source); - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_ACTIVE_PORT_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state) + || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) { + dbus_uint32_t state = 0; - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (d->type == DEVICE_TYPE_SINK) + d->sink_state = new_sink_state; + else + d->source_state = new_source_state; - new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist; + state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; - if (!pa_proplist_equal(d->proplist, new_proplist)) { - DBusMessageIter msg_iter; + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_STATE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)); - pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist); + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, d->proplist); + new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port; - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (d->active_port != new_active_port) { + const char *object_path = NULL; + + d->active_port = new_active_port; + object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_ACTIVE_PORT_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist; + + if (!pa_proplist_equal(d->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, d->proplist); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } -- cgit From 36dc61a2bff7ee93ca289c33c24b76cb82068cac Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:56:12 +0300 Subject: dbusiface-stream: Finish the Stream D-Bus interface. --- src/modules/dbus/iface-stream.c | 578 ++++++++++++++++++++++++++++++++++------ 1 file changed, 498 insertions(+), 80 deletions(-) diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 4333c16f..354ca6eb 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -39,78 +39,86 @@ enum stream_type { }; struct pa_dbusiface_stream { + pa_dbusiface_core *core; + union { pa_sink_input *sink_input; pa_source_output *source_output; }; enum stream_type type; char *path; + union { + pa_sink *sink; + pa_source *source; + }; + uint32_t sample_rate; pa_cvolume volume; pa_bool_t is_muted; pa_proplist *proplist; pa_dbus_protocol *dbus_protocol; pa_subscription *subscription; + pa_hook_slot *send_event_slot; }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); enum property_handler_index { PROPERTY_HANDLER_INDEX, -/* PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_DRIVER, PROPERTY_HANDLER_OWNER_MODULE, PROPERTY_HANDLER_CLIENT, PROPERTY_HANDLER_DEVICE, PROPERTY_HANDLER_SAMPLE_FORMAT, PROPERTY_HANDLER_SAMPLE_RATE, - PROPERTY_HANDLER_CHANNELS,*/ + PROPERTY_HANDLER_CHANNELS, PROPERTY_HANDLER_VOLUME, PROPERTY_HANDLER_IS_MUTED, -/* PROPERTY_HANDLER_BUFFER_LATENCY, + PROPERTY_HANDLER_BUFFER_LATENCY, PROPERTY_HANDLER_DEVICE_LATENCY, - PROPERTY_HANDLER_RESAMPLE_METHOD,*/ + PROPERTY_HANDLER_RESAMPLE_METHOD, PROPERTY_HANDLER_PROPERTY_LIST, PROPERTY_HANDLER_MAX }; static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, -/* [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, [PROPERTY_HANDLER_CLIENT] = { .property_name = "Client", .type = "o", .get_cb = handle_get_client, .set_cb = NULL }, [PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "o", .get_cb = handle_get_device, .set_cb = NULL }, [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, - [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },*/ + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, -/* [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL }, - [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL },*/ + [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL }, [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } }; -/*enum method_handler_index { +enum method_handler_index { METHOD_HANDLER_MOVE, METHOD_HANDLER_KILL, METHOD_HANDLER_MAX @@ -129,38 +137,38 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .arguments = NULL, .n_arguments = 0, .receive_cb = handle_kill } -};*/ +}; enum signal_index { -/* SIGNAL_DEVICE_UPDATED, - SIGNAL_SAMPLE_RATE_UPDATED,*/ + SIGNAL_DEVICE_UPDATED, + SIGNAL_SAMPLE_RATE_UPDATED, SIGNAL_VOLUME_UPDATED, SIGNAL_MUTE_UPDATED, SIGNAL_PROPERTY_LIST_UPDATED, -/* SIGNAL_STREAM_EVENT,*/ + SIGNAL_STREAM_EVENT, SIGNAL_MAX }; -/*static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } }; -static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } };*/ +static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } }; +static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } }; static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } }; static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } }; static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; -/*static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } };*/ +static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } }; static pa_dbus_signal_info signals[SIGNAL_MAX] = { -/* [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 }, - [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 },*/ + [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 }, + [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 }, [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 }, [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 }, - [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*, - [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/ + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }, + [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) } }; static pa_dbus_interface_info stream_interface_info = { .name = PA_DBUSIFACE_STREAM_INTERFACE, - .method_handlers = /*method_handlers*/ NULL, - .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, .n_property_handlers = PROPERTY_HANDLER_MAX, .get_all_properties_cb = handle_get_all, @@ -181,6 +189,140 @@ static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userd pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); } +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *driver = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver; + + if (!driver) { + if (s->type == STREAM_TYPE_PLAYBACK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Playback stream %u doesn't have a driver.", s->sink_input->index); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Record stream %u doesn't have a driver.", s->source_output->index); + return; + } + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + pa_module *owner_module = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module; + + if (!owner_module) { + if (s->type == STREAM_TYPE_PLAYBACK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Playback stream %u doesn't have an owner module.", s->sink_input->index); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Record stream %u doesn't have an owner module.", s->source_output->index); + return; + } + + object_path = pa_dbusiface_core_get_module_path(s->core, owner_module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + pa_client *client = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client; + + if (!client) { + if (s->type == STREAM_TYPE_PLAYBACK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Playback stream %u isn't associated to any client.", s->sink_input->index); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Record stream %u isn't associated to any client.", s->source_output->index); + return; + } + + object_path = pa_dbusiface_core_get_client_path(s->core, client); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *device = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + device = pa_dbusiface_core_get_sink_path(s->core, s->sink); + else + device = pa_dbusiface_core_get_source_path(s->core, s->source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &device); +} + +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t sample_format = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + sample_format = (s->type == STREAM_TYPE_PLAYBACK) + ? s->sink_input->sample_spec.format + : s->source_output->sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); +} + +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &s->sample_rate); +} + +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + channel_map = (s->type == STREAM_TYPE_PLAYBACK) ? &s->sink_input->channel_map : &s->source_output->channel_map; + + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels); +} + static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; dbus_uint32_t volume[PA_CHANNELS_MAX]; @@ -228,7 +370,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user return; if (n_volume_entries != stream_channels) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); return; } @@ -281,6 +424,54 @@ static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *us pa_dbus_send_empty_reply(conn, msg); }; +static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint64_t buffer_latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + buffer_latency = pa_sink_input_get_latency(s->sink_input, NULL); + else + buffer_latency = pa_source_output_get_latency(s->source_output, NULL); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &buffer_latency); +} + +static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint64_t device_latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + pa_sink_input_get_latency(s->sink_input, &device_latency); + else + pa_source_output_get_latency(s->source_output, &device_latency); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &device_latency); +} + +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *resample_method = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); + else + resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method); +} + static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; @@ -296,19 +487,55 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat DBusMessage *reply = NULL; DBusMessageIter msg_iter; DBusMessageIter dict_iter; - dbus_uint32_t idx; + dbus_uint32_t idx = 0; + const char *driver = NULL; + pa_module *owner_module = NULL; + const char *owner_module_path = NULL; + pa_client *client = NULL; + const char *client_path = NULL; + const char *device = NULL; + dbus_uint32_t sample_format = 0; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint64_t buffer_latency = 0; + dbus_uint64_t device_latency = 0; + const char *resample_method = NULL; unsigned i = 0; pa_assert(conn); pa_assert(msg); pa_assert(s); - idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; if (s->type == STREAM_TYPE_PLAYBACK) { + idx = s->sink_input->index; + driver = s->sink_input->driver; + owner_module = s->sink_input->module; + client = s->sink_input->client; + device = pa_dbusiface_core_get_sink_path(s->core, s->sink); + sample_format = s->sink_input->sample_spec.format; + channel_map = &s->sink_input->channel_map; for (i = 0; i < s->volume.channels; ++i) volume[i] = s->volume.values[i]; + buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency); + resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); + } else { + idx = s->source_output->index; + driver = s->source_output->driver; + owner_module = s->source_output->module; + client = s->source_output->client; + device = pa_dbusiface_core_get_source_path(s->core, s->source); + sample_format = s->source_output->sample_spec.format; + channel_map = &s->source_output->channel_map; + buffer_latency = pa_source_output_get_latency(s->source_output, &device_latency); + resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method); } + if (owner_module) + owner_module_path = pa_dbusiface_core_get_module_path(s->core, owner_module); + if (client) + client_path = pa_dbusiface_core_get_client_path(s->core, client); + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; pa_assert_se((reply = dbus_message_new_method_return(msg))); @@ -317,11 +544,27 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + if (driver) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path); + + if (client) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels); + if (s->type == STREAM_TYPE_PLAYBACK) { pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted); } + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE_LATENCY].property_name, DBUS_TYPE_UINT64, &device_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RESAMPLE_METHOD].property_name, DBUS_TYPE_STRING, &resample_method); pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); @@ -329,83 +572,240 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat dbus_message_unref(reply); } +static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *device = NULL; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device); + + if (!sink) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", device); + return; + } + + if (pa_sink_input_move_to(s->sink_input, sink, TRUE) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Moving playback stream %u to sink %s failed.", s->sink_input->index, sink->name); + return; + } + } else { + pa_source *source = pa_dbusiface_core_get_source(s->core, device); + + if (!source) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", device); + return; + } + + if (pa_source_output_move_to(s->source_output, source, TRUE) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Moving record stream %u to source %s failed.", s->source_output->index, source->name); + return; + } + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + pa_sink_input_kill(s->sink_input); + else + pa_source_output_kill(s->source_output); + + pa_dbus_send_empty_reply(conn, msg); +} + static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_stream *s = userdata; + DBusMessage *signal = NULL; + const char *new_device_path = NULL; + uint32_t new_sample_rate = 0; + pa_proplist *new_proplist = NULL; + unsigned i = 0; pa_assert(c); pa_assert(s); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; - pa_proplist *new_proplist = NULL; - unsigned i = 0; - - pa_assert(((s->type == STREAM_TYPE_PLAYBACK) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT)) - || ((s->type == STREAM_TYPE_RECORD) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT))); + if ((s->type == STREAM_TYPE_PLAYBACK && idx != s->sink_input->index) + || (s->type == STREAM_TYPE_RECORD && idx != s->source_output->index)) + return; - if (s->type == STREAM_TYPE_PLAYBACK) { - pa_cvolume new_volume; - pa_bool_t new_muted = FALSE; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + pa_assert(((s->type == STREAM_TYPE_PLAYBACK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT)) + || ((s->type == STREAM_TYPE_RECORD) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT))); - if (!pa_cvolume_equal(&s->volume, &new_volume)) { - dbus_uint32_t volume[PA_CHANNELS_MAX]; - dbus_uint32_t *volume_ptr = volume; + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_sink *new_sink = s->sink_input->sink; - s->volume = new_volume; + if (s->sink != new_sink) { + pa_sink_unref(s->sink); + s->sink = pa_sink_ref(new_sink); - for (i = 0; i < s->volume.channels; ++i) - volume[i] = s->volume.values[i]; + new_device_path = pa_dbusiface_core_get_sink_path(s->core, new_sink); - pa_assert_se(signal = dbus_message_new_signal(s->path, - PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_VOLUME_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, - DBUS_TYPE_INVALID)); + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_DEVICE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID)); - pa_dbus_protocol_send_signal(s->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } else { + pa_source *new_source = s->source_output->source; - new_muted = pa_sink_input_get_mute(s->sink_input); + if (s->source != new_source) { + pa_source_unref(s->source); + s->source = pa_source_ref(new_source); - if (s->is_muted != new_muted) { - s->is_muted = new_muted; + new_device_path = pa_dbusiface_core_get_source_path(s->core, new_source); - pa_assert_se(signal = dbus_message_new_signal(s->path, - PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_DEVICE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID)); - pa_dbus_protocol_send_signal(s->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } + } + + new_sample_rate = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->sample_spec.rate : s->source_output->sample_spec.rate; + + if (s->sample_rate != new_sample_rate) { + s->sample_rate = new_sample_rate; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_SAMPLE_RATE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_cvolume new_volume; + pa_bool_t new_muted = FALSE; + + pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + + if (!pa_cvolume_equal(&s->volume, &new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; + + s->volume = new_volume; + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, + DBUS_TYPE_INVALID)); - new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist; + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - if (!pa_proplist_equal(s->proplist, new_proplist)) { - DBusMessageIter msg_iter; + new_muted = pa_sink_input_get_mute(s->sink_input); - pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist); + if (s->is_muted != new_muted) { + s->is_muted = new_muted; pa_assert_se(signal = dbus_message_new_signal(s->path, PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, s->proplist); + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(s->dbus_protocol, signal); dbus_message_unref(signal); signal = NULL; } } + + new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist; + + if (!pa_proplist_equal(s->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, s->proplist); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } +} + +static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *slot_data) { + pa_dbusiface_stream *s = slot_data; + DBusMessage *signal = NULL; + DBusMessageIter msg_iter; + const char *name = NULL; + pa_proplist *property_list = NULL; + + pa_assert(call_data); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_sink_input_send_event_hook_data *data = call_data; + + name = data->event; + property_list = data->data; + } else { + pa_source_output_send_event_hook_data *data = call_data; + + name = data->event; + property_list = data->data; + } + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_STREAM_EVENT].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name)); + pa_dbus_append_proplist(&msg_iter, property_list); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + + return PA_HOOK_OK; } pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { @@ -415,14 +815,21 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p pa_assert(sink_input); s = pa_xnew(pa_dbusiface_stream, 1); + s->core = core; s->sink_input = pa_sink_input_ref(sink_input); s->type = STREAM_TYPE_PLAYBACK; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); + s->sink = pa_sink_ref(sink_input->sink); + s->sample_rate = sink_input->sample_spec.rate; pa_sink_input_get_volume(sink_input, &s->volume, TRUE); s->is_muted = pa_sink_input_get_mute(sink_input); s->proplist = pa_proplist_copy(sink_input->proplist); s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); + s->send_event_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], + PA_HOOK_NORMAL, + send_event_cb, + s); pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); @@ -436,14 +843,21 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ pa_assert(source_output); s = pa_xnew(pa_dbusiface_stream, 1); + s->core = core; s->source_output = pa_source_output_ref(source_output); s->type = STREAM_TYPE_RECORD; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index); + s->source = pa_source_ref(source_output->source); + s->sample_rate = source_output->sample_spec.rate; pa_cvolume_init(&s->volume); s->is_muted = FALSE; s->proplist = pa_proplist_copy(source_output->proplist); s->dbus_protocol = pa_dbus_protocol_get(source_output->core); s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); + s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], + PA_HOOK_NORMAL, + send_event_cb, + s); pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); @@ -455,14 +869,18 @@ void pa_dbusiface_stream_free(pa_dbusiface_stream *s) { pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0); - if (s->type == STREAM_TYPE_PLAYBACK) + if (s->type == STREAM_TYPE_PLAYBACK) { pa_sink_input_unref(s->sink_input); - else + pa_sink_unref(s->sink); + } else { pa_source_output_unref(s->source_output); + pa_source_unref(s->source); + } pa_proplist_free(s->proplist); pa_dbus_protocol_unref(s->dbus_protocol); pa_subscription_free(s->subscription); + pa_hook_slot_free(s->send_event_slot); pa_xfree(s->path); pa_xfree(s); -- cgit From 8e6664f499ff3431ea51c99baf366ef11c5301a5 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:09:40 +0300 Subject: dbusiface-core: Split some overly long lines. --- src/modules/dbus/iface-core.c | 160 +++++++++++++++++++++++++++++++----------- 1 file changed, 118 insertions(+), 42 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 86a8fc7b..946fdcc2 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -450,7 +450,8 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, } if (n_channels > PA_CHANNELS_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); return; } @@ -627,7 +628,8 @@ static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi pa_assert(c); if (!c->fallback_sink) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sinks, and therefore no fallback sink either."); return; } @@ -647,7 +649,8 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi pa_assert(c); if (!c->fallback_sink) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sinks, and therefore no fallback sink either."); return; } @@ -713,7 +716,8 @@ static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, v pa_assert(c); if (!c->fallback_source) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sources, and therefore no fallback source either."); return; } @@ -733,7 +737,8 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v pa_assert(c); if (!c->fallback_source) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sources, and therefore no fallback source either."); return; } @@ -1037,9 +1042,14 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat default_sample_rate = c->core->default_sample_spec.rate; cards = get_cards(c, &n_cards); sinks = get_sinks(c, &n_sinks); - fallback_sink = c->fallback_sink ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) : NULL; + fallback_sink = c->fallback_sink + ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) + : NULL; sources = get_sources(c, &n_sources); - fallback_source = c->fallback_source ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))) : NULL; + fallback_source = c->fallback_source + ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, + PA_UINT32_TO_PTR(c->fallback_source->index))) + : NULL; playback_streams = get_playback_streams(c, &n_playback_streams); record_streams = get_record_streams(c, &n_record_streams); samples = get_samples(c, &n_samples); @@ -1299,7 +1309,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u } if (n_channels > PA_CHANNELS_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too many channels: %u. The maximum is %u.", n_channels, PA_CHANNELS_MAX); goto finish; } @@ -1311,7 +1322,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u } if (n_volume_entries != 0 && n_volume_entries != n_channels) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The channels and default_volume arguments have different number of elements."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "The channels and default_volume arguments have different number of elements."); goto finish; } @@ -1337,7 +1349,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u ss.channels = n_channels; if (!pa_frame_aligned(data_length, &ss)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The sample length in bytes doesn't align with the sample format and channels."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "The sample length in bytes doesn't align with the sample format and channels."); goto finish; } @@ -1431,15 +1444,19 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use arg_type = dbus_message_iter_get_arg_type(&msg_iter); if (arg_type != DBUS_TYPE_ARRAY) { if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. A dictionary from strings to strings was expected."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too few arguments. A dictionary from strings to strings was expected."); else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", + (char) arg_type); return; } arg_type = dbus_message_iter_get_element_type(&msg_iter); if (arg_type != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); return; } @@ -1455,7 +1472,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); if (arg_type != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); goto finish; } @@ -1472,7 +1490,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use if (arg_type == DBUS_TYPE_INVALID) pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); goto finish; } @@ -1532,7 +1551,10 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi dbus_error_init(&error); - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) { + if (!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, + DBUS_TYPE_INVALID)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); goto finish; @@ -1591,10 +1613,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_sink != new_fallback_sink) { c->fallback_sink = new_fallback_sink; - if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { + if (new_fallback_sink + && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1605,10 +1630,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_source != new_fallback_source) { c->fallback_source = new_fallback_source; - if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { + if (new_fallback_source + && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1626,7 +1654,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_CARD].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1634,7 +1664,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_card_free(card); @@ -1653,7 +1685,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1665,7 +1699,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * the D-Bus sink object wasn't created yet. Now that the * object is created, let's send the fallback sink change * signal. */ - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1678,7 +1714,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1697,7 +1735,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1709,7 +1749,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * point the D-Bus source object wasn't created yet. Now * that the object is created, let's send the fallback * source change signal. */ - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1722,7 +1764,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1738,7 +1782,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1746,7 +1792,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1762,7 +1810,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_RECORD_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1770,7 +1820,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1786,7 +1838,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SAMPLE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1794,7 +1848,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_sample_free(sample); @@ -1810,7 +1866,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_MODULE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1818,7 +1876,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_module_free(module); @@ -1834,7 +1894,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_CLIENT].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1842,7 +1904,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_client_free(client); @@ -1864,7 +1928,9 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_EXTENSION].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1881,7 +1947,9 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_EXTENSION_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1921,8 +1989,16 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); c->fallback_sink = pa_namereg_get_default_sink(core); c->fallback_source = pa_namereg_get_default_source(core); - c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); - c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); + c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, + PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, + PA_HOOK_NORMAL, + extension_registered_cb, + c); + c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, + PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, + PA_HOOK_NORMAL, + extension_unregistered_cb, + c); c->memstats = pa_dbusiface_memstats_new(c, core); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) -- cgit From 636dbc31f9f7acd76402ea01121f327d21315177 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:10:38 +0300 Subject: dbusiface-core: Use the PA_IDXSET_FOREACH macro. --- src/modules/dbus/iface-core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 946fdcc2..e0aedbec 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2001,34 +2001,34 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c); c->memstats = pa_dbusiface_memstats_new(c, core); - for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) + PA_IDXSET_FOREACH(card, core->cards, idx) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card)); - for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) { + PA_IDXSET_FOREACH(sink, core->sinks, idx) { device = pa_dbusiface_device_new_sink(c, sink); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } - for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) { + PA_IDXSET_FOREACH(source, core->sources, idx) { device = pa_dbusiface_device_new_source(c, source); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } - for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx)) + PA_IDXSET_FOREACH(sink_input, core->sink_inputs, idx) pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input)); - for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx)) + PA_IDXSET_FOREACH(source_output, core->source_outputs, idx) pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output)); - for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx)) + PA_IDXSET_FOREACH(sample, core->scache, idx) pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample)); - for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx)) + PA_IDXSET_FOREACH(module, core->modules, idx) pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module)); - for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx)) + PA_IDXSET_FOREACH(client, core->clients, idx) pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c); -- cgit From 3de210b67120debc680d74e93118a80d360fe1e1 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:13:59 +0300 Subject: dbusiface-core: Assert that _add/remove_interface calls succeed. --- src/modules/dbus/iface-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e0aedbec..a0694d6f 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2031,7 +2031,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { PA_IDXSET_FOREACH(client, core->clients, idx) pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); - pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c); + pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c) >= 0); return c; } @@ -2087,7 +2087,7 @@ static void free_client_cb(void *p, void *userdata) { void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_assert(c); - pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name); + pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name) >= 0); pa_subscription_free(c->subscription); pa_hashmap_free(c->cards, free_card_cb, NULL); -- cgit From b4e0d5d1e17409557d21dae9a770d1429e17cb15 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:18:50 +0300 Subject: dbusiface-sample: Implement the Sample D-Bus interface. --- src/modules/dbus/iface-sample.c | 467 +++++++++++++++++++++++++++++++++++++++- src/modules/dbus/iface-sample.h | 3 + 2 files changed, 468 insertions(+), 2 deletions(-) diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index 44cfb031..4cffd59c 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -24,6 +24,8 @@ #endif #include +#include +#include #include #include "iface-sample.h" @@ -31,19 +33,474 @@ #define OBJECT_NAME "sample" struct pa_dbusiface_sample { + pa_dbusiface_core *core; + pa_scache_entry *sample; char *path; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata); + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_SAMPLE_FORMAT, + PROPERTY_HANDLER_SAMPLE_RATE, + PROPERTY_HANDLER_CHANNELS, + PROPERTY_HANDLER_DEFAULT_VOLUME, + PROPERTY_HANDLER_DURATION, + PROPERTY_HANDLER_BYTES, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, + [PROPERTY_HANDLER_DEFAULT_VOLUME] = { .property_name = "DefaultVolume", .type = "au", .get_cb = handle_get_default_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_DURATION] = { .property_name = "Duration", .type = "t", .get_cb = handle_get_duration, .set_cb = NULL }, + [PROPERTY_HANDLER_BYTES] = { .property_name = "Bytes", .type = "u", .get_cb = handle_get_bytes, .set_cb = NULL }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_PLAY, + METHOD_HANDLER_PLAY_TO_SINK, + METHOD_HANDLER_REMOVE, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info play_args[] = { { "volume", "u", "in" }, { "property_list", "a{say}", "in" } }; +static pa_dbus_arg_info play_to_sink_args[] = { { "sink", "o", "in" }, + { "volume", "u", "in" }, + { "property_list", "a{say}", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_PLAY] = { + .method_name = "Play", + .arguments = play_args, + .n_arguments = sizeof(play_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_play }, + [METHOD_HANDLER_PLAY_TO_SINK] = { + .method_name = "PlayToSink", + .arguments = play_to_sink_args, + .n_arguments = sizeof(play_to_sink_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_play_to_sink }, + [METHOD_HANDLER_REMOVE] = { + .method_name = "Remove", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_remove } +}; + +enum signal_index { + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } }; +static pa_dbus_interface_info sample_interface_info = { + .name = PA_DBUSIFACE_SAMPLE_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = s->sample->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &s->sample->name); +} + +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t sample_format = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its sample format is unknown.", s->sample->name); + return; + } + + sample_format = s->sample->sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); +} + +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t sample_rate = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its sample rate is unknown.", s->sample->name); + return; + } + + sample_rate = s->sample->sample_spec.rate; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate); +} + +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its channel map is unknown.", s->sample->name); + return; + } + + for (i = 0; i < s->sample->channel_map.channels; ++i) + channels[i] = s->sample->channel_map.map[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels); +} + +static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t default_volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->volume_is_set) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s doesn't have default volume stored.", s->sample->name); + return; + } + + for (i = 0; i < s->sample->volume.channels; ++i) + default_volume[i] = s->sample->volume.values[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels); +} + +static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint64_t duration = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its duration is unknown.", s->sample->name); + return; + } + + duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &duration); +} + +static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t bytes = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its size is unknown.", s->sample->name); + return; + } + + bytes = s->sample->memchunk.length; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &bytes); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx = 0; + dbus_uint32_t sample_format = 0; + dbus_uint32_t sample_rate = 0; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + dbus_uint32_t default_volume[PA_CHANNELS_MAX]; + dbus_uint64_t duration = 0; + dbus_uint32_t bytes = 0; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = s->sample->index; + if (s->sample->memchunk.memblock) { + sample_format = s->sample->sample_spec.format; + sample_rate = s->sample->sample_spec.rate; + for (i = 0; i < s->sample->channel_map.channels; ++i) + channels[i] = s->sample->channel_map.map[i]; + duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec); + bytes = s->sample->memchunk.length; + } + if (s->sample->volume_is_set) { + for (i = 0; i < s->sample->volume.channels; ++i) + default_volume[i] = s->sample->volume.values[i]; + } + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &s->sample->name); + + if (s->sample->memchunk.memblock) { + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels); + } + + if (s->sample->volume_is_set) + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_VOLUME].property_name, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels); + + if (s->sample->memchunk.memblock) { + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DURATION].property_name, DBUS_TYPE_UINT64, &duration); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BYTES].property_name, DBUS_TYPE_UINT32, &bytes); + } + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessageIter msg_iter; + dbus_uint32_t volume = 0; + pa_proplist *property_list = NULL; + pa_sink *sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) + return; + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (volume > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + goto finish; + } + + if (!(sink = pa_namereg_get_default_sink(s->sample->core))) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Can't play sample %s, because there are no sinks available.", s->sample->name); + goto finish; + } + + if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name); + goto finish; + } + + pa_dbus_send_empty_reply(conn, msg); + +finish: + if (property_list) + pa_proplist_free(property_list); +} + +static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessageIter msg_iter; + const char *sink_path = NULL; + dbus_uint32_t volume = 0; + pa_proplist *property_list = NULL; + pa_sink *sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_OBJECT_PATH, &sink_path) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) + return; + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (!(sink = pa_dbusiface_core_get_sink(s->core, sink_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_path); + goto finish; + } + + if (volume > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + goto finish; + } + + if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name); + goto finish; + } + + pa_dbus_send_empty_reply(conn, msg); + +finish: + if (property_list) + pa_proplist_free(property_list); +} + +static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (pa_scache_remove_item(s->sample->core, s->sample->name) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Removing sample %s failed.", s->sample->name); + return; + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessage *signal = NULL; + + pa_assert(c); + pa_assert(s); + + if (idx != s->sample->index) + return; + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; + + if (!pa_proplist_equal(s->proplist, s->sample->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(s->proplist, PA_UPDATE_SET, s->sample->proplist); + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_SAMPLE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, s->proplist); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } +} + pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) { - pa_dbusiface_sample *s; + pa_dbusiface_sample *s = NULL; pa_assert(core); pa_assert(sample); - s = pa_xnew(pa_dbusiface_sample, 1); + s = pa_xnew0(pa_dbusiface_sample, 1); + s->core = core; s->sample = sample; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index); + s->proplist = pa_proplist_copy(sample->proplist); + s->dbus_protocol = pa_dbus_protocol_get(sample->core); + s->subscription = pa_subscription_new(sample->core, PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &sample_interface_info, s) >= 0); return s; } @@ -51,6 +508,12 @@ pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_ void pa_dbusiface_sample_free(pa_dbusiface_sample *s) { pa_assert(s); + pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, sample_interface_info.name) >= 0); + + pa_proplist_free(s->proplist); + pa_dbus_protocol_unref(s->dbus_protocol); + pa_subscription_free(s->subscription); + pa_xfree(s->path); pa_xfree(s); } diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h index 1b82648d..f1947ce8 100644 --- a/src/modules/dbus/iface-sample.h +++ b/src/modules/dbus/iface-sample.h @@ -29,9 +29,12 @@ */ #include +#include #include "iface-core.h" +#define PA_DBUSIFACE_SAMPLE_INTERFACE PA_DBUS_CORE_INTERFACE ".Sample" + typedef struct pa_dbusiface_sample pa_dbusiface_sample; pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample); -- cgit From 179f849c0869982c6fc97d6bbf9083203c586b17 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:54:09 +0300 Subject: dbusifaca-device: Adapt to the changed pa_sink_get/set_volume() interface. --- src/modules/dbus/iface-device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 8dc0b2c4..8f719bce 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -444,7 +444,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user } if (d->type == DEVICE_TYPE_SINK) - pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE, TRUE, TRUE); + pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE); else pa_source_set_volume(d->source, &new_vol, TRUE); @@ -1099,7 +1099,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); new_volume = (d->type == DEVICE_TYPE_SINK) - ? pa_sink_get_volume(d->sink, FALSE, FALSE) + ? pa_sink_get_volume(d->sink, FALSE) : pa_source_get_volume(d->source, FALSE); if (!pa_cvolume_equal(&d->volume, new_volume)) { @@ -1212,7 +1212,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_si d->sink = pa_sink_ref(sink); d->type = DEVICE_TYPE_SINK; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); - d->volume = *pa_sink_get_volume(sink, FALSE, FALSE); + d->volume = *pa_sink_get_volume(sink, FALSE); d->is_muted = pa_sink_get_mute(sink, FALSE); d->sink_state = pa_sink_get_state(sink); d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -- cgit From 1e4e26c87fc4f74c9805cc084c88783558acb418 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:22:32 +0300 Subject: proplist: Return early from pa_proplist_equal() if the pointers are equal. --- src/pulse/proplist.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 4f0d6a6d..8b5b6538 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -690,6 +690,9 @@ int pa_proplist_equal(pa_proplist *a, pa_proplist *b) { pa_assert(a); pa_assert(b); + if (a == b) + return 1; + if (pa_proplist_size(a) != pa_proplist_size(b)) return 0; -- cgit From 187c4f32cffb75787213e8692b27d0a3c736b95e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:23:49 +0300 Subject: proplist: A couple of documentation fixes. --- src/pulse/proplist.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index a585944a..9effc861 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -337,7 +337,7 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep); * readable string. \since 0.9.15 */ pa_proplist *pa_proplist_from_string(const char *str); - /** Returns 1 if an entry for the specified key is existant in the +/** Returns 1 if an entry for the specified key is existant in the * property list. \since 0.9.11 */ int pa_proplist_contains(pa_proplist *p, const char *key); @@ -354,7 +354,8 @@ unsigned pa_proplist_size(pa_proplist *t); /** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */ int pa_proplist_isempty(pa_proplist *t); -/** Return non-zero when a and b have the same keys and values. */ +/** Return non-zero when a and b have the same keys and values. + * \since 0.9.16 */ int pa_proplist_equal(pa_proplist *a, pa_proplist *b); PA_C_DECL_END -- cgit From 7049b3c5bc351b9ea7ed932baa5bf7ccc1b67347 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:24:59 +0300 Subject: modargs: New function: pa_modargs_iterate(). --- src/pulsecore/modargs.c | 10 ++++++++++ src/pulsecore/modargs.h | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index c7d734d9..e78cdb9a 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -415,3 +415,13 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa return 0; } + +const char *pa_modargs_iterate(pa_modargs *ma, void **state) { + pa_hashmap *map = (pa_hashmap*) ma; + struct entry *e; + + if (!(e = pa_hashmap_iterate(map, state, NULL))) + return NULL; + + return e->key; +} diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h index b3125b10..1ed66e9a 100644 --- a/src/pulsecore/modargs.h +++ b/src/pulsecore/modargs.h @@ -60,4 +60,13 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *s int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m); +/* Iterate through the module argument list. The user should allocate a + * state variable of type void* and initialize it with NULL. A pointer + * to this variable should then be passed to pa_modargs_iterate() + * which should be called in a loop until it returns NULL which + * signifies EOL. On each invication this function will return the + * key string for the next entry. The keys in the argument list do not + * have any particular order. */ +const char *pa_modargs_iterate(pa_modargs *ma, void **state); + #endif -- cgit From 57886ff34ac39dae709cb50cbc7fbbf817df534b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:26:13 +0300 Subject: dbus-protocol: Print a debug line whenever interfaces are unregistered. --- src/pulsecore/protocol-dbus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index cf561c83..d1e19ff3 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -694,6 +694,8 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con update_introspection(obj_entry); + pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path); + pa_xfree(iface_entry->name); pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); -- cgit From 3025645b0b58f476f6404f2aed3b61a633794d10 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:27:14 +0300 Subject: dbusiface-module: Implement the Module D-Bus interface. --- src/modules/dbus/iface-core.c | 6 +- src/modules/dbus/iface-module.c | 275 +++++++++++++++++++++++++++++++++++++++- src/modules/dbus/iface-module.h | 5 +- 3 files changed, 279 insertions(+), 7 deletions(-) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index a0694d6f..9e8f775e 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1509,7 +1509,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use goto finish; } - dbus_module = pa_dbusiface_module_new(c, module); + dbus_module = pa_dbusiface_module_new(module); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module); object_path = pa_dbusiface_module_get_path(dbus_module); @@ -1860,7 +1860,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { - module = pa_dbusiface_module_new(c, pa_idxset_get_by_index(core->modules, idx)); + module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx)); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); } @@ -2026,7 +2026,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample)); PA_IDXSET_FOREACH(module, core->modules, idx) - pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module)); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module)); PA_IDXSET_FOREACH(client, core->clients, idx) pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index 788d104b..63786dae 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -24,6 +24,8 @@ #endif #include +#include +#include #include #include "iface-module.h" @@ -33,17 +35,278 @@ struct pa_dbusiface_module { pa_module *module; char *path; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; -pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module) { - pa_dbusiface_module *m; +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata); + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_ARGUMENTS, + PROPERTY_HANDLER_USAGE_COUNTER, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_ARGUMENTS] = { .property_name = "Arguments", .type = "a{ss}", .get_cb = handle_get_arguments, .set_cb = NULL }, + [PROPERTY_HANDLER_USAGE_COUNTER] = { .property_name = "UsageCounter", .type = "u", .get_cb = handle_get_usage_counter, .set_cb = NULL }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_UNLOAD, + METHOD_HANDLER_MAX +}; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_UNLOAD] = { + .method_name = "Unload", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_unload } +}; + +enum signal_index { + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info module_interface_info = { + .name = PA_DBUSIFACE_MODULE_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + idx = m->module->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &m->module->name); +} + +static void append_modargs_variant(DBusMessageIter *iter, pa_dbusiface_module *m) { + pa_modargs *ma = NULL; + DBusMessageIter variant_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + void *state = NULL; + const char *key = NULL; + const char *value = NULL; + + pa_assert(iter); + pa_assert(m); + + pa_assert_se(ma = pa_modargs_new(m->module->argument, NULL)); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{ss}", &variant_iter)); + pa_assert_se(dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "{ss}", &dict_iter)); + + for (state = NULL, key = pa_modargs_iterate(ma, &state); key; key = pa_modargs_iterate(ma, &state)) { + pa_assert_se(value = pa_modargs_get_value(ma, key, NULL)); + + pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &value)); + + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + } + + pa_assert_se(dbus_message_iter_close_container(&variant_iter, &dict_iter)); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); + + pa_modargs_free(ma); +} + +static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_assert_se(reply = dbus_message_new_method_return(msg)); + dbus_message_iter_init_append(reply, &msg_iter); + append_modargs_variant(&msg_iter, m); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + int real_counter_value = -1; + dbus_uint32_t usage_counter = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + if (!m->module->get_n_used || (real_counter_value = m->module->get_n_used(m->module)) < 0) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Module %u (%s) doesn't have a usage counter.", m->module->index, m->module->name); + return; + } + + usage_counter = real_counter_value; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &usage_counter); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_dbus_send_proplist_variant_reply(conn, msg, m->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + dbus_uint32_t idx = 0; + int real_counter_value = -1; + dbus_uint32_t usage_counter = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + idx = m->module->index; + if (m->module->get_n_used && (real_counter_value = m->module->get_n_used(m->module)) >= 0) + usage_counter = real_counter_value; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &m->module->name); + + 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, &property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name)); + append_modargs_variant(&dict_entry_iter, m); + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + + if (real_counter_value >= 0) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name, DBUS_TYPE_UINT32, &usage_counter); + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, m->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + if (m->module->core->disallow_module_loading) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module unloading."); + return; + } + + pa_module_unload_request(m->module, FALSE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_module *m = userdata; pa_assert(core); + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE); + pa_assert(m); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + + if (!pa_proplist_equal(m->proplist, m->module->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist); + + pa_assert_se(signal = dbus_message_new_signal(m->path, + PA_DBUSIFACE_MODULE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, m->proplist); + + pa_dbus_protocol_send_signal(m->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} + +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module) { + pa_dbusiface_module *m; + pa_assert(module); - m = pa_xnew(pa_dbusiface_module, 1); + m = pa_xnew0(pa_dbusiface_module, 1); m->module = module; m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index); + m->proplist = pa_proplist_copy(module->proplist); + m->dbus_protocol = pa_dbus_protocol_get(module->core); + m->subscription = pa_subscription_new(module->core, PA_SUBSCRIPTION_MASK_MODULE, subscription_cb, m); + + pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &module_interface_info, m) >= 0); return m; } @@ -51,6 +314,12 @@ pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module void pa_dbusiface_module_free(pa_dbusiface_module *m) { pa_assert(m); + pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, module_interface_info.name) >= 0); + + pa_proplist_free(m->proplist); + pa_dbus_protocol_unref(m->dbus_protocol); + pa_subscription_free(m->subscription); + pa_xfree(m->path); pa_xfree(m); } diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h index c4f29210..68ca1de5 100644 --- a/src/modules/dbus/iface-module.h +++ b/src/modules/dbus/iface-module.h @@ -29,12 +29,15 @@ */ #include +#include #include "iface-core.h" +#define PA_DBUSIFACE_MODULE_INTERFACE PA_DBUS_CORE_INTERFACE ".Module" + typedef struct pa_dbusiface_module pa_dbusiface_module; -pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module); +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module); void pa_dbusiface_module_free(pa_dbusiface_module *m); const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m); -- cgit From 3e0e685a8c6372fb67efad5ffa54583dc757203e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:12:42 +0300 Subject: dbus: Save one level of identation by returning early. --- src/modules/dbus/iface-card.c | 53 +++++++++++++++++++++-------------------- src/modules/dbus/iface-module.c | 28 +++++++++++----------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 5a797948..fce44ff7 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -459,45 +459,46 @@ static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, v static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_card *c = userdata; + DBusMessage *signal = NULL; pa_assert(core); pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD); pa_assert(c); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; - if (c->active_profile != c->card->active_profile) { - const char *object_path; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - c->active_profile = c->card->active_profile; - object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + if (c->active_profile != c->card->active_profile) { + const char *object_path; - pa_assert_se(signal = dbus_message_new_signal(c->path, - PA_DBUSIFACE_CARD_INTERFACE, - signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + c->active_profile = c->card->active_profile; + object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); - pa_dbus_protocol_send_signal(c->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - if (!pa_proplist_equal(c->proplist, c->card->proplist)) { - DBusMessageIter msg_iter; + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); + if (!pa_proplist_equal(c->proplist, c->card->proplist)) { + DBusMessageIter msg_iter; - pa_assert_se(signal = dbus_message_new_signal(c->path, - PA_DBUSIFACE_CARD_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, c->proplist); + pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); - pa_dbus_protocol_send_signal(c->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, c->proplist); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index 63786dae..e3882fc9 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -268,29 +268,29 @@ static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_module *m = userdata; + DBusMessage *signal = NULL; pa_assert(core); pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE); pa_assert(m); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - if (!pa_proplist_equal(m->proplist, m->module->proplist)) { - DBusMessageIter msg_iter; + if (!pa_proplist_equal(m->proplist, m->module->proplist)) { + DBusMessageIter msg_iter; - pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist); + pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist); - pa_assert_se(signal = dbus_message_new_signal(m->path, - PA_DBUSIFACE_MODULE_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, m->proplist); + pa_assert_se(signal = dbus_message_new_signal(m->path, + PA_DBUSIFACE_MODULE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, m->proplist); - pa_dbus_protocol_send_signal(m->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_dbus_protocol_send_signal(m->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } -- cgit From edf80104e32830a3d4ec58f88f9a092c32203d8a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:17:35 +0300 Subject: dbus: Make sure that subscription callbacks don't try to access removed objects. --- src/modules/dbus/iface-card.c | 4 + src/modules/dbus/iface-core.c | 202 ++++++++++++++++++++++++++-------------- src/modules/dbus/iface-module.c | 5 + src/modules/dbus/iface-sample.c | 4 +- 4 files changed, 143 insertions(+), 72 deletions(-) diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index fce44ff7..7e37f8bb 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -465,6 +465,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD); pa_assert(c); + /* We can't use idx != c->card->index, because the c->card pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(core->cards, idx) != c->card) + return; if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) return; diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 9e8f775e..c54a3b7a 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1592,12 +1592,12 @@ static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage * static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_core *c = userdata; - pa_dbusiface_card *card = NULL; - pa_dbusiface_device *device = NULL; - pa_dbusiface_stream *stream = NULL; - pa_dbusiface_sample *sample = NULL; - pa_dbusiface_module *module = NULL; - pa_dbusiface_client *client = NULL; + pa_dbusiface_card *card_iface = NULL; + pa_dbusiface_device *device_iface = NULL; + pa_dbusiface_stream *stream_iface = NULL; + pa_dbusiface_sample *sample_iface = NULL; + pa_dbusiface_module *module_iface = NULL; + pa_dbusiface_client *client_iface = NULL; DBusMessage *signal = NULL; const char *object_path = NULL; pa_sink *new_fallback_sink = NULL; @@ -1611,11 +1611,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 new_fallback_source = pa_namereg_get_default_source(core); if (c->fallback_sink != new_fallback_sink) { - c->fallback_sink = new_fallback_sink; + if (c->fallback_sink) + pa_sink_unref(c->fallback_sink); + c->fallback_sink = new_fallback_sink ? pa_sink_ref(new_fallback_sink) : NULL; if (new_fallback_sink - && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { - object_path = pa_dbusiface_device_get_path(device); + && (device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1628,11 +1630,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } if (c->fallback_source != new_fallback_source) { - c->fallback_source = new_fallback_source; + if (c->fallback_source) + pa_source_unref(c->fallback_source); + c->fallback_source = new_fallback_source ? pa_source_ref(new_fallback_source) : NULL; if (new_fallback_source - && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { - object_path = pa_dbusiface_device_get_path(device); + && (device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1647,12 +1651,17 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { - card = pa_dbusiface_card_new(c, pa_idxset_get_by_index(core->cards, idx)); - pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + if (!(card_iface = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { + pa_card *card = NULL; + + if (!(card = pa_idxset_get_by_index(core->cards, idx))) + return; /* The card was removed immediately after creation. */ + + card_iface = pa_dbusiface_card_new(c, card); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card_iface); } - object_path = pa_dbusiface_card_get_path(card); + object_path = pa_dbusiface_card_get_path(card_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1660,30 +1669,34 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((card = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx)))); + if (!(card_iface = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_card_get_path(card); + object_path = pa_dbusiface_card_get_path(card_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_card_free(card); + pa_dbusiface_card_free(card_iface); } break; case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx); + pa_sink *sink = NULL; - if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_sink(c, sink); - pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); + if (!(sink = pa_idxset_get_by_index(core->sinks, idx))) + return; /* The sink was removed immediately after creation. */ + + if (!(device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { + device_iface = pa_dbusiface_device_new_sink(c, sink); + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device_iface); + pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device_iface), device_iface); } - object_path = pa_dbusiface_device_get_path(device); + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1710,8 +1723,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))); - object_path = pa_dbusiface_device_get_path(device); + if (!(device_iface = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) + return; + + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, @@ -1719,7 +1734,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_device_free(device); + pa_dbusiface_device_free(device_iface); } break; @@ -1727,13 +1742,16 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { pa_source *source = pa_idxset_get_by_index(core->sources, idx); - if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_source(c, source); - pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); + if (!(source = pa_idxset_get_by_index(core->sources, idx))) + return; /* The source was removed immediately after creation. */ + + if (!(device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { + device_iface = pa_dbusiface_device_new_source(c, source); + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device_iface); + pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device_iface), device_iface); } - object_path = pa_dbusiface_device_get_path(device); + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1760,8 +1778,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))); - object_path = pa_dbusiface_device_get_path(device); + if (!(device_iface = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) + return; + + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, @@ -1769,18 +1789,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_device_free(device); + pa_dbusiface_device_free(device_iface); } break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_playback(c, pa_idxset_get_by_index(core->sink_inputs, idx)); - pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + pa_sink_input *sink_input = NULL; + + if (!(sink_input = pa_idxset_get_by_index(core->sink_inputs, idx))) + return; /* The sink input was removed immediately after creation. */ + + if (!(stream_iface = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { + stream_iface = pa_dbusiface_stream_new_playback(c, sink_input); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream_iface); } - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1788,27 +1813,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((stream = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx)))); + if (!(stream_iface = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_stream_free(stream); + pa_dbusiface_stream_free(stream_iface); } break; case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_record(c, pa_idxset_get_by_index(core->source_outputs, idx)); - pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + pa_source_output *source_output = NULL; + + if (!(source_output = pa_idxset_get_by_index(core->source_outputs, idx))) + return; /* The source output was removed immediately after creation. */ + + if (!(stream_iface = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { + stream_iface = pa_dbusiface_stream_new_record(c, source_output); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream_iface); } - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1816,27 +1847,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((stream = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx)))); + if (!(stream_iface = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_stream_free(stream); + pa_dbusiface_stream_free(stream_iface); } break; case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { - sample = pa_dbusiface_sample_new(c, pa_idxset_get_by_index(core->scache, idx)); - pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); + pa_scache_entry *sample = NULL; + + if (!(sample = pa_idxset_get_by_index(core->scache, idx))) + return; /* The sample was removed immediately after creation. */ + + if (!(sample_iface = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { + sample_iface = pa_dbusiface_sample_new(c, sample); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample_iface); } - object_path = pa_dbusiface_sample_get_path(sample); + object_path = pa_dbusiface_sample_get_path(sample_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1844,27 +1881,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((sample = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx)))); + if (!(sample_iface = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_sample_get_path(sample); + object_path = pa_dbusiface_sample_get_path(sample_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_sample_free(sample); + pa_dbusiface_sample_free(sample_iface); } break; case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { - module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx)); - pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); + pa_module *module = NULL; + + if (!(module = pa_idxset_get_by_index(core->modules, idx))) + return; /* The module was removed immediately after creation. */ + + if (!(module_iface = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { + module_iface = pa_dbusiface_module_new(module); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module_iface); } - object_path = pa_dbusiface_module_get_path(module); + object_path = pa_dbusiface_module_get_path(module_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1872,27 +1915,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((module = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx)))); + if (!(module_iface = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_module_get_path(module); + object_path = pa_dbusiface_module_get_path(module_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_module_free(module); + pa_dbusiface_module_free(module_iface); } break; case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { - client = pa_dbusiface_client_new(c, pa_idxset_get_by_index(core->clients, idx)); - pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + pa_client *client = NULL; + + if (!(client = pa_idxset_get_by_index(core->clients, idx))) + return; /* The client was removed immediately after creation. */ + + if (!(client_iface = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { + client_iface = pa_dbusiface_client_new(c, client); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client_iface); } - object_path = pa_dbusiface_client_get_path(client); + object_path = pa_dbusiface_client_get_path(client_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1900,16 +1949,17 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((client = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx)))); + if (!(client_iface = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_client_get_path(client); + object_path = pa_dbusiface_client_get_path(client_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_client_free(client); + pa_dbusiface_client_free(client_iface); } break; } @@ -2001,6 +2051,11 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c); c->memstats = pa_dbusiface_memstats_new(c, core); + if (c->fallback_sink) + pa_sink_ref(c->fallback_sink); + if (c->fallback_source) + pa_source_ref(c->fallback_source); + PA_IDXSET_FOREACH(card, core->cards, idx) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card)); @@ -2104,6 +2159,11 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hook_slot_free(c->extension_unregistered_slot); pa_dbusiface_memstats_free(c->memstats); + if (c->fallback_sink) + pa_sink_unref(c->fallback_sink); + if (c->fallback_source) + pa_source_unref(c->fallback_source); + pa_dbus_protocol_unref(c->dbus_protocol); pa_core_unref(c->core); diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index e3882fc9..e8aea50f 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -274,6 +274,11 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE); pa_assert(m); + /* We can't use idx != m->module->index, because the m->module pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(core->modules, idx) != m->module) + return; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) return; diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index 4cffd59c..7147be14 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -463,7 +463,9 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t pa_assert(c); pa_assert(s); - if (idx != s->sample->index) + /* We can't use idx != s->sample->index, because the s->sample pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(c->scache, idx) != s->sample) return; if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) -- cgit From 11fcc8c85f15dba8e78dffb88b3d0d04ebc329e1 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:19:11 +0300 Subject: dbusiface-stream: Only send stream event signals from the right D-Bus objects. --- src/modules/dbus/iface-stream.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 354ca6eb..183625ba 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -786,11 +786,17 @@ static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *sl if (s->type == STREAM_TYPE_PLAYBACK) { pa_sink_input_send_event_hook_data *data = call_data; + if (data->sink_input != s->sink_input) + return PA_HOOK_OK; + name = data->event; property_list = data->data; } else { pa_source_output_send_event_hook_data *data = call_data; + if (data->source_output != s->source_output) + return PA_HOOK_OK; + name = data->event; property_list = data->data; } -- cgit From 219f7508f6420f94ad8c426c6aa3dc79df246f36 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:20:26 +0300 Subject: dbus: Finish the Client D-Bus interface. --- src/modules/dbus/iface-client.c | 163 ++++++++++++++++++++++++++++---- src/modules/dbus/module-dbus-protocol.c | 31 +++++- 2 files changed, 176 insertions(+), 18 deletions(-) diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index 587d5c42..a68259a9 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -39,8 +39,10 @@ struct pa_dbusiface_client { pa_client *client; char *path; + pa_proplist *proplist; pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -52,9 +54,9 @@ static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, voi static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); enum property_handler_index { PROPERTY_HANDLER_INDEX, @@ -75,7 +77,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } }; -/*enum method_handler_index { +enum method_handler_index { METHOD_HANDLER_KILL, METHOD_HANDLER_UPDATE_PROPERTIES, METHOD_HANDLER_REMOVE_PROPERTIES, @@ -83,7 +85,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { }; static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } }; -static pa_dbus_arg_info update_properties_args[] = { { "keys", "as", "in" } }; +static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } }; static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { [METHOD_HANDLER_KILL] = { @@ -93,14 +95,14 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .receive_cb = handle_kill }, [METHOD_HANDLER_UPDATE_PROPERTIES] = { .method_name = "UpdateProperties", - .arguments = update_propertes_args, + .arguments = update_properties_args, .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_update_properties }, [METHOD_HANDLER_REMOVE_PROPERTIES] = { .method_name = "RemoveProperties", - .arguments = remove_propertes_args, + .arguments = remove_properties_args, .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), - .receive_cb = handle_update_properties } + .receive_cb = handle_remove_properties } }; enum signal_index { @@ -109,23 +111,25 @@ enum signal_index { SIGNAL_MAX }; -static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } }; -static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +static pa_dbus_arg_info client_event_args[] = { { "name", "s", NULL }, + { "property_list", "a{say}", NULL } }; static pa_dbus_signal_info signals[SIGNAL_MAX] = { - [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 }, - [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } -};*/ + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }, + /* ClientEvent is sent from module-dbus-protocol.c. */ + [SIGNAL_CLIENT_EVENT] = { .name = "ClientEvent", .arguments = client_event_args, .n_arguments = 1 } +}; static pa_dbus_interface_info client_interface_info = { .name = PA_DBUSIFACE_CLIENT_INTERFACE, - .method_handlers = /*method_handlers*/ NULL, - .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, .n_property_handlers = PROPERTY_HANDLER_MAX, .get_all_properties_cb = handle_get_all, - .signals = /*signals*/ NULL, - .n_signals = /*SIGNAL_MAX*/ 0 + .signals = signals, + .n_signals = SIGNAL_MAX }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { @@ -304,6 +308,131 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_xfree(record_streams); } +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_connection_ref(conn); + + pa_client_kill(c->client); + + pa_dbus_send_empty_reply(conn, msg); + + dbus_connection_unref(conn); +} + +static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessageIter msg_iter; + pa_proplist *property_list = NULL; + dbus_uint32_t update_mode = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); + return; + } + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &update_mode) < 0) + goto finish; + + if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode); + goto finish; + } + + pa_client_update_proplist(c->client, update_mode, property_list); + + pa_dbus_send_empty_reply(conn, msg); + +finish: + if (property_list) + pa_proplist_free(property_list); +} + +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + char **keys = NULL; + int n_keys = 0; + DBusError error; + pa_bool_t changed = FALSE; + int i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); + return; + } + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + for (i = 0; i < n_keys; ++i) + changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0; + + pa_dbus_send_empty_reply(conn, msg); + + if (changed) + pa_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); + + dbus_free_string_array(keys); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessage *signal = NULL; + + pa_assert(core); + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CLIENT); + pa_assert(c); + + /* We can't use idx != c->client->index, because the c->client pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(core->clients, idx) != c->client) + return; + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; + + if (!pa_proplist_equal(c->proplist, c->client->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(c->proplist, PA_UPDATE_SET, c->client->proplist); + + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CLIENT_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, c->proplist); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } +} + pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { pa_dbusiface_client *c = NULL; @@ -314,7 +443,9 @@ pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client c->core = core; c->client = client; c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); + c->proplist = pa_proplist_copy(client->proplist); c->dbus_protocol = pa_dbus_protocol_get(client->core); + c->subscription = pa_subscription_new(client->core, PA_SUBSCRIPTION_MASK_CLIENT, subscription_cb, c); pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0); diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c index 807d32da..11064c33 100644 --- a/src/modules/dbus/module-dbus-protocol.c +++ b/src/modules/dbus/module-dbus-protocol.c @@ -40,6 +40,7 @@ #include #include +#include "iface-client.h" #include "iface-core.h" #include "module-dbus-protocol-symdef.h" @@ -117,10 +118,36 @@ static void client_kill_cb(pa_client *c) { conn = c->userdata; connection_free(conn); + c->userdata = NULL; pa_log_info("Connection killed."); } +/* Called from pa_client_send_event(). */ +static void client_send_event_cb(pa_client *c, const char *name, pa_proplist *data) { + struct connection *conn = NULL; + DBusMessage *signal = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(name); + pa_assert(data); + pa_assert(c->userdata); + + conn = c->userdata; + + pa_assert_se(signal = dbus_message_new_signal(pa_dbusiface_core_get_client_path(conn->server->userdata->core_iface, c), + PA_DBUSIFACE_CLIENT_INTERFACE, + "ClientEvent")); + dbus_message_iter_init_append(signal, &msg_iter); + pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name)); + pa_dbus_append_proplist(&msg_iter, data); + + pa_assert_se(dbus_connection_send(pa_dbus_wrap_connection_get(conn->wrap_conn), signal, NULL)); + dbus_message_unref(signal); +} + +/* Called by D-Bus at the authentication phase. */ static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) { pa_log_debug("Allowing connection by user %lu.", uid); @@ -140,7 +167,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne 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? */ + pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); client = pa_client_new(s->userdata->module->core, &new_data); pa_client_new_data_done(&new_data); @@ -162,7 +189,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne c->client = client; c->client->kill = client_kill_cb; - c->client->send_event = NULL; /* TODO: Implement this. */ + c->client->send_event = client_send_event_cb; c->client->userdata = c; pa_idxset_put(s->userdata->connections, c, NULL); -- cgit From 0e096632c53b746b9f4b4c0249d9e5a18c1c543d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 30 Aug 2009 19:52:22 +0300 Subject: dbus: Do message argument type checking early, centrally. --- src/modules/dbus/iface-card.c | 19 +- src/modules/dbus/iface-client.c | 17 +- src/modules/dbus/iface-core.c | 247 ++++++++++---------------- src/modules/dbus/iface-device.c | 56 +++--- src/modules/dbus/iface-sample.c | 24 +-- src/modules/dbus/iface-stream.c | 33 ++-- src/modules/module-stream-restore.c | 120 ++++--------- src/pulsecore/dbus-util.c | 193 +++------------------ src/pulsecore/dbus-util.h | 42 +++-- src/pulsecore/protocol-dbus.c | 336 +++++++++++++++++++++++------------- src/pulsecore/protocol-dbus.h | 26 ++- 11 files changed, 471 insertions(+), 642 deletions(-) diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 7e37f8bb..1714df36 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -43,7 +43,7 @@ static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userd static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -322,7 +322,7 @@ static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, vo pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile); } -static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_card *c = userdata; const char *new_active_path; pa_dbusiface_card_profile *new_active; @@ -330,11 +330,9 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) - return; - if (!c->active_profile) { pa_assert(pa_hashmap_isempty(c->profiles)); @@ -344,6 +342,8 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo return; } + dbus_message_iter_get_basic(iter, &new_active_path); + if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path); return; @@ -430,7 +430,6 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_card *c = userdata; - DBusError error; const char *profile_name = NULL; pa_dbusiface_card_profile *profile = NULL; const char *profile_path = NULL; @@ -439,13 +438,7 @@ static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, v pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)); if (!(profile = pa_hashmap_get(c->profiles, profile_name))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name); diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index a68259a9..54550d2c 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -339,16 +339,12 @@ static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, voi return; } - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &update_mode) < 0) - goto finish; + dbus_message_iter_get_basic(&msg_iter, &update_mode); if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode); @@ -368,7 +364,6 @@ static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, voi pa_dbusiface_client *c = userdata; char **keys = NULL; int n_keys = 0; - DBusError error; pa_bool_t changed = FALSE; int i = 0; @@ -376,18 +371,12 @@ static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, voi pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); return; } - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)); for (i = 0; i < n_keys; ++i) changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0; diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index c54a3b7a..0507ac9c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -57,18 +57,18 @@ static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *us static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -428,30 +428,32 @@ static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, pa_xfree(default_channels); } -static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; + DBusMessageIter array_iter; pa_channel_map new_channel_map; - dbus_uint32_t *default_channels; - unsigned n_channels; + const dbus_uint32_t *default_channels; + int n_channels; unsigned i; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); pa_channel_map_init(&new_channel_map); - if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_channels, &n_channels) < 0) - return; + dbus_message_iter_recurse(iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels); if (n_channels <= 0) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); return; } - if (n_channels > PA_CHANNELS_MAX) { + if (n_channels > (int) PA_CHANNELS_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); + "Too many channels: %i. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); return; } @@ -485,16 +487,16 @@ static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage * pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format); } -static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; dbus_uint32_t default_sample_format; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_format) < 0) - return; + dbus_message_iter_get_basic(iter, &default_sample_format); if (default_sample_format >= PA_SAMPLE_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); @@ -519,16 +521,16 @@ static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *ms pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate); } -static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; dbus_uint32_t default_sample_rate; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate) < 0) - return; + dbus_message_iter_get_basic(iter, &default_sample_rate); if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate."); @@ -639,13 +641,14 @@ static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); } -static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; pa_dbusiface_device *fallback_sink; const char *object_path; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); if (!c->fallback_sink) { @@ -654,8 +657,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi return; } - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) - return; + dbus_message_iter_get_basic(iter, &object_path); if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path); @@ -727,13 +729,14 @@ static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, v pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); } -static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; pa_dbusiface_device *fallback_source; const char *object_path; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); if (!c->fallback_source) { @@ -742,8 +745,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v return; } - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) - return; + dbus_message_iter_get_basic(iter, &object_path); if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path); @@ -1117,19 +1119,12 @@ static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void pa_card *card; pa_dbusiface_card *dbus_card; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)); if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card."); @@ -1149,19 +1144,12 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void pa_sink *sink; pa_dbusiface_device *dbus_sink; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)); if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name); @@ -1181,19 +1169,12 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo pa_source *source; pa_dbusiface_device *dbus_source; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)); if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name); @@ -1213,19 +1194,12 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo pa_scache_entry *sample; pa_dbusiface_sample *dbus_sample; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)); if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample."); @@ -1242,17 +1216,18 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_core *c = userdata; DBusMessageIter msg_iter; + DBusMessageIter array_iter; const char *name; dbus_uint32_t sample_format; dbus_uint32_t sample_rate; const dbus_uint32_t *channels; - unsigned n_channels; + int n_channels; const dbus_uint32_t *default_volume; - unsigned n_volume_entries; + int n_volume_entries; pa_proplist *property_list; const uint8_t *data; - unsigned data_length; - unsigned i; + int data_length; + int i; pa_sample_spec ss; pa_channel_map map; pa_memchunk chunk; @@ -1267,31 +1242,29 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u chunk.memblock = NULL; - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) - return; + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &name); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_format) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &sample_format); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_rate) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &sample_rate); - if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &channels, &n_channels) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_recurse(&msg_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &channels, &n_channels); - if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &default_volume, &n_volume_entries) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_recurse(&msg_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &default_volume, &n_volume_entries); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; - if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_BYTE, &data, &data_length) < 0) - goto finish; + dbus_message_iter_recurse(&msg_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &data, &data_length); if (sample_format >= PA_SAMPLE_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); @@ -1303,14 +1276,14 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u goto finish; } - if (n_channels == 0) { + if (n_channels <= 0) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map."); goto finish; } - if (n_channels > PA_CHANNELS_MAX) { + if (n_channels > (int) PA_CHANNELS_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Too many channels: %u. The maximum is %u.", n_channels, PA_CHANNELS_MAX); + "Too many channels: %i. The maximum is %u.", n_channels, PA_CHANNELS_MAX); goto finish; } @@ -1323,13 +1296,14 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u if (n_volume_entries != 0 && n_volume_entries != n_channels) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "The channels and default_volume arguments have different number of elements."); + "The channels and default_volume arguments have different number of elements (%i and %i, resp).", + n_channels, n_volume_entries); goto finish; } for (i = 0; i < n_volume_entries; ++i) { if (default_volume[i] > PA_VOLUME_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u.", default_volume[i]); goto finish; } } @@ -1340,7 +1314,9 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u } if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too big sample."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too big sample: %i bytes. The maximum sample length is %u bytes.", + data_length, PA_SCACHE_ENTRY_SIZE_MAX); goto finish; } @@ -1348,9 +1324,13 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u ss.rate = sample_rate; ss.channels = n_channels; + pa_assert(pa_sample_spec_valid(&ss)); + if (!pa_frame_aligned(data_length, &ss)) { + char buf[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "The sample length in bytes doesn't align with the sample format and channels."); + "The sample length (%i bytes) doesn't align with the sample format and channels (%s).", + data_length, pa_sample_spec_snprint(buf, sizeof(buf), &ss)); goto finish; } @@ -1414,15 +1394,15 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use DBusMessageIter msg_iter; DBusMessageIter dict_iter; DBusMessageIter dict_entry_iter; - char *name; - const char *key; - const char *value; - char *escaped_value; + char *name = NULL; + const char *key = NULL; + const char *value = NULL; + char *escaped_value = NULL; pa_strbuf *arg_buffer = NULL; - pa_module *module; - pa_dbusiface_module *dbus_module; - const char *object_path; - int arg_type; + char *arg_string = NULL; + pa_module *module = NULL; + pa_dbusiface_module *dbus_module = NULL; + const char *object_path = NULL; pa_assert(conn); pa_assert(msg); @@ -1433,35 +1413,12 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use return; } - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) - return; - - arg_type = dbus_message_iter_get_arg_type(&msg_iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Too few arguments. A dictionary from strings to strings was expected."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", - (char) arg_type); - return; - } - - arg_type = dbus_message_iter_get_element_type(&msg_iter); - if (arg_type != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); - return; - } + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &name); arg_buffer = pa_strbuf_new(); + pa_assert_se(dbus_message_iter_next(&msg_iter)); dbus_message_iter_recurse(&msg_iter, &dict_iter); while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { @@ -1470,41 +1427,26 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); - goto finish; - } - dbus_message_iter_get_basic(&dict_entry_iter, &key); - dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key); goto finish; } - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_STRING) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); - goto finish; - } - + pa_assert_se(dbus_message_iter_next(&dict_entry_iter)); dbus_message_iter_get_basic(&dict_entry_iter, &value); - dbus_message_iter_next(&dict_iter); - escaped_value = pa_escape(value, "\""); pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value); pa_xfree(escaped_value); + + dbus_message_iter_next(&dict_iter); } - if (!(module = pa_module_load(c->core, name, pa_strbuf_tostring(arg_buffer)))) { + arg_string = pa_strbuf_tostring(arg_buffer); + + if (!(module = pa_module_load(c->core, name, arg_string))) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module."); goto finish; } @@ -1519,6 +1461,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use finish: if (arg_buffer) pa_strbuf_free(arg_buffer); + + pa_xfree(arg_string); } static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) { @@ -1543,47 +1487,32 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi const char *signal; char **objects = NULL; int n_objects; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, - DBUS_TYPE_STRING, &signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, - DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - goto finish; - } + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, + DBUS_TYPE_INVALID)); pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL, objects, n_objects); pa_dbus_send_empty_reply(conn, msg); -finish: dbus_free_string_array(objects); } static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_core *c = userdata; const char *signal; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)); pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL); diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 8f719bce..27525113 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -43,13 +43,13 @@ static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, voi static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -60,7 +60,7 @@ static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -408,16 +408,18 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels); } -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; - unsigned device_channels = 0; + DBusMessageIter array_iter; + int device_channels = 0; dbus_uint32_t *volume = NULL; - unsigned n_volume_entries = 0; + int n_volume_entries = 0; pa_cvolume new_vol; - unsigned i = 0; + int i = 0; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(d); pa_cvolume_init(&new_vol); @@ -426,12 +428,12 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user new_vol.channels = device_channels; - if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) - return; + dbus_message_iter_recurse(iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries); if (n_volume_entries != device_channels) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Expected %u volume entries, got %u.", device_channels, n_volume_entries); + "Expected %u volume entries, got %i.", device_channels, n_volume_entries); return; } @@ -515,16 +517,16 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; dbus_bool_t is_muted = FALSE; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(d); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) - return; + dbus_message_iter_get_basic(iter, &is_muted); if (d->type == DEVICE_TYPE_SINK) pa_sink_set_mute(d->sink, is_muted, TRUE); @@ -722,7 +724,7 @@ static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port); } -static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; const char *new_active_path; pa_dbusiface_device_port *new_active; @@ -730,11 +732,9 @@ static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(d); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) - return; - if (!d->active_port) { pa_assert(pa_hashmap_isempty(d->ports)); @@ -747,8 +747,10 @@ static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void return; } + dbus_message_iter_get_basic(iter, &new_active_path); + if (!(new_active = pa_hashmap_get(d->ports, new_active_path))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such port.", new_active_path); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such port: %s", new_active_path); return; } @@ -923,19 +925,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; dbus_bool_t suspend = FALSE; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(d); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)); if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed."); @@ -950,7 +945,6 @@ static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; - DBusError error; const char *port_name = NULL; pa_dbusiface_device_port *port = NULL; const char *port_path = NULL; @@ -959,13 +953,7 @@ static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void pa_assert(msg); pa_assert(d); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)); if (!(port = pa_hashmap_get(d->ports, port_name))) { if (d->type == DEVICE_TYPE_SINK) diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index 7147be14..b0542a60 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -359,14 +359,10 @@ static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) pa_assert(msg); pa_assert(s); - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) - return; + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &volume); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; @@ -405,17 +401,13 @@ static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *us pa_assert(msg); pa_assert(s); - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_OBJECT_PATH, &sink_path) < 0) - return; + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &sink_path); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &volume); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 183625ba..a5f9bb51 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -70,9 +70,9 @@ static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, voi static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -343,16 +343,18 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels); } -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_stream *s = userdata; - unsigned stream_channels = 0; + DBusMessageIter array_iter; + int stream_channels = 0; dbus_uint32_t *volume = NULL; - unsigned n_volume_entries = 0; + int n_volume_entries = 0; pa_cvolume new_vol; - unsigned i = 0; + int i = 0; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(s); if (s->type == STREAM_TYPE_RECORD) { @@ -366,8 +368,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user new_vol.channels = stream_channels; - if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) - return; + dbus_message_iter_recurse(iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries); if (n_volume_entries != stream_channels) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, @@ -403,16 +405,16 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_stream *s = userdata; dbus_bool_t is_muted = FALSE; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(s); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) - return; + dbus_message_iter_get_basic(iter, &is_muted); if (s->type == STREAM_TYPE_RECORD) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); @@ -575,19 +577,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; const char *device = NULL; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(s); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)); if (s->type == STREAM_TYPE_PLAYBACK) { pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device); diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 076b3918..d45cf792 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -167,11 +167,11 @@ static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, voi static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -326,18 +326,20 @@ static void dbus_entry_free(struct dbus_entry *de) { /* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are * are a channel position and a volume value, respectively. The result is - * stored in the map and vol arguments. If the volume can't be read from the - * iterator, an error reply is sent and a negative number is returned. In case - * of a failure we make no guarantees about the state of map and vol. In case - * of an empty array the channels field of both map and vol are set to 0. */ + * stored in the map and vol arguments. The iterator must point to a "a(uu)" + * element. If the data is invalid, an error reply is sent and a negative + * number is returned. In case of a failure we make no guarantees about the + * state of map and vol. In case of an empty array the channels field of both + * map and vol are set to 0. This function calls dbus_message_iter_next(iter) + * before returning. */ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, pa_channel_map *map, pa_cvolume *vol) { DBusMessageIter array_iter; DBusMessageIter struct_iter; - int arg_type; pa_assert(conn); pa_assert(msg); pa_assert(iter); + pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a(uu)")); pa_assert(map); pa_assert(vol); @@ -347,21 +349,6 @@ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIte map->channels = 0; vol->channels = 0; - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); - return -1; - } - - arg_type = dbus_message_iter_get_element_type(iter); - if (arg_type != DBUS_TYPE_STRUCT) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A struct was expected.", (char) arg_type); - return -1; - } - dbus_message_iter_recurse(iter, &array_iter); while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) { @@ -370,29 +357,14 @@ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIte dbus_message_iter_recurse(&array_iter, &struct_iter); - arg_type = dbus_message_iter_get_arg_type(&struct_iter); - if (arg_type != DBUS_TYPE_UINT32) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong channel position type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); - return -1; - } - dbus_message_iter_get_basic(&struct_iter, &chan_pos); - dbus_message_iter_next(&struct_iter); if (chan_pos >= PA_CHANNEL_POSITION_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u", chan_pos); return -1; } - arg_type = dbus_message_iter_get_arg_type(&struct_iter); - if (arg_type != DBUS_TYPE_UINT32) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Channel volume missing."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong volume value type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); - return -1; - } - + pa_assert_se(dbus_message_iter_next(&struct_iter)); dbus_message_iter_get_basic(&struct_iter, &chan_vol); if (chan_vol > PA_VOLUME_MAX) { @@ -612,40 +584,35 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata) { struct userdata *u = userdata; DBusMessageIter msg_iter; - const char *name; - const char *device; + const char *name = NULL; + const char *device = NULL; pa_channel_map map; pa_cvolume vol; - dbus_bool_t muted; - dbus_bool_t apply_immediately; + dbus_bool_t muted = FALSE; + dbus_bool_t apply_immediately = FALSE; pa_datum key; pa_datum value; - struct dbus_entry *dbus_entry; - struct entry *e; + struct dbus_entry *dbus_entry = NULL; + struct entry *e = NULL; pa_assert(conn); pa_assert(msg); pa_assert(u); - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &name); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) - return; - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &device) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &device); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) return; - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &muted) < 0) - return; + dbus_message_iter_get_basic(&msg_iter, &muted); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &apply_immediately) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &apply_immediately); if (!*name) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "An empty string was given as the entry name."); @@ -714,19 +681,12 @@ static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, voi struct userdata *u = userdata; const char *name; struct dbus_entry *de; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(u); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)); if (!(de = pa_hashmap_get(u->dbus_entries, name))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such stream restore entry."); @@ -774,7 +734,7 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void pa_xfree(e); } -static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; const char *device; struct entry *e; @@ -782,10 +742,10 @@ static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(de); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_STRING, &device) < 0) - return; + dbus_message_iter_get_basic(iter, &device); pa_assert_se(e = read_entry(de->userdata, de->entry_name)); @@ -835,25 +795,19 @@ static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void pa_xfree(e); } -static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; - DBusMessageIter msg_iter; pa_channel_map map; pa_cvolume vol; - struct entry *e; - pa_bool_t updated; + struct entry *e = NULL; + pa_bool_t updated = FALSE; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(de); - /* 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(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) + if (get_volume_arg(conn, msg, iter, &map, &vol) < 0) return; pa_assert_se(e = read_entry(de->userdata, de->entry_name)); @@ -901,7 +855,7 @@ static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, vo pa_xfree(e); } -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; pa_bool_t muted; struct entry *e; @@ -909,10 +863,10 @@ static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, vo pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(de); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &muted) < 0) - return; + dbus_message_iter_get_basic(iter, &muted); pa_assert_se(e = read_entry(de->userdata, de->entry_name)); diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index b45e6a6c..e3700ea5 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -291,7 +291,10 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool return pconn; } -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusConnection *conn) { +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing( + pa_mainloop_api *m, + pa_bool_t use_rtclock, + DBusConnection *conn) { pa_dbus_wrap_connection *pconn; pa_assert(m); @@ -522,7 +525,10 @@ void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_t pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter)); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, + DBUS_TYPE_VARIANT, + signature_from_basic_type(type), + &variant_iter)); pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data)); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter)); pa_assert_se(dbus_connection_send(c, reply, NULL)); @@ -548,7 +554,12 @@ static unsigned basic_type_size(int type) { } } -void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) { +void pa_dbus_send_basic_array_variant_reply( + DBusConnection *c, + DBusMessage *in_reply_to, + int item_type, + void *array, + unsigned n) { DBusMessage *reply = NULL; DBusMessageIter msg_iter; @@ -641,7 +652,12 @@ void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const c pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } -void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) { +void pa_dbus_append_basic_array_variant_dict_entry( + DBusMessageIter *dict_iter, + const char *key, + int item_type, + const void *array, + unsigned n) { DBusMessageIter dict_entry_iter; pa_assert(dict_iter); @@ -711,156 +727,18 @@ void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, cons pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } -int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(c); - pa_assert(msg); - pa_assert(dbus_type_is_basic(type)); - pa_assert(data); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return -1; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return -1; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0) - return -1; - - return 0; -} - -int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) { - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(c); - pa_assert(msg); - pa_assert(dbus_type_is_fixed(item_type)); - pa_assert(data); - pa_assert(n); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return -1; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return -1; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0) - return -1; - - return 0; -} - -int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { - int arg_type; - - pa_assert(c); - pa_assert(msg); - pa_assert(iter); - pa_assert(dbus_type_is_basic(type)); - pa_assert(data); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != type) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type); - return -1; - } - - dbus_message_iter_get_basic(iter, data); - - dbus_message_iter_next(iter); - - return 0; -} - -int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { - DBusMessageIter array_iter; - int signed_n; - int arg_type; - int element_type; - - pa_assert(c); - pa_assert(msg); - pa_assert(iter); - pa_assert(dbus_type_is_fixed(item_type)); - pa_assert(array); - pa_assert(n); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type); - return -1; - } - - element_type = dbus_message_iter_get_element_type(iter); - if (element_type != item_type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type); - return -1; - } - - dbus_message_iter_recurse(iter, &array_iter); - - dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); - - dbus_message_iter_next(iter); - - pa_assert(signed_n >= 0); - - *n = signed_n; - - return 0; -} - pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { DBusMessageIter dict_iter; DBusMessageIter dict_entry_iter; - int arg_type; pa_proplist *proplist = NULL; - const char *key; - const uint8_t *value; - int value_length; + const char *key = NULL; + const uint8_t *value = NULL; + int value_length = 0; pa_assert(c); pa_assert(msg); pa_assert(iter); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); - return NULL; - } - - arg_type = dbus_message_iter_get_element_type(iter); - if (arg_type != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type); - return NULL; - } + pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a{say}")); proplist = pa_proplist_new(); @@ -869,32 +747,11 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_STRING) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); - goto fail; - } - dbus_message_iter_get_basic(&dict_entry_iter, &key); dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); - goto fail; - } - - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type); - goto fail; - } - - arg_type = dbus_message_iter_get_element_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_BYTE) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type); + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key); goto fail; } diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 9cee293c..f35e66cb 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -32,7 +32,10 @@ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error); -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusConnection *conn); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing( + pa_mainloop_api *mainloop, + pa_bool_t use_rtclock, + DBusConnection *conn); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); @@ -63,36 +66,41 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); void pa_dbus_free_pending_list(pa_dbus_pending **p); /* Sends an error message as the reply to the given message. */ -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); +void pa_dbus_send_error( + DBusConnection *c, + DBusMessage *in_reply_to, + const char *name, + const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); -void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); +void pa_dbus_send_basic_array_variant_reply( + DBusConnection *c, + DBusMessage *in_reply_to, + int item_type, + void *array, + unsigned n); void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist); void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); -void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); +void pa_dbus_append_basic_array_variant_dict_entry( + DBusMessageIter *dict_iter, + const char *key, + int item_type, + const void *array, + unsigned n); void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist); void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist); void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist); -/* Helper functions for extracting the value argument of a Set call. If the - * message is invalid, an error reply is sent and a negative number is - * returned. */ -int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); -int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n); - -/* If the arguments can't be read from the iterator, an error reply is sent and - * a negative number is returned. */ -int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); -int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); - -/* Returns a new proplist that the caller has to free. If the proplist can't be - * read from the iterator, an error reply is sent and NULL is returned. */ +/* Returns a new proplist that the caller has to free. If the proplist contains + * invalid keys, an error reply is sent and NULL is returned. The iterator must + * point to "a{say}" element. This function calls dbus_message_iter_next(iter) + * before returning. */ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); #endif diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index d1e19ff3..91022511 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -257,37 +257,90 @@ static void update_introspection(struct object_entry *oe) { oe->introspection = pa_strbuf_tostring_free(buf); } +/* Return value of find_handler() and its subfunctions. */ enum find_result_t { - FOUND_METHOD, + /* The received message is a valid .Get call. */ FOUND_GET_PROPERTY, + + /* The received message is a valid .Set call. */ FOUND_SET_PROPERTY, + + /* The received message is a valid .GetAll call. */ FOUND_GET_ALL, + + /* The received message is a valid method call. */ + FOUND_METHOD, + + /* The interface of the received message hasn't been registered for the + * destination object. */ + NO_SUCH_INTERFACE, + + /* No property handler was found for the received .Get or .Set call. */ + NO_SUCH_PROPERTY, + + /* The interface argument of a property call didn't match any registered + * interface. */ + NO_SUCH_PROPERTY_INTERFACE, + + /* The received message called .Get or .Set for a property whose access + * mode doesn't match the call. */ PROPERTY_ACCESS_DENIED, + + /* The new value signature of a .Set call didn't match the expexted + * signature. */ + INVALID_PROPERTY_SIG, + + /* No method handler was found for the received message. */ NO_SUCH_METHOD, - NO_SUCH_PROPERTY, - INVALID_MESSAGE_ARGUMENTS + + /* The signature of the received message didn't match the expected + * signature. Despite the name, this can also be returned for a property + * call if its message signature is invalid. */ + INVALID_METHOD_SIG }; -static enum find_result_t find_handler_by_property(struct object_entry *obj_entry, - DBusMessage *msg, - const char *property, - struct interface_entry **iface_entry, - pa_dbus_property_handler **property_handler) { +/* Data for resolving the correct reaction to a received message. */ +struct call_info { + DBusMessage *message; /* The received message. */ + struct object_entry *obj_entry; + const char *interface; /* Destination interface name (extracted from the message). */ + struct interface_entry *iface_entry; + + const char *property; /* Property name (extracted from the message). */ + const char *property_interface; /* The interface argument of a property call is stored here. */ + pa_dbus_property_handler *property_handler; + const char *expected_property_sig; /* Property signature from the introspection data. */ + const char *property_sig; /* The signature of the new value in the received .Set message. */ + DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */ + + const char *method; /* Method name (extracted from the message). */ + pa_dbus_method_handler *method_handler; + const char *expected_method_sig; /* Method signature from the introspection data. */ + const char *method_sig; /* The signature of the received message. */ +}; + +/* Called when call_info->property has been set and the property interface has + * not been given. In case of a Set call, call_info->property_sig is also set, + * which is checked against the expected value in this function. */ +static enum find_result_t find_handler_by_property(struct call_info *call_info) { void *state = NULL; - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(property); - pa_assert(iface_entry); - pa_assert(property_handler); - - PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { - if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { - if (dbus_message_has_member(msg, "Get")) - return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; - else if (dbus_message_has_member(msg, "Set")) - return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; - else + pa_assert(call_info); + + PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { + if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) { + if (pa_streq(call_info->method, "Get")) + return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; + + else if (pa_streq(call_info->method, "Set")) { + call_info->expected_property_sig = call_info->property_handler->type; + + if (pa_streq(call_info->property_sig, call_info->expected_property_sig)) + return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + else + return INVALID_PROPERTY_SIG; + + } else pa_assert_not_reached(); } } @@ -295,120 +348,138 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr return NO_SUCH_PROPERTY; } -static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, - const char *method, - struct interface_entry **iface_entry, - pa_dbus_method_handler **method_handler) { +static enum find_result_t find_handler_by_method(struct call_info *call_info) { void *state = NULL; - pa_assert(obj_entry); - pa_assert(method); - pa_assert(iface_entry); - pa_assert(method_handler); + pa_assert(call_info); - PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { - if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) + PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { + if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) return FOUND_METHOD; } return NO_SUCH_METHOD; } -static enum find_result_t find_handler_from_properties_call(struct object_entry *obj_entry, - DBusMessage *msg, - struct interface_entry **iface_entry, - pa_dbus_property_handler **property_handler, - const char **attempted_property) { - const char *interface; +static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) { + pa_assert(call_info); - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(iface_entry); + if (pa_streq(call_info->method, "GetAll")) { + call_info->expected_method_sig = "s"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; - if (dbus_message_has_member(msg, "GetAll")) { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; + pa_assert_se(dbus_message_get_args(call_info->message, NULL, + DBUS_TYPE_STRING, &call_info->property_interface, + DBUS_TYPE_INVALID)); - if (*interface) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) + if (*call_info->property_interface) { + if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) return FOUND_GET_ALL; - else { - return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ - } + else + return NO_SUCH_PROPERTY_INTERFACE; + } else { - pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); + pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces)); return FOUND_GET_ALL; } - } else { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, attempted_property, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; - - if (*interface) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && - (*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, *attempted_property))) { - if (dbus_message_has_member(msg, "Get")) - return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; - else if (dbus_message_has_member(msg, "Set")) - return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + + } else if (pa_streq(call_info->method, "Get")) { + call_info->expected_method_sig = "ss"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; + + pa_assert_se(dbus_message_get_args(call_info->message, NULL, + DBUS_TYPE_STRING, &call_info->property_interface, + DBUS_TYPE_STRING, &call_info->property, + DBUS_TYPE_INVALID)); + + if (*call_info->property_interface) { + if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) + return NO_SUCH_PROPERTY_INTERFACE; + else if ((call_info->property_handler = + pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) + return FOUND_GET_PROPERTY; + else + return NO_SUCH_PROPERTY; + + } else + return find_handler_by_property(call_info); + + } else if (pa_streq(call_info->method, "Set")) { + DBusMessageIter msg_iter; + + call_info->expected_method_sig = "ssv"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; + + pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter)); + + dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface); + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &call_info->property); + pa_assert_se(dbus_message_iter_next(&msg_iter)); + + dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter); + + call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter); + + if (*call_info->property_interface) { + if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) + return NO_SUCH_PROPERTY_INTERFACE; + + else if ((call_info->property_handler = + pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) { + call_info->expected_property_sig = call_info->property_handler->type; + + if (pa_streq(call_info->property_sig, call_info->expected_property_sig)) + return FOUND_SET_PROPERTY; else - pa_assert_not_reached(); + return INVALID_PROPERTY_SIG; + } else return NO_SUCH_PROPERTY; + } else - return find_handler_by_property(obj_entry, msg, *attempted_property, iface_entry, property_handler); - } -} + return find_handler_by_property(call_info); -static enum find_result_t find_handler(struct object_entry *obj_entry, - DBusMessage *msg, - struct interface_entry **iface_entry, - pa_dbus_method_handler **method_handler, - pa_dbus_property_handler **property_handler, - const char **attempted_property) { - const char *interface; + } else + pa_assert_not_reached(); +} - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(iface_entry); - pa_assert(method_handler); - pa_assert(property_handler); - pa_assert(attempted_property); +static enum find_result_t find_handler(struct call_info *call_info) { + pa_assert(call_info); - *iface_entry = NULL; - *method_handler = NULL; + if (call_info->interface) { + if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES)) + return find_handler_from_properties_call(call_info); - if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) - return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface))) + return NO_SUCH_INTERFACE; - else if ((interface = dbus_message_get_interface(msg))) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && - (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) + else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) return FOUND_METHOD; - else { - pa_log("Message has unknown interface or there's no method handler."); + + else return NO_SUCH_METHOD; - } } else { /* The method call doesn't contain an interface. */ - if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { - if (find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler) == FOUND_METHOD) - return FOUND_METHOD; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) { + if (find_handler_by_method(call_info) == FOUND_METHOD) + /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + return FOUND_METHOD; else /* Assume this is a .Properties call. */ - return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + return find_handler_from_properties_call(call_info); } else /* This is not a .Properties call. */ - return find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler); + return find_handler_by_method(call_info); } } static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { pa_dbus_protocol *p = user_data; - struct object_entry *obj_entry = NULL; - struct interface_entry *iface_entry = NULL; - pa_dbus_method_handler *method_handler = NULL; - pa_dbus_property_handler *property_handler = NULL; - const char *attempted_property = NULL; + struct call_info call_info; pa_assert(connection); pa_assert(message); @@ -423,49 +494,72 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa dbus_message_get_interface(message), dbus_message_get_member(message)); - pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); + call_info.message = message; + pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message))); + call_info.interface = dbus_message_get_interface(message); + pa_assert_se(call_info.method = dbus_message_get_member(message)); + pa_assert_se(call_info.method_sig = dbus_message_get_signature(message)); if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection); + pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection); goto finish; } - switch (find_handler(obj_entry, message, &iface_entry, &method_handler, &property_handler, &attempted_property)) { - case FOUND_METHOD: - method_handler->receive_cb(connection, message, iface_entry->userdata); - break; - + switch (find_handler(&call_info)) { case FOUND_GET_PROPERTY: - property_handler->get_cb(connection, message, iface_entry->userdata); + call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata); break; case FOUND_SET_PROPERTY: - property_handler->set_cb(connection, message, iface_entry->userdata); + call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata); + break; + + case FOUND_METHOD: + call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata); break; case FOUND_GET_ALL: - if (iface_entry->get_all_properties_cb) - iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); - /* TODO: Write an else branch where a dummy response is sent. */ + if (call_info.iface_entry->get_all_properties_cb) + call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata); + else { + DBusMessage *dummy_reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + + pa_assert_se(dummy_reply = dbus_message_new_method_return(message)); + dbus_message_iter_init_append(dummy_reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL)); + dbus_message_unref(dummy_reply); + } break; case PROPERTY_ACCESS_DENIED: - pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property); + pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, + "%s access denied for property %s", call_info.method, call_info.property); break; case NO_SUCH_METHOD: - pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)); + pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method); break; case NO_SUCH_PROPERTY: - pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property); + pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property); break; - case INVALID_MESSAGE_ARGUMENTS: - pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)); + case INVALID_METHOD_SIG: + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, + "Invalid signature for method %s: '%s'. Expected '%s'.", + call_info.method, call_info.method_sig, call_info.expected_method_sig); break; + case INVALID_PROPERTY_SIG: + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, + "Invalid signature for property %s: '%s'. Expected '%s'.", + call_info.property, call_info.property_sig, call_info.expected_property_sig); + default: pa_assert_not_reached(); } @@ -821,7 +915,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn return conn_entry->client; } -void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { +void pa_dbus_protocol_add_signal_listener( + pa_dbus_protocol *p, + DBusConnection *conn, + const char *signal, + char **objects, + unsigned n_objects) { struct connection_entry *conn_entry; pa_idxset *object_set; char *object_path; @@ -981,7 +1080,12 @@ int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) return 0; } -pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) { +pa_hook_slot *pa_dbus_protocol_hook_connect( + pa_dbus_protocol *p, + pa_dbus_protocol_hook_t hook, + pa_hook_priority_t prio, + pa_hook_cb_t cb, + void *data) { pa_assert(p); pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX); pa_assert(cb); diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index d771b4fc..6d100f7c 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -56,9 +56,19 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p); * message isn't a good idea; if you can't handle the message, reply with an * error. * + * The message signature is already checked against the introspection data, so + * you don't have to do that yourself. + * * All messages are method calls. */ typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata); +/* A specialized version of pa_dbus_receive_cb_t: the additional iterator + * argument points to the element inside the new value variant. + * + * The new value signature is checked against the introspection data, so you + * don't have to do that yourself. */ +typedef void (*pa_dbus_set_property_cb_t)(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); + typedef struct pa_dbus_arg_info { const char *name; const char *type; @@ -85,7 +95,7 @@ typedef struct pa_dbus_property_handler { /* The access mode for the property is determined by checking whether * get_cb or set_cb is NULL. */ pa_dbus_receive_cb_t get_cb; - pa_dbus_receive_cb_t set_cb; + pa_dbus_set_property_cb_t set_cb; } pa_dbus_property_handler; typedef struct pa_dbus_interface_info { @@ -140,7 +150,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn * only signals from the given objects are delivered. If this function is * called multiple time for the same connection and signal, the latest call * always replaces the previous object list. */ -void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects); +void pa_dbus_protocol_add_signal_listener( + pa_dbus_protocol *p, + DBusConnection *conn, + const char *signal, + char **objects, + unsigned n_objects); /* Disables the delivery of the signal for the given connection. The connection * must have been registered. If signal is NULL, all signals are disabled. If @@ -192,6 +207,11 @@ typedef enum pa_dbus_protocol_hook { PA_DBUS_PROTOCOL_HOOK_MAX } pa_dbus_protocol_hook_t; -pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); +pa_hook_slot *pa_dbus_protocol_hook_connect( + pa_dbus_protocol *p, + pa_dbus_protocol_hook_t hook, + pa_hook_priority_t prio, + pa_hook_cb_t cb, + void *data); #endif -- cgit From 411feaed15edcef685e88582d76eddc5acfd965e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 31 Aug 2009 09:14:50 +0300 Subject: dbusiface-core: Add signals FallbackSinkUnset and FallbackSourceUnset. --- src/modules/dbus/iface-core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 0507ac9c..169e8e55 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -247,9 +247,11 @@ enum signal_index { SIGNAL_NEW_SINK, SIGNAL_SINK_REMOVED, SIGNAL_FALLBACK_SINK_UPDATED, + SIGNAL_FALLBACK_SINK_UNSET, SIGNAL_NEW_SOURCE, SIGNAL_SOURCE_REMOVED, SIGNAL_FALLBACK_SOURCE_UPDATED, + SIGNAL_FALLBACK_SOURCE_UNSET, SIGNAL_NEW_PLAYBACK_STREAM, SIGNAL_PLAYBACK_STREAM_REMOVED, SIGNAL_NEW_RECORD_STREAM, @@ -292,9 +294,11 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { [SIGNAL_NEW_SINK] = { .name = "NewSink", .arguments = new_sink_args, .n_arguments = 1 }, [SIGNAL_SINK_REMOVED] = { .name = "SinkRemoved", .arguments = sink_removed_args, .n_arguments = 1 }, [SIGNAL_FALLBACK_SINK_UPDATED] = { .name = "FallbackSinkUpdated", .arguments = fallback_sink_updated_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SINK_UNSET] = { .name = "FallbackSinkUnset", .arguments = NULL, .n_arguments = 0 }, [SIGNAL_NEW_SOURCE] = { .name = "NewSource", .arguments = new_source_args, .n_arguments = 1 }, [SIGNAL_SOURCE_REMOVED] = { .name = "SourceRemoved", .arguments = source_removed_args, .n_arguments = 1 }, [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SOURCE_UNSET] = { .name = "FallbackSourceUnset", .arguments = NULL, .n_arguments = 0 }, [SIGNAL_NEW_PLAYBACK_STREAM] = { .name = "NewPlaybackStream", .arguments = new_playback_stream_args, .n_arguments = 1 }, [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 }, [SIGNAL_NEW_RECORD_STREAM] = { .name = "NewRecordStream", .arguments = new_record_stream_args, .n_arguments = 1 }, @@ -1555,6 +1559,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); signal = NULL; + + } else if (!new_fallback_sink) { + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SINK_UNSET].name))); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } @@ -1574,6 +1586,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); signal = NULL; + + } else if (!new_fallback_source) { + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SOURCE_UNSET].name))); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } break; -- cgit From 8a28e5de941cece0fccaac1c2babc502e2b2d46f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 31 Aug 2009 17:17:09 +0300 Subject: dbus: Change IsMuted property names to Mute. --- src/modules/dbus/iface-device.c | 40 +++++++++++++++++------------------ src/modules/dbus/iface-stream.c | 38 ++++++++++++++++----------------- src/modules/module-stream-restore.c | 42 ++++++++++++++++++------------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 27525113..3a747a44 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -48,8 +48,8 @@ static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, v static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -91,7 +91,7 @@ struct pa_dbusiface_device { enum device_type type; char *path; pa_cvolume volume; - pa_bool_t is_muted; + dbus_bool_t mute; union { pa_sink_state_t sink_state; pa_source_state_t source_state; @@ -119,7 +119,7 @@ enum property_handler_index { PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME, PROPERTY_HANDLER_BASE_VOLUME, PROPERTY_HANDLER_VOLUME_STEPS, - PROPERTY_HANDLER_IS_MUTED, + PROPERTY_HANDLER_MUTE, PROPERTY_HANDLER_HAS_HARDWARE_VOLUME, PROPERTY_HANDLER_HAS_HARDWARE_MUTE, PROPERTY_HANDLER_CONFIGURED_LATENCY, @@ -158,7 +158,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b", .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL }, [PROPERTY_HANDLER_BASE_VOLUME] = { .property_name = "BaseVolume", .type = "u", .get_cb = handle_get_base_volume, .set_cb = NULL }, [PROPERTY_HANDLER_VOLUME_STEPS] = { .property_name = "VolumeSteps", .type = "u", .get_cb = handle_get_volume_steps, .set_cb = NULL }, - [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, + [PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_get_mute, .set_cb = handle_set_mute }, [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME] = { .property_name = "HasHardwareVolume", .type = "b", .get_cb = handle_get_has_hardware_volume, .set_cb = NULL }, [PROPERTY_HANDLER_HAS_HARDWARE_MUTE] = { .property_name = "HasHardwareMute", .type = "b", .get_cb = handle_get_has_hardware_mute, .set_cb = NULL }, [PROPERTY_HANDLER_CONFIGURED_LATENCY] = { .property_name = "ConfiguredLatency", .type = "t", .get_cb = handle_get_configured_latency, .set_cb = NULL }, @@ -507,31 +507,31 @@ static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps); } -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; pa_assert(conn); pa_assert(msg); pa_assert(d); - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->mute); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; - dbus_bool_t is_muted = FALSE; + dbus_bool_t mute = FALSE; pa_assert(conn); pa_assert(msg); pa_assert(iter); pa_assert(d); - dbus_message_iter_get_basic(iter, &is_muted); + dbus_message_iter_get_basic(iter, &mute); if (d->type == DEVICE_TYPE_SINK) - pa_sink_set_mute(d->sink, is_muted, TRUE); + pa_sink_set_mute(d->sink, mute, TRUE); else - pa_source_set_mute(d->source, is_muted, TRUE); + pa_source_set_mute(d->source, mute, TRUE); pa_dbus_send_empty_reply(conn, msg); } @@ -897,7 +897,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &d->is_muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &d->mute); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency); @@ -1065,7 +1065,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t pa_dbusiface_device *d = userdata; DBusMessage *signal = NULL; const pa_cvolume *new_volume = NULL; - pa_bool_t new_muted = FALSE; + pa_bool_t new_mute = FALSE; pa_sink_state_t new_sink_state = 0; pa_source_state_t new_source_state = 0; pa_device_port *new_active_port = NULL; @@ -1111,15 +1111,15 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t signal = NULL; } - new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); + new_mute = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); - if (d->is_muted != new_muted) { - d->is_muted = new_muted; + if (d->mute != new_mute) { + d->mute = new_mute; pa_assert_se(signal = dbus_message_new_signal(d->path, PA_DBUSIFACE_DEVICE_INTERFACE, signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->mute, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(d->dbus_protocol, signal); dbus_message_unref(signal); @@ -1201,7 +1201,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_si d->type = DEVICE_TYPE_SINK; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); d->volume = *pa_sink_get_volume(sink, FALSE); - d->is_muted = pa_sink_get_mute(sink, FALSE); + d->mute = pa_sink_get_mute(sink, FALSE); d->sink_state = pa_sink_get_state(sink); d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); d->next_port_index = 0; @@ -1239,7 +1239,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_ d->type = DEVICE_TYPE_SOURCE; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index); d->volume = *pa_source_get_volume(source, FALSE); - d->is_muted = pa_source_get_mute(source, FALSE); + d->mute = pa_source_get_mute(source, FALSE); d->source_state = pa_source_get_state(source); d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); d->next_port_index = 0; diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index a5f9bb51..04a45e6c 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -53,7 +53,7 @@ struct pa_dbusiface_stream { }; uint32_t sample_rate; pa_cvolume volume; - pa_bool_t is_muted; + dbus_bool_t mute; pa_proplist *proplist; pa_dbus_protocol *dbus_protocol; @@ -71,8 +71,8 @@ static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -93,7 +93,7 @@ enum property_handler_index { PROPERTY_HANDLER_SAMPLE_RATE, PROPERTY_HANDLER_CHANNELS, PROPERTY_HANDLER_VOLUME, - PROPERTY_HANDLER_IS_MUTED, + PROPERTY_HANDLER_MUTE, PROPERTY_HANDLER_BUFFER_LATENCY, PROPERTY_HANDLER_DEVICE_LATENCY, PROPERTY_HANDLER_RESAMPLE_METHOD, @@ -111,7 +111,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, - [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, + [PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_get_mute, .set_cb = handle_set_mute }, [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL }, [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL }, @@ -390,7 +390,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag pa_dbus_send_empty_reply(conn, msg); } -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; pa_assert(conn); @@ -402,26 +402,26 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us return; } - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->mute); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_stream *s = userdata; - dbus_bool_t is_muted = FALSE; + dbus_bool_t mute = FALSE; pa_assert(conn); pa_assert(msg); pa_assert(iter); pa_assert(s); - dbus_message_iter_get_basic(iter, &is_muted); + dbus_message_iter_get_basic(iter, &mute); if (s->type == STREAM_TYPE_RECORD) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); return; } - pa_sink_input_set_mute(s->sink_input, is_muted, TRUE); + pa_sink_input_set_mute(s->sink_input, mute, TRUE); pa_dbus_send_empty_reply(conn, msg); }; @@ -561,7 +561,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat if (s->type == STREAM_TYPE_PLAYBACK) { pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute); } pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency); @@ -708,7 +708,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t if (s->type == STREAM_TYPE_PLAYBACK) { pa_cvolume new_volume; - pa_bool_t new_muted = FALSE; + pa_bool_t new_mute = FALSE; pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); @@ -733,15 +733,15 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t signal = NULL; } - new_muted = pa_sink_input_get_mute(s->sink_input); + new_mute = pa_sink_input_get_mute(s->sink_input); - if (s->is_muted != new_muted) { - s->is_muted = new_muted; + if (s->mute != new_mute) { + s->mute = new_mute; pa_assert_se(signal = dbus_message_new_signal(s->path, PA_DBUSIFACE_STREAM_INTERFACE, signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->mute, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(s->dbus_protocol, signal); dbus_message_unref(signal); @@ -823,7 +823,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p s->sink = pa_sink_ref(sink_input->sink); s->sample_rate = sink_input->sample_spec.rate; pa_sink_input_get_volume(sink_input, &s->volume, TRUE); - s->is_muted = pa_sink_input_get_mute(sink_input); + s->mute = pa_sink_input_get_mute(sink_input); s->proplist = pa_proplist_copy(sink_input->proplist); s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); @@ -851,7 +851,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ s->source = pa_source_ref(source_output->source); s->sample_rate = source_output->sample_spec.rate; pa_cvolume_init(&s->volume); - s->is_muted = FALSE; + s->mute = FALSE; s->proplist = pa_proplist_copy(source_output->proplist); s->dbus_protocol = pa_dbus_protocol_get(source_output->core); s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 8389be67..5a6c8a3d 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -170,8 +170,8 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); -static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); +static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -188,7 +188,7 @@ enum entry_property_handler_index { ENTRY_PROPERTY_HANDLER_NAME, ENTRY_PROPERTY_HANDLER_DEVICE, ENTRY_PROPERTY_HANDLER_VOLUME, - ENTRY_PROPERTY_HANDLER_IS_MUTED, + ENTRY_PROPERTY_HANDLER_MUTE, ENTRY_PROPERTY_HANDLER_MAX }; @@ -202,7 +202,7 @@ static pa_dbus_property_handler entry_property_handlers[ENTRY_PROPERTY_HANDLER_M [ENTRY_PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_entry_get_name, .set_cb = NULL }, [ENTRY_PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "s", .get_cb = handle_entry_get_device, .set_cb = handle_entry_set_device }, [ENTRY_PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "a(uu)", .get_cb = handle_entry_get_volume, .set_cb = handle_entry_set_volume }, - [ENTRY_PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_entry_get_is_muted, .set_cb = handle_entry_set_is_muted } + [ENTRY_PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_entry_get_mute, .set_cb = handle_entry_set_mute } }; enum method_handler_index { @@ -216,11 +216,11 @@ enum entry_method_handler_index { ENTRY_METHOD_HANDLER_MAX }; -static pa_dbus_arg_info add_entry_args[] = { { "name", "s", "in" }, - { "device", "s", "in" }, - { "volume", "a(uu)", "in" }, - { "is_muted", "b", "in" }, - { "entry", "o", "out" } }; +static pa_dbus_arg_info add_entry_args[] = { { "name", "s", "in" }, + { "device", "s", "in" }, + { "volume", "a(uu)", "in" }, + { "mute", "b", "in" }, + { "entry", "o", "out" } }; static pa_dbus_arg_info get_entry_by_name_args[] = { { "name", "s", "in" }, { "entry", "o", "out" } }; static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { @@ -837,10 +837,10 @@ static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBus pa_xfree(e); } -static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { struct dbus_entry *de = userdata; struct entry *e; - dbus_bool_t muted; + dbus_bool_t mute; pa_assert(conn); pa_assert(msg); @@ -848,16 +848,16 @@ static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, vo pa_assert_se(e = read_entry(de->userdata, de->entry_name)); - muted = e->muted_valid ? e->muted : FALSE; + mute = e->muted_valid ? e->muted : FALSE; - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &muted); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &mute); pa_xfree(e); } -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { +static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; - pa_bool_t muted; + dbus_bool_t mute; struct entry *e; pa_bool_t updated; @@ -866,17 +866,17 @@ static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DB pa_assert(iter); pa_assert(de); - dbus_message_iter_get_basic(iter, &muted); + dbus_message_iter_get_basic(iter, &mute); pa_assert_se(e = read_entry(de->userdata, de->entry_name)); - updated = !e->muted_valid || e->muted != muted; + updated = !e->muted_valid || e->muted != mute; if (updated) { pa_datum key; pa_datum value; - e->muted = muted; + e->muted = mute; e->muted_valid = TRUE; key.data = de->entry_name; @@ -902,7 +902,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u DBusMessageIter dict_iter; DBusMessageIter dict_entry_iter; const char *device; - dbus_bool_t muted; + dbus_bool_t mute; pa_assert(conn); pa_assert(msg); @@ -911,7 +911,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u pa_assert_se(e = read_entry(de->userdata, de->entry_name)); device = e->device_valid ? e->device : ""; - muted = e->muted_valid ? e->muted : FALSE; + mute = e->muted_valid ? e->muted : FALSE; pa_assert_se((reply = dbus_message_new_method_return(msg))); @@ -929,7 +929,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &mute); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); -- cgit From 587131917f9129c8347c789febb7e755dfb091de Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 31 Aug 2009 18:12:55 +0300 Subject: dbus-protocol: Implement argument type checking for normal methods. --- src/modules/dbus/iface-client.c | 2 +- src/pulsecore/protocol-dbus.c | 44 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index 54550d2c..546370f9 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -101,7 +101,7 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { [METHOD_HANDLER_REMOVE_PROPERTIES] = { .method_name = "RemoveProperties", .arguments = remove_properties_args, - .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), + .n_arguments = sizeof(remove_properties_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_remove_properties } }; diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 91022511..5c1127be 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -72,6 +72,7 @@ struct connection_entry { struct interface_entry { char *name; pa_hashmap *method_handlers; + pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */ pa_hashmap *property_handlers; pa_dbus_receive_cb_t get_all_properties_cb; pa_dbus_signal_info *signals; @@ -354,8 +355,14 @@ static enum find_result_t find_handler_by_method(struct call_info *call_info) { pa_assert(call_info); PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { - if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) - return FOUND_METHOD; + if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) { + call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method); + + if (pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return FOUND_METHOD; + else + return INVALID_METHOD_SIG; + } } return NO_SUCH_METHOD; @@ -630,6 +637,31 @@ static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) { return handlers; } +static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) { + pa_hashmap *signatures = NULL; + pa_dbus_method_handler *handler = NULL; + void *state = NULL; + pa_strbuf *sig_buf = NULL; + unsigned i = 0; + + pa_assert(method_handlers); + + signatures = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + PA_HASHMAP_FOREACH(handler, method_handlers, state) { + sig_buf = pa_strbuf_new(); + + for (i = 0; i < handler->n_arguments; ++i) { + if (pa_streq(handler->arguments[i].direction, "in")) + pa_strbuf_puts(sig_buf, handler->arguments[i].type); + } + + pa_hashmap_put(signatures, handler->method_name, pa_strbuf_tostring_free(sig_buf)); + } + + return signatures; +} + static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) { pa_hashmap *handlers; unsigned i = 0; @@ -707,6 +739,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, iface_entry = pa_xnew(struct interface_entry, 1); iface_entry->name = pa_xstrdup(info->name); iface_entry->method_handlers = create_method_handlers(info); + iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers); iface_entry->property_handlers = create_property_handlers(info); iface_entry->get_all_properties_cb = info->get_all_properties_cb; iface_entry->signals = copy_signals(info); @@ -760,6 +793,12 @@ static void method_handler_free_cb(void *p, void *userdata) { pa_xfree((pa_dbus_arg_info *) h->arguments); } +static void method_signature_free_cb(void *p, void *userdata) { + pa_assert(p); + + pa_xfree(p); +} + static void property_handler_free_cb(void *p, void *userdata) { pa_dbus_property_handler *h = p; @@ -792,6 +831,7 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con pa_xfree(iface_entry->name); pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); + pa_hashmap_free(iface_entry->method_signatures, method_signature_free_cb, NULL); pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); for (i = 0; i < iface_entry->n_signals; ++i) { -- cgit