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