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