• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/xmalloc.h>
29 
30 #include <pulsecore/log.h>
31 #include <pulsecore/modargs.h>
32 #include <pulsecore/core-util.h>
33 #include <pulsecore/dbus-shared.h>
34 
35 PA_MODULE_AUTHOR("David Henningsson");
36 PA_MODULE_DESCRIPTION("Adds JACK sink/source ports when JACK is started");
37 PA_MODULE_LOAD_ONCE(true);
38 PA_MODULE_VERSION(PACKAGE_VERSION);
39 PA_MODULE_USAGE(
40     "channels=<number of channels> "
41     "source_channels=<number of channels> "
42     "sink_channels=<number of channels> "
43     "connect=<connect ports?>");
44 
45 #define JACK_SERVICE_NAME "org.jackaudio.service"
46 #define JACK_INTERFACE_NAME "org.jackaudio.JackControl"
47 #define JACK_INTERFACE_PATH "/org/jackaudio/Controller"
48 
49 #define SERVICE_FILTER                          \
50         "type='signal',"                        \
51         "sender='" DBUS_SERVICE_DBUS "',"       \
52         "interface='" DBUS_INTERFACE_DBUS "',"  \
53         "member='NameOwnerChanged',"            \
54         "arg0='" JACK_SERVICE_NAME "'"
55 
56 #define RUNNING_FILTER(_a)                      \
57         "type='signal',"                        \
58         "sender='" JACK_SERVICE_NAME "',"       \
59         "interface='" JACK_INTERFACE_NAME "',"  \
60         "member='" _a "'"
61 
62 static const char* const valid_modargs[] = {
63     "channels",
64     "source_channels",
65     "sink_channels",
66     "connect",
67     NULL
68 };
69 
70 #define JACK_SS_SINK 0
71 #define JACK_SS_SOURCE 1
72 #define JACK_SS_COUNT 2
73 
74 static const char* const modnames[JACK_SS_COUNT] = {
75     "module-jack-sink",
76     "module-jack-source"
77 };
78 
79 struct userdata {
80     pa_module *module;
81     pa_core *core;
82     pa_dbus_connection *connection;
83     bool filter_added, match_added;
84     bool is_service_started;
85     bool autoconnect_ports;
86     uint32_t channels[JACK_SS_COUNT];
87     /* Using index here protects us from module unloading without us knowing */
88     int jack_module_index[JACK_SS_COUNT];
89 };
90 
ensure_ports_stopped(struct userdata * u)91 static void ensure_ports_stopped(struct userdata* u) {
92     int i;
93     pa_assert(u);
94 
95     for (i = 0; i < JACK_SS_COUNT; i++)
96         if (u->jack_module_index[i]) {
97             pa_module_unload_request_by_index(u->core, u->jack_module_index[i], true);
98             u->jack_module_index[i] = 0;
99             pa_log_info("Stopped %s.", modnames[i]);
100         }
101 }
102 
ensure_ports_started(struct userdata * u)103 static void ensure_ports_started(struct userdata* u) {
104     int i;
105     pa_assert(u);
106 
107     for (i = 0; i < JACK_SS_COUNT; i++)
108         if (!u->jack_module_index[i]) {
109             char* args;
110             pa_module* m;
111             if (u->channels[i] > 0) {
112                 args = pa_sprintf_malloc("connect=%s channels=%" PRIu32, pa_yes_no(u->autoconnect_ports), u->channels[i]);
113             } else {
114                 args = pa_sprintf_malloc("connect=%s", pa_yes_no(u->autoconnect_ports));
115             }
116             pa_module_load(&m, u->core, modnames[i], args);
117             pa_xfree(args);
118 
119             if (m) {
120                 pa_log_info("Successfully started %s.", modnames[i]);
121                 u->jack_module_index[i] = m->index;
122             }
123             else
124                 pa_log_info("Failed to start %s.", modnames[i]);
125         }
126 }
127 
check_service_started(struct userdata * u)128 static bool check_service_started(struct userdata* u) {
129     DBusError error;
130     DBusMessage *m = NULL, *reply = NULL;
131     bool new_status = false;
132     dbus_bool_t call_result;
133     pa_assert(u);
134 
135     dbus_error_init(&error);
136 
137     /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */
138     if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u->connection),
139             JACK_SERVICE_NAME, &error)) {
140         pa_log_debug("jackdbus isn't running.");
141         goto finish;
142     }
143 
144     if (!(m = dbus_message_new_method_call(JACK_SERVICE_NAME, JACK_INTERFACE_PATH, JACK_INTERFACE_NAME, "IsStarted"))) {
145         pa_log("Failed to allocate IsStarted() method call.");
146         goto finish;
147     }
148 
149     if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
150         pa_log("IsStarted() call failed: %s: %s", error.name, error.message);
151         goto finish;
152     }
153 
154     if (!dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &call_result, DBUS_TYPE_INVALID)) {
155         pa_log("IsStarted() call return failed: %s: %s", error.name, error.message);
156         goto finish;
157     }
158 
159     new_status = call_result;
160 
161 finish:
162     if (m)
163         dbus_message_unref(m);
164     if (reply)
165         dbus_message_unref(reply);
166 
167     dbus_error_free(&error);
168     if (new_status)
169         ensure_ports_started(u);
170     else
171         ensure_ports_stopped(u);
172     u->is_service_started = new_status;
173     return new_status;
174 }
175 
dbus_filter_handler(DBusConnection * c,DBusMessage * s,void * userdata)176 static DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata) {
177     struct userdata *u = NULL;
178     DBusError error;
179 
180     pa_assert(userdata);
181     u = ((pa_module*) userdata)->userdata;
182     pa_assert(u);
183 
184     dbus_error_init(&error);
185 
186     if (dbus_message_is_signal(s, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
187         const char *name, *old, *new;
188         if (!dbus_message_get_args(s, &error,
189                                    DBUS_TYPE_STRING, &name,
190                                    DBUS_TYPE_STRING, &old,
191                                    DBUS_TYPE_STRING, &new,
192                                    DBUS_TYPE_INVALID))
193             goto finish;
194         if (!pa_streq(name, JACK_SERVICE_NAME))
195             goto finish;
196 
197         ensure_ports_stopped(u);
198         check_service_started(u);
199     }
200 
201     else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStarted")) {
202         ensure_ports_stopped(u);
203         check_service_started(u);
204     }
205 
206     else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStopped")) {
207         ensure_ports_stopped(u);
208     }
209 
210 finish:
211     dbus_error_free(&error);
212     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
213 }
214 
pa__init(pa_module * m)215 int pa__init(pa_module *m) {
216     DBusError error;
217     pa_dbus_connection *connection = NULL;
218     struct userdata *u = NULL;
219     pa_modargs *ma;
220     uint32_t channels = 0;
221     int i;
222 
223     pa_assert(m);
224 
225     dbus_error_init(&error);
226 
227     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
228         pa_log("Failed to parse module arguments");
229         goto fail;
230     }
231 
232     m->userdata = u = pa_xnew0(struct userdata, 1);
233     u->core = m->core;
234     u->module = m;
235     u->autoconnect_ports = true;
236 
237     if (pa_modargs_get_value_boolean(ma, "connect", &u->autoconnect_ports) < 0) {
238         pa_log("Failed to parse connect= argument.");
239         goto fail;
240     }
241 
242     if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || (channels > 0 && !pa_channels_valid(channels))) {
243         pa_log("Failed to parse channels= argument.");
244         goto fail;
245     }
246     for (i = 0; i < JACK_SS_COUNT; i++) {
247         u->channels[i] = channels;
248     }
249 
250     if (pa_modargs_get_value_u32(ma, "source_channels", &u->channels[JACK_SS_SOURCE]) < 0 || (u->channels[JACK_SS_SOURCE] > 0 && !pa_channels_valid(u->channels[JACK_SS_SOURCE]))) {
251         pa_log("Failed to parse source_channels= argument.");
252         goto fail;
253     }
254 
255     if (pa_modargs_get_value_u32(ma, "sink_channels", &u->channels[JACK_SS_SINK]) < 0 || (u->channels[JACK_SS_SINK] > 0 && !pa_channels_valid(u->channels[JACK_SS_SINK]))) {
256         pa_log("Failed to parse sink_channels= argument.");
257         goto fail;
258     }
259 
260     if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
261 
262         if (connection)
263             pa_dbus_connection_unref(connection);
264 
265         pa_log_error("Unable to contact D-Bus session bus: %s: %s", error.name, error.message);
266         goto fail;
267     }
268     u->connection = connection;
269 
270     if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), dbus_filter_handler, m, NULL)) {
271         pa_log_error("Unable to add D-Bus filter");
272         goto fail;
273     }
274     u->filter_added = 1;
275 
276     if (pa_dbus_add_matches(
277                 pa_dbus_connection_get(connection), &error, SERVICE_FILTER,
278                 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL) < 0) {
279         pa_log_error("Unable to subscribe to signals: %s: %s", error.name, error.message);
280         goto fail;
281     }
282     u->match_added = 1;
283 
284     check_service_started(u);
285 
286     pa_modargs_free(ma);
287     return 0;
288 
289 fail:
290     if (ma)
291         pa_modargs_free(ma);
292 
293     dbus_error_free(&error);
294     pa__done(m);
295 
296     return -1;
297 }
298 
pa__done(pa_module * m)299 void pa__done(pa_module *m) {
300     struct userdata *u;
301 
302     pa_assert(m);
303 
304     if (!(u = m->userdata))
305         return;
306 
307     ensure_ports_stopped(u);
308 
309     if (u->match_added) {
310         pa_dbus_remove_matches(
311                 pa_dbus_connection_get(u->connection), SERVICE_FILTER,
312                 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL);
313     }
314 
315     if (u->filter_added) {
316         dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), dbus_filter_handler, m);
317     }
318 
319     if (u->connection) {
320         pa_dbus_connection_unref(u->connection);
321     }
322 
323     pa_xfree(u);
324 }
325