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