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