1 /***
2 This file is part of PulseAudio.
3
4 Written by David Henningsson <david.henningsson@canonical.com>
5 Copyright 2010 Canonical Ltd.
6
7 Some code taken from other parts of PulseAudio, these are
8 Copyright 2006-2009 Lennart Poettering
9
10 PulseAudio is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published
12 by the Free Software Foundation; either version 2.1 of the License,
13 or (at your option) any later version.
14
15 PulseAudio is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public License
21 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <pulse/proplist.h>
29 #include <pulse/xmalloc.h>
30
31 #include <pulsecore/log.h>
32 #include <pulsecore/modargs.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/dbus-shared.h>
35 #include <pulsecore/strbuf.h>
36
37 PA_MODULE_AUTHOR("David Henningsson");
38 PA_MODULE_DESCRIPTION("Adds JACK sink/source ports when JACK is started");
39 PA_MODULE_LOAD_ONCE(false);
40 PA_MODULE_VERSION(PACKAGE_VERSION);
41 PA_MODULE_USAGE(
42 "channels=<number of channels> "
43 "sink_name=<name for the sink> "
44 "sink_properties=<properties for the card> "
45 "sink_client_name=<jack client name> "
46 "sink_channels=<number of channels> "
47 "sink_channel_map=<channel map> "
48 "source_name=<name for the source> "
49 "source_properties=<properties for the source> "
50 "source_client_name=<jack client name> "
51 "source_channels=<number of channels> "
52 "source_channel_map=<channel map> "
53 "connect=<connect ports?>");
54
55 #define JACK_SERVICE_NAME "org.jackaudio.service"
56 #define JACK_INTERFACE_NAME "org.jackaudio.JackControl"
57 #define JACK_INTERFACE_PATH "/org/jackaudio/Controller"
58
59 #define SERVICE_FILTER \
60 "type='signal'," \
61 "sender='" DBUS_SERVICE_DBUS "'," \
62 "interface='" DBUS_INTERFACE_DBUS "'," \
63 "member='NameOwnerChanged'," \
64 "arg0='" JACK_SERVICE_NAME "'"
65
66 #define RUNNING_FILTER(_a) \
67 "type='signal'," \
68 "sender='" JACK_SERVICE_NAME "'," \
69 "interface='" JACK_INTERFACE_NAME "'," \
70 "member='" _a "'"
71
72 static const char* const valid_modargs[] = {
73 "channels",
74 "sink_enabled",
75 "sink_name",
76 "sink_properties",
77 "sink_client_name",
78 "sink_channels",
79 "sink_channel_map",
80 "source_enabled",
81 "source_name",
82 "source_properties",
83 "source_client_name",
84 "source_channels",
85 "source_channel_map",
86 "connect",
87 NULL
88 };
89
90 #define JACK_SS_SINK 0
91 #define JACK_SS_SOURCE 1
92 #define JACK_SS_COUNT 2
93
94 static const char* const modnames[JACK_SS_COUNT] = {
95 "module-jack-sink",
96 "module-jack-source"
97 };
98
99 static const char* const modtypes[JACK_SS_COUNT] = {
100 "sink",
101 "source"
102 };
103
104 struct moddata {
105 bool enabled;
106 char *name;
107 pa_proplist *proplist;
108 char *client_name;
109 uint32_t channels;
110 pa_channel_map channel_map;
111 };
112
113 struct userdata {
114 pa_module *module;
115 pa_core *core;
116 pa_dbus_connection *connection;
117 bool filter_added, match_added;
118 bool is_service_started;
119 bool autoconnect_ports;
120 struct moddata mod_args[JACK_SS_COUNT];
121 /* Using index here protects us from module unloading without us knowing */
122 int jack_module_index[JACK_SS_COUNT];
123 };
124
ensure_ports_stopped(struct userdata* u)125 static void ensure_ports_stopped(struct userdata* u) {
126 unsigned i;
127 pa_assert(u);
128
129 for (i = 0; i < JACK_SS_COUNT; i++)
130 if (u->jack_module_index[i]) {
131 pa_module_unload_request_by_index(u->core, u->jack_module_index[i], true);
132 u->jack_module_index[i] = 0;
133 pa_log_info("Stopped %s.", modnames[i]);
134 }
135 }
136
proplist_to_arg(pa_proplist *p)137 static char* proplist_to_arg(pa_proplist *p) {
138 const char *key;
139 void *state = NULL;
140 pa_strbuf *buf;
141
142 pa_assert(p);
143
144 buf = pa_strbuf_new();
145
146 while ((key = pa_proplist_iterate(p, &state))) {
147 const char *v;
148 char *escaped;
149
150 if (!pa_strbuf_isempty(buf))
151 pa_strbuf_puts(buf, " ");
152
153 if ((v = pa_proplist_gets(p, key))) {
154 pa_strbuf_printf(buf, "%s=\"", key);
155
156 escaped = pa_escape(v, "\"'");
157 pa_strbuf_puts(buf, escaped);
158 pa_xfree(escaped);
159
160 pa_strbuf_puts(buf, "\"");
161 } else {
162 const void *value;
163 size_t nbytes;
164 char *c;
165
166 pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
167 c = pa_xmalloc(nbytes*2+1);
168 pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
169
170 pa_strbuf_printf(buf, "%s=hex:%s", key, c);
171 pa_xfree(c);
172 }
173 }
174
175 return pa_strbuf_to_string_free(buf);
176 }
177
ensure_ports_started(struct userdata* u)178 static void ensure_ports_started(struct userdata* u) {
179 unsigned i;
180 char *escaped;
181 pa_assert(u);
182
183 for (i = 0; i < JACK_SS_COUNT; i++)
184 if (u->mod_args[i].enabled && !u->jack_module_index[i]) {
185 pa_strbuf *args_buf = pa_strbuf_new();
186 char *args;
187 pa_module *m;
188 pa_strbuf_printf(args_buf, "connect=%s", pa_yes_no(u->autoconnect_ports));
189 if (u->mod_args[i].name) {
190 escaped = pa_escape(u->mod_args[i].name, "'");
191 pa_strbuf_printf(args_buf, " %s_name='%s'", modtypes[i], escaped);
192 pa_xfree(escaped);
193 }
194 if (!pa_proplist_isempty(u->mod_args[i].proplist)) {
195 escaped = proplist_to_arg(u->mod_args[i].proplist);
196 pa_strbuf_printf(args_buf, " %s_properties='%s'", modtypes[i], escaped);
197 pa_xfree(escaped);
198 }
199 if (u->mod_args[i].client_name) {
200 escaped = pa_escape(u->mod_args[i].client_name, "'");
201 pa_strbuf_printf(args_buf, " client_name='%s'", escaped);
202 pa_xfree(escaped);
203 }
204 if (u->mod_args[i].channels > 0)
205 pa_strbuf_printf(args_buf, " channels=%" PRIu32, u->mod_args[i].channels);
206 if (u->mod_args[i].channel_map.channels > 0) {
207 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
208 pa_channel_map_snprint(cm, sizeof(cm), &u->mod_args[i].channel_map);
209 pa_strbuf_printf(args_buf, " channel_map='%s'", cm);
210 }
211 args = pa_strbuf_to_string_free(args_buf);
212 pa_module_load(&m, u->core, modnames[i], args);
213 pa_xfree(args);
214
215 if (m) {
216 pa_log_info("Successfully started %s.", modnames[i]);
217 u->jack_module_index[i] = m->index;
218 }
219 else
220 pa_log_info("Failed to start %s.", modnames[i]);
221 }
222 }
223
check_service_started(struct userdata* u)224 static bool check_service_started(struct userdata* u) {
225 DBusError error;
226 DBusMessage *m = NULL, *reply = NULL;
227 bool new_status = false;
228 dbus_bool_t call_result;
229 pa_assert(u);
230
231 dbus_error_init(&error);
232
233 /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */
234 if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u->connection),
235 JACK_SERVICE_NAME, &error)) {
236 pa_log_debug("jackdbus isn't running.");
237 goto finish;
238 }
239
240 if (!(m = dbus_message_new_method_call(JACK_SERVICE_NAME, JACK_INTERFACE_PATH, JACK_INTERFACE_NAME, "IsStarted"))) {
241 pa_log("Failed to allocate IsStarted() method call.");
242 goto finish;
243 }
244
245 if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
246 pa_log("IsStarted() call failed: %s: %s", error.name, error.message);
247 goto finish;
248 }
249
250 if (!dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &call_result, DBUS_TYPE_INVALID)) {
251 pa_log("IsStarted() call return failed: %s: %s", error.name, error.message);
252 goto finish;
253 }
254
255 new_status = call_result;
256
257 finish:
258 if (m)
259 dbus_message_unref(m);
260 if (reply)
261 dbus_message_unref(reply);
262
263 dbus_error_free(&error);
264 if (new_status)
265 ensure_ports_started(u);
266 else
267 ensure_ports_stopped(u);
268 u->is_service_started = new_status;
269 return new_status;
270 }
271
dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata)272 static DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata) {
273 struct userdata *u = NULL;
274 DBusError error;
275
276 pa_assert(userdata);
277 u = ((pa_module*) userdata)->userdata;
278 pa_assert(u);
279
280 dbus_error_init(&error);
281
282 if (dbus_message_is_signal(s, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
283 const char *name, *old, *new;
284 if (!dbus_message_get_args(s, &error,
285 DBUS_TYPE_STRING, &name,
286 DBUS_TYPE_STRING, &old,
287 DBUS_TYPE_STRING, &new,
288 DBUS_TYPE_INVALID))
289 goto finish;
290 if (!pa_streq(name, JACK_SERVICE_NAME))
291 goto finish;
292
293 ensure_ports_stopped(u);
294 check_service_started(u);
295 }
296
297 else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStarted")) {
298 ensure_ports_stopped(u);
299 check_service_started(u);
300 }
301
302 else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStopped")) {
303 ensure_ports_stopped(u);
304 }
305
306 finish:
307 dbus_error_free(&error);
308 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
309 }
310
pa__init(pa_module *m)311 int pa__init(pa_module *m) {
312 DBusError error;
313 pa_dbus_connection *connection = NULL;
314 struct userdata *u = NULL;
315 pa_modargs *ma;
316 uint32_t channels = 0;
317 unsigned i;
318 char argname[32];
319 const char *name;
320
321 pa_assert(m);
322
323 dbus_error_init(&error);
324
325 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
326 pa_log("Failed to parse module arguments");
327 goto fail;
328 }
329
330 m->userdata = u = pa_xnew0(struct userdata, 1);
331 u->core = m->core;
332 u->module = m;
333 u->autoconnect_ports = true;
334
335 if (pa_modargs_get_value_boolean(ma, "connect", &u->autoconnect_ports) < 0) {
336 pa_log("Failed to parse connect= argument.");
337 goto fail;
338 }
339
340 if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || (channels > 0 && !pa_channels_valid(channels))) {
341 pa_log("Failed to parse channels= argument.");
342 goto fail;
343 }
344
345 for (i = 0; i < JACK_SS_COUNT; i++) {
346 u->mod_args[i].enabled = true;
347 pa_snprintf(argname, sizeof(argname), "%s_enabled", modtypes[i]);
348 if (pa_modargs_get_value_boolean(ma, argname, &u->mod_args[i].enabled) < 0) {
349 pa_log("Failed to parse %s= argument.", argname);
350 goto fail;
351 }
352
353 pa_snprintf(argname, sizeof(argname), "%s_name", modtypes[i]);
354 name = pa_modargs_get_value(ma, argname, NULL);
355 u->mod_args[i].name = pa_xstrdup(name);
356
357 u->mod_args[i].proplist = pa_proplist_new();
358 pa_snprintf(argname, sizeof(argname), "%s_properties", modtypes[i]);
359 if (pa_modargs_get_proplist(ma, argname, u->mod_args[i].proplist, PA_UPDATE_REPLACE) < 0) {
360 pa_log("Invalid %s properties", modtypes[i]);
361 goto fail;
362 }
363
364 pa_snprintf(argname, sizeof(argname), "%s_client_name", modtypes[i]);
365 name = pa_modargs_get_value(ma, argname, NULL);
366 u->mod_args[i].client_name = pa_xstrdup(name);
367
368 u->mod_args[i].channels = channels;
369 pa_snprintf(argname, sizeof(argname), "%s_channels", modtypes[i]);
370 if (pa_modargs_get_value_u32(ma, argname, &u->mod_args[i].channels) < 0
371 || (u->mod_args[i].channels > 0 && !pa_channels_valid(u->mod_args[i].channels))) {
372 pa_log("Failed to parse %s= argument.", argname);
373 goto fail;
374 }
375
376 pa_channel_map_init(&u->mod_args[i].channel_map);
377 pa_snprintf(argname, sizeof(argname), "%s_channel_map", modtypes[i]);
378 if (pa_modargs_get_value(ma, argname, NULL)) {
379 if (pa_modargs_get_channel_map(ma, argname, &u->mod_args[i].channel_map) < 0
380 || (u->mod_args[i].channels > 0 && u->mod_args[i].channel_map.channels != u->mod_args[i].channels)) {
381 pa_log("Failed to parse %s= argument.", argname);
382 goto fail;
383 }
384 }
385 }
386
387 if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
388
389 if (connection)
390 pa_dbus_connection_unref(connection);
391
392 pa_log_error("Unable to contact D-Bus session bus: %s: %s", error.name, error.message);
393 goto fail;
394 }
395 u->connection = connection;
396
397 if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), dbus_filter_handler, m, NULL)) {
398 pa_log_error("Unable to add D-Bus filter");
399 goto fail;
400 }
401 u->filter_added = 1;
402
403 if (pa_dbus_add_matches(
404 pa_dbus_connection_get(connection), &error, SERVICE_FILTER,
405 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL) < 0) {
406 pa_log_error("Unable to subscribe to signals: %s: %s", error.name, error.message);
407 goto fail;
408 }
409 u->match_added = 1;
410
411 check_service_started(u);
412
413 pa_modargs_free(ma);
414 return 0;
415
416 fail:
417 if (ma)
418 pa_modargs_free(ma);
419
420 dbus_error_free(&error);
421 pa__done(m);
422
423 return -1;
424 }
425
pa__done(pa_module *m)426 void pa__done(pa_module *m) {
427 struct userdata *u;
428 unsigned i;
429
430 pa_assert(m);
431
432 if (!(u = m->userdata))
433 return;
434
435 ensure_ports_stopped(u);
436
437 if (u->match_added) {
438 pa_dbus_remove_matches(
439 pa_dbus_connection_get(u->connection), SERVICE_FILTER,
440 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL);
441 }
442
443 if (u->filter_added) {
444 dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), dbus_filter_handler, m);
445 }
446
447 if (u->connection) {
448 pa_dbus_connection_unref(u->connection);
449 }
450
451 for (i = 0; i < JACK_SS_COUNT; i++) {
452 pa_xfree(u->mod_args[i].name);
453
454 if (u->mod_args[i].proplist)
455 pa_proplist_free(u->mod_args[i].proplist);
456
457 pa_xfree(u->mod_args[i].client_name);
458 }
459
460 pa_xfree(u);
461 }
462