1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Tanu Kaskinen 5 Copyright 2009 Vincent Filali-Ansary <filali.v@azurdigitalnetworks.net> 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published 9 by the Free Software Foundation; either version 2.1 of the License, 10 or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <dbus/dbus.h> 26 27#include <pulsecore/core-util.h> 28#include <pulsecore/dbus-util.h> 29#include <pulsecore/protocol-dbus.h> 30 31#include "iface-client.h" 32 33#define OBJECT_NAME "client" 34 35struct pa_dbusiface_client { 36 pa_dbusiface_core *core; 37 38 pa_client *client; 39 char *path; 40 pa_proplist *proplist; 41 42 pa_hook_slot *client_proplist_changed_slot; 43 44 pa_dbus_protocol *dbus_protocol; 45}; 46 47static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); 48static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); 49static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); 50static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); 51static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); 52static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); 53 54static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); 55 56static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); 57static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); 58static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); 59 60enum property_handler_index { 61 PROPERTY_HANDLER_INDEX, 62 PROPERTY_HANDLER_DRIVER, 63 PROPERTY_HANDLER_OWNER_MODULE, 64 PROPERTY_HANDLER_PLAYBACK_STREAMS, 65 PROPERTY_HANDLER_RECORD_STREAMS, 66 PROPERTY_HANDLER_PROPERTY_LIST, 67 PROPERTY_HANDLER_MAX 68}; 69 70static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { 71 [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, 72 [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, 73 [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, 74 [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao", .get_cb = handle_get_playback_streams, .set_cb = NULL }, 75 [PROPERTY_HANDLER_RECORD_STREAMS] = { .property_name = "RecordStreams", .type = "ao", .get_cb = handle_get_record_streams, .set_cb = NULL }, 76 [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } 77}; 78 79enum method_handler_index { 80 METHOD_HANDLER_KILL, 81 METHOD_HANDLER_UPDATE_PROPERTIES, 82 METHOD_HANDLER_REMOVE_PROPERTIES, 83 METHOD_HANDLER_MAX 84}; 85 86static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } }; 87static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } }; 88 89static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { 90 [METHOD_HANDLER_KILL] = { 91 .method_name = "Kill", 92 .arguments = NULL, 93 .n_arguments = 0, 94 .receive_cb = handle_kill }, 95 [METHOD_HANDLER_UPDATE_PROPERTIES] = { 96 .method_name = "UpdateProperties", 97 .arguments = update_properties_args, 98 .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), 99 .receive_cb = handle_update_properties }, 100 [METHOD_HANDLER_REMOVE_PROPERTIES] = { 101 .method_name = "RemoveProperties", 102 .arguments = remove_properties_args, 103 .n_arguments = sizeof(remove_properties_args) / sizeof(pa_dbus_arg_info), 104 .receive_cb = handle_remove_properties } 105}; 106 107enum signal_index { 108 SIGNAL_PROPERTY_LIST_UPDATED, 109 SIGNAL_CLIENT_EVENT, 110 SIGNAL_MAX 111}; 112 113static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; 114static pa_dbus_arg_info client_event_args[] = { { "name", "s", NULL }, 115 { "property_list", "a{say}", NULL } }; 116 117static pa_dbus_signal_info signals[SIGNAL_MAX] = { 118 [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }, 119 /* ClientEvent is sent from module-dbus-protocol.c. */ 120 [SIGNAL_CLIENT_EVENT] = { .name = "ClientEvent", .arguments = client_event_args, .n_arguments = 1 } 121}; 122 123static pa_dbus_interface_info client_interface_info = { 124 .name = PA_DBUSIFACE_CLIENT_INTERFACE, 125 .method_handlers = method_handlers, 126 .n_method_handlers = METHOD_HANDLER_MAX, 127 .property_handlers = property_handlers, 128 .n_property_handlers = PROPERTY_HANDLER_MAX, 129 .get_all_properties_cb = handle_get_all, 130 .signals = signals, 131 .n_signals = SIGNAL_MAX 132}; 133 134static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { 135 pa_dbusiface_client *c = userdata; 136 dbus_uint32_t idx = 0; 137 138 pa_assert(conn); 139 pa_assert(msg); 140 pa_assert(c); 141 142 idx = c->client->index; 143 144 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); 145} 146 147static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { 148 pa_dbusiface_client *c = userdata; 149 150 pa_assert(conn); 151 pa_assert(msg); 152 pa_assert(c); 153 154 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->client->driver); 155} 156 157static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { 158 pa_dbusiface_client *c = userdata; 159 const char *owner_module = NULL; 160 161 pa_assert(conn); 162 pa_assert(msg); 163 pa_assert(c); 164 165 if (!c->client->module) { 166 pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Client %d doesn't have an owner module.", c->client->index); 167 return; 168 } 169 170 owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module); 171 172 pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module); 173} 174 175/* The caller frees the array, but not the strings. */ 176static const char **get_playback_streams(pa_dbusiface_client *c, unsigned *n) { 177 const char **playback_streams = NULL; 178 unsigned i = 0; 179 uint32_t idx = 0; 180 pa_sink_input *sink_input = NULL; 181 182 pa_assert(c); 183 pa_assert(n); 184 185 *n = pa_idxset_size(c->client->sink_inputs); 186 187 if (*n == 0) 188 return NULL; 189 190 playback_streams = pa_xnew(const char *, *n); 191 192 PA_IDXSET_FOREACH(sink_input, c->client->sink_inputs, idx) 193 playback_streams[i++] = pa_dbusiface_core_get_playback_stream_path(c->core, sink_input); 194 195 return playback_streams; 196} 197 198static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { 199 pa_dbusiface_client *c = userdata; 200 const char **playback_streams = NULL; 201 unsigned n_playback_streams = 0; 202 203 pa_assert(conn); 204 pa_assert(msg); 205 pa_assert(c); 206 207 playback_streams = get_playback_streams(c, &n_playback_streams); 208 209 pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams); 210 211 pa_xfree(playback_streams); 212} 213 214/* The caller frees the array, but not the strings. */ 215static const char **get_record_streams(pa_dbusiface_client *c, unsigned *n) { 216 const char **record_streams = NULL; 217 unsigned i = 0; 218 uint32_t idx = 0; 219 pa_source_output *source_output = NULL; 220 221 pa_assert(c); 222 pa_assert(n); 223 224 *n = pa_idxset_size(c->client->source_outputs); 225 226 if (*n == 0) 227 return NULL; 228 229 record_streams = pa_xnew(const char *, *n); 230 231 PA_IDXSET_FOREACH(source_output, c->client->source_outputs, idx) 232 record_streams[i++] = pa_dbusiface_core_get_record_stream_path(c->core, source_output); 233 234 return record_streams; 235} 236 237static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { 238 pa_dbusiface_client *c = userdata; 239 const char **record_streams = NULL; 240 unsigned n_record_streams = 0; 241 242 pa_assert(conn); 243 pa_assert(msg); 244 pa_assert(c); 245 246 record_streams = get_record_streams(c, &n_record_streams); 247 248 pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams); 249 250 pa_xfree(record_streams); 251} 252 253static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { 254 pa_dbusiface_client *c = userdata; 255 256 pa_assert(conn); 257 pa_assert(msg); 258 pa_assert(c); 259 260 pa_dbus_send_proplist_variant_reply(conn, msg, c->client->proplist); 261} 262 263static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { 264 pa_dbusiface_client *c = userdata; 265 DBusMessage *reply = NULL; 266 DBusMessageIter msg_iter; 267 DBusMessageIter dict_iter; 268 dbus_uint32_t idx = 0; 269 const char *owner_module = NULL; 270 const char **playback_streams = NULL; 271 unsigned n_playback_streams = 0; 272 const char **record_streams = NULL; 273 unsigned n_record_streams = 0; 274 275 pa_assert(conn); 276 pa_assert(msg); 277 pa_assert(c); 278 279 idx = c->client->index; 280 if (c->client->module) 281 owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module); 282 playback_streams = get_playback_streams(c, &n_playback_streams); 283 record_streams = get_record_streams(c, &n_record_streams); 284 285 pa_assert_se((reply = dbus_message_new_method_return(msg))); 286 287 dbus_message_iter_init_append(reply, &msg_iter); 288 pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); 289 290 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); 291 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->client->driver); 292 293 if (owner_module) 294 pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module); 295 296 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); 297 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); 298 pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->client->proplist); 299 300 pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); 301 302 pa_assert_se(dbus_connection_send(conn, reply, NULL)); 303 304 dbus_message_unref(reply); 305 306 pa_xfree(playback_streams); 307 pa_xfree(record_streams); 308} 309 310static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) { 311 pa_dbusiface_client *c = userdata; 312 313 pa_assert(conn); 314 pa_assert(msg); 315 pa_assert(c); 316 317 dbus_connection_ref(conn); 318 319 pa_client_kill(c->client); 320 321 pa_dbus_send_empty_reply(conn, msg); 322 323 dbus_connection_unref(conn); 324} 325 326static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { 327 pa_dbusiface_client *c = userdata; 328 DBusMessageIter msg_iter; 329 pa_proplist *property_list = NULL; 330 dbus_uint32_t update_mode = 0; 331 332 pa_assert(conn); 333 pa_assert(msg); 334 pa_assert(c); 335 336 if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { 337 pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); 338 return; 339 } 340 341 pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); 342 343 if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) 344 return; 345 346 dbus_message_iter_get_basic(&msg_iter, &update_mode); 347 348 if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) { 349 pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode); 350 goto finish; 351 } 352 353 pa_client_update_proplist(c->client, update_mode, property_list); 354 355 pa_dbus_send_empty_reply(conn, msg); 356 357finish: 358 if (property_list) 359 pa_proplist_free(property_list); 360} 361 362static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { 363 pa_dbusiface_client *c = userdata; 364 char **keys = NULL; 365 int n_keys = 0; 366 bool changed = false; 367 int i = 0; 368 369 pa_assert(conn); 370 pa_assert(msg); 371 pa_assert(c); 372 373 if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { 374 pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); 375 return; 376 } 377 378 pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)); 379 380 for (i = 0; i < n_keys; ++i) 381 changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0; 382 383 pa_dbus_send_empty_reply(conn, msg); 384 385 if (changed) { 386 pa_hook_fire(&c->client->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c->client); 387 pa_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); 388 } 389 390 dbus_free_string_array(keys); 391} 392 393static pa_hook_result_t client_proplist_changed_cb(void *hook_data, void *call_data, void *slot_data) { 394 pa_dbusiface_client *c = slot_data; 395 pa_client *client = call_data; 396 DBusMessage *signal_msg; 397 398 pa_assert(c); 399 pa_assert(client); 400 401 if (c->client != client) 402 return PA_HOOK_OK; 403 404 if (!pa_proplist_equal(c->proplist, c->client->proplist)) { 405 DBusMessageIter msg_iter; 406 407 pa_proplist_update(c->proplist, PA_UPDATE_SET, c->client->proplist); 408 409 pa_assert_se(signal_msg = dbus_message_new_signal(c->path, 410 PA_DBUSIFACE_CLIENT_INTERFACE, 411 signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); 412 dbus_message_iter_init_append(signal_msg, &msg_iter); 413 pa_dbus_append_proplist(&msg_iter, c->proplist); 414 415 pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg); 416 dbus_message_unref(signal_msg); 417 } 418 419 return PA_HOOK_OK; 420} 421 422pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { 423 pa_dbusiface_client *c = NULL; 424 425 pa_assert(core); 426 pa_assert(client); 427 428 c = pa_xnew(pa_dbusiface_client, 1); 429 c->core = core; 430 c->client = client; 431 c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); 432 c->proplist = pa_proplist_copy(client->proplist); 433 c->dbus_protocol = pa_dbus_protocol_get(client->core); 434 c->client_proplist_changed_slot = pa_hook_connect(&client->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], 435 PA_HOOK_NORMAL, client_proplist_changed_cb, c); 436 437 pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0); 438 439 return c; 440} 441 442void pa_dbusiface_client_free(pa_dbusiface_client *c) { 443 pa_assert(c); 444 445 pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, client_interface_info.name) >= 0); 446 447 pa_hook_slot_free(c->client_proplist_changed_slot); 448 pa_proplist_free(c->proplist); 449 pa_dbus_protocol_unref(c->dbus_protocol); 450 451 pa_xfree(c->path); 452 pa_xfree(c); 453} 454 455const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c) { 456 pa_assert(c); 457 458 return c->path; 459} 460