1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Tanu Kaskinen 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <pulsecore/core-util.h> 25#include <pulsecore/dbus-util.h> 26#include <pulsecore/modargs.h> 27#include <pulsecore/protocol-dbus.h> 28 29#include "iface-module.h" 30 31#define OBJECT_NAME "module" 32 33struct pa_dbusiface_module { 34 pa_module *module; 35 char *path; 36 pa_proplist *proplist; 37 38 pa_dbus_protocol *dbus_protocol; 39 pa_hook_slot *module_proplist_changed_slot; 40}; 41 42static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); 43static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); 44static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata); 45static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata); 46static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); 47 48static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); 49 50static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata); 51 52enum property_handler_index { 53 PROPERTY_HANDLER_INDEX, 54 PROPERTY_HANDLER_NAME, 55 PROPERTY_HANDLER_ARGUMENTS, 56 PROPERTY_HANDLER_USAGE_COUNTER, 57 PROPERTY_HANDLER_PROPERTY_LIST, 58 PROPERTY_HANDLER_MAX 59}; 60 61static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { 62 [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, 63 [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, 64 [PROPERTY_HANDLER_ARGUMENTS] = { .property_name = "Arguments", .type = "a{ss}", .get_cb = handle_get_arguments, .set_cb = NULL }, 65 [PROPERTY_HANDLER_USAGE_COUNTER] = { .property_name = "UsageCounter", .type = "u", .get_cb = handle_get_usage_counter, .set_cb = NULL }, 66 [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } 67}; 68 69enum method_handler_index { 70 METHOD_HANDLER_UNLOAD, 71 METHOD_HANDLER_MAX 72}; 73 74static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { 75 [METHOD_HANDLER_UNLOAD] = { 76 .method_name = "Unload", 77 .arguments = NULL, 78 .n_arguments = 0, 79 .receive_cb = handle_unload } 80}; 81 82enum signal_index { 83 SIGNAL_PROPERTY_LIST_UPDATED, 84 SIGNAL_MAX 85}; 86 87static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; 88 89static pa_dbus_signal_info signals[SIGNAL_MAX] = { 90 [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } 91}; 92 93static pa_dbus_interface_info module_interface_info = { 94 .name = PA_DBUSIFACE_MODULE_INTERFACE, 95 .method_handlers = method_handlers, 96 .n_method_handlers = METHOD_HANDLER_MAX, 97 .property_handlers = property_handlers, 98 .n_property_handlers = PROPERTY_HANDLER_MAX, 99 .get_all_properties_cb = handle_get_all, 100 .signals = signals, 101 .n_signals = SIGNAL_MAX 102}; 103 104static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { 105 pa_dbusiface_module *m = userdata; 106 dbus_uint32_t idx = 0; 107 108 pa_assert(conn); 109 pa_assert(msg); 110 pa_assert(m); 111 112 idx = m->module->index; 113 114 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); 115} 116 117static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { 118 pa_dbusiface_module *m = userdata; 119 120 pa_assert(conn); 121 pa_assert(msg); 122 pa_assert(m); 123 124 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &m->module->name); 125} 126 127static void append_modargs_variant(DBusMessageIter *iter, pa_dbusiface_module *m) { 128 pa_modargs *ma = NULL; 129 DBusMessageIter variant_iter; 130 DBusMessageIter dict_iter; 131 DBusMessageIter dict_entry_iter; 132 void *state = NULL; 133 const char *key = NULL; 134 const char *value = NULL; 135 136 pa_assert(iter); 137 pa_assert(m); 138 139 pa_assert_se(ma = pa_modargs_new(m->module->argument, NULL)); 140 141 pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{ss}", &variant_iter)); 142 pa_assert_se(dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "{ss}", &dict_iter)); 143 144 for (state = NULL, key = pa_modargs_iterate(ma, &state); key; key = pa_modargs_iterate(ma, &state)) { 145 pa_assert_se(value = pa_modargs_get_value(ma, key, NULL)); 146 147 pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); 148 149 pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); 150 pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &value)); 151 152 pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); 153 } 154 155 pa_assert_se(dbus_message_iter_close_container(&variant_iter, &dict_iter)); 156 pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); 157 158 pa_modargs_free(ma); 159} 160 161static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata) { 162 pa_dbusiface_module *m = userdata; 163 DBusMessage *reply = NULL; 164 DBusMessageIter msg_iter; 165 166 pa_assert(conn); 167 pa_assert(msg); 168 pa_assert(m); 169 170 pa_assert_se(reply = dbus_message_new_method_return(msg)); 171 dbus_message_iter_init_append(reply, &msg_iter); 172 append_modargs_variant(&msg_iter, m); 173 pa_assert_se(dbus_connection_send(conn, reply, NULL)); 174 dbus_message_unref(reply); 175} 176 177static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata) { 178 pa_dbusiface_module *m = userdata; 179 int real_counter_value = -1; 180 dbus_uint32_t usage_counter = 0; 181 182 pa_assert(conn); 183 pa_assert(msg); 184 pa_assert(m); 185 186 if (!m->module->get_n_used || (real_counter_value = m->module->get_n_used(m->module)) < 0) { 187 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, 188 "Module %u (%s) doesn't have a usage counter.", m->module->index, m->module->name); 189 return; 190 } 191 192 usage_counter = real_counter_value; 193 194 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &usage_counter); 195} 196 197static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { 198 pa_dbusiface_module *m = userdata; 199 200 pa_assert(conn); 201 pa_assert(msg); 202 pa_assert(m); 203 204 pa_dbus_send_proplist_variant_reply(conn, msg, m->proplist); 205} 206 207static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { 208 pa_dbusiface_module *m = userdata; 209 DBusMessage *reply = NULL; 210 DBusMessageIter msg_iter; 211 DBusMessageIter dict_iter; 212 DBusMessageIter dict_entry_iter; 213 dbus_uint32_t idx = 0; 214 int real_counter_value = -1; 215 dbus_uint32_t usage_counter = 0; 216 217 pa_assert(conn); 218 pa_assert(msg); 219 pa_assert(m); 220 221 idx = m->module->index; 222 if (m->module->get_n_used && (real_counter_value = m->module->get_n_used(m->module)) >= 0) 223 usage_counter = real_counter_value; 224 225 pa_assert_se((reply = dbus_message_new_method_return(msg))); 226 227 dbus_message_iter_init_append(reply, &msg_iter); 228 pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); 229 230 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); 231 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &m->module->name); 232 233 pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); 234 pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name)); 235 append_modargs_variant(&dict_entry_iter, m); 236 pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); 237 238 if (real_counter_value >= 0) 239 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name, DBUS_TYPE_UINT32, &usage_counter); 240 241 pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, m->proplist); 242 243 pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); 244 245 pa_assert_se(dbus_connection_send(conn, reply, NULL)); 246 247 dbus_message_unref(reply); 248} 249 250static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata) { 251 pa_dbusiface_module *m = userdata; 252 253 pa_assert(conn); 254 pa_assert(msg); 255 pa_assert(m); 256 257 if (m->module->core->disallow_module_loading) { 258 pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module unloading."); 259 return; 260 } 261 262 pa_module_unload_request(m->module, false); 263 264 pa_dbus_send_empty_reply(conn, msg); 265} 266 267static pa_hook_result_t module_proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) { 268 pa_dbusiface_module *module_iface = slot_data; 269 pa_module * module = call_data; 270 DBusMessage *signal_msg; 271 272 pa_assert(module_iface); 273 pa_assert(module); 274 275 if (module_iface->module != module) 276 return PA_HOOK_OK; 277 278 if (!pa_proplist_equal(module_iface->proplist, module->proplist)) { 279 DBusMessageIter msg_iter; 280 281 pa_proplist_update(module_iface->proplist, PA_UPDATE_SET, module->proplist); 282 283 pa_assert_se(signal_msg = dbus_message_new_signal(module_iface->path, 284 PA_DBUSIFACE_MODULE_INTERFACE, 285 signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); 286 dbus_message_iter_init_append(signal_msg, &msg_iter); 287 pa_dbus_append_proplist(&msg_iter, module_iface->proplist); 288 289 pa_dbus_protocol_send_signal(module_iface->dbus_protocol, signal_msg); 290 dbus_message_unref(signal_msg); 291 } 292 293 return PA_HOOK_OK; 294} 295 296pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module) { 297 pa_dbusiface_module *m; 298 299 pa_assert(module); 300 301 m = pa_xnew0(pa_dbusiface_module, 1); 302 m->module = module; 303 m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index); 304 m->proplist = pa_proplist_copy(module->proplist); 305 m->dbus_protocol = pa_dbus_protocol_get(module->core); 306 m->module_proplist_changed_slot = pa_hook_connect(&module->core->hooks[PA_CORE_HOOK_MODULE_PROPLIST_CHANGED], 307 PA_HOOK_NORMAL, module_proplist_changed_cb, m); 308 309 pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &module_interface_info, m) >= 0); 310 311 return m; 312} 313 314void pa_dbusiface_module_free(pa_dbusiface_module *m) { 315 pa_assert(m); 316 317 pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, module_interface_info.name) >= 0); 318 319 pa_proplist_free(m->proplist); 320 pa_dbus_protocol_unref(m->dbus_protocol); 321 pa_hook_slot_free(m->module_proplist_changed_slot); 322 323 pa_xfree(m->path); 324 pa_xfree(m); 325} 326 327const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m) { 328 pa_assert(m); 329 330 return m->path; 331} 332