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