• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 struct 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 
47 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
48 static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
49 static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
50 static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
51 static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
52 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
53 
54 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
55 
56 static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
57 static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
58 static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
59 
60 enum 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 
70 static 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 
79 enum method_handler_index {
80     METHOD_HANDLER_KILL,
81     METHOD_HANDLER_UPDATE_PROPERTIES,
82     METHOD_HANDLER_REMOVE_PROPERTIES,
83     METHOD_HANDLER_MAX
84 };
85 
86 static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } };
87 static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } };
88 
89 static 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 
107 enum signal_index {
108     SIGNAL_PROPERTY_LIST_UPDATED,
109     SIGNAL_CLIENT_EVENT,
110     SIGNAL_MAX
111 };
112 
113 static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
114 static pa_dbus_arg_info client_event_args[]          = { { "name",          "s",      NULL },
115                                                          { "property_list", "a{say}", NULL } };
116 
117 static 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 
123 static 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 
handle_get_index(DBusConnection * conn,DBusMessage * msg,void * userdata)134 static 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 
handle_get_driver(DBusConnection * conn,DBusMessage * msg,void * userdata)147 static 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 
handle_get_owner_module(DBusConnection * conn,DBusMessage * msg,void * userdata)157 static 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. */
get_playback_streams(pa_dbusiface_client * c,unsigned * n)176 static 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 
handle_get_playback_streams(DBusConnection * conn,DBusMessage * msg,void * userdata)198 static 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. */
get_record_streams(pa_dbusiface_client * c,unsigned * n)215 static 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 
handle_get_record_streams(DBusConnection * conn,DBusMessage * msg,void * userdata)237 static 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 
handle_get_property_list(DBusConnection * conn,DBusMessage * msg,void * userdata)253 static 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 
handle_get_all(DBusConnection * conn,DBusMessage * msg,void * userdata)263 static 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 
handle_kill(DBusConnection * conn,DBusMessage * msg,void * userdata)310 static 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 
handle_update_properties(DBusConnection * conn,DBusMessage * msg,void * userdata)326 static 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 
357 finish:
358     if (property_list)
359         pa_proplist_free(property_list);
360 }
361 
handle_remove_properties(DBusConnection * conn,DBusMessage * msg,void * userdata)362 static 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 
client_proplist_changed_cb(void * hook_data,void * call_data,void * slot_data)393 static 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 
pa_dbusiface_client_new(pa_dbusiface_core * core,pa_client * client)422 pa_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 
pa_dbusiface_client_free(pa_dbusiface_client * c)442 void 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 
pa_dbusiface_client_get_path(pa_dbusiface_client * c)455 const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c) {
456     pa_assert(c);
457 
458     return c->path;
459 }
460