]> code.delx.au - pulseaudio/blob - src/modules/dbus/module-dbus-protocol.c
Revert dbus_bool_t variables to use TRUE/FALSE instead of true/false
[pulseaudio] / src / modules / dbus / module-dbus-protocol.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2009 Tanu Kaskinen
5 Copyright 2006 Lennart Poettering
6 Copyright 2006 Shams E. King
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2.1 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <dbus/dbus.h>
29
30 #include <pulse/mainloop-api.h>
31 #include <pulse/timeval.h>
32 #include <pulse/xmalloc.h>
33
34 #include <pulsecore/client.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/dbus-util.h>
37 #include <pulsecore/idxset.h>
38 #include <pulsecore/macro.h>
39 #include <pulsecore/modargs.h>
40 #include <pulsecore/module.h>
41 #include <pulsecore/protocol-dbus.h>
42
43 #include "iface-client.h"
44 #include "iface-core.h"
45
46 #include "module-dbus-protocol-symdef.h"
47
48 PA_MODULE_DESCRIPTION("D-Bus interface");
49 PA_MODULE_USAGE(
50 "access=local|remote|local,remote "
51 "tcp_port=<port number> "
52 "tcp_listen=<hostname>");
53 PA_MODULE_LOAD_ONCE(true);
54 PA_MODULE_AUTHOR("Tanu Kaskinen");
55 PA_MODULE_VERSION(PACKAGE_VERSION);
56
57 enum server_type {
58 SERVER_TYPE_LOCAL,
59 SERVER_TYPE_TCP
60 };
61
62 struct server;
63 struct connection;
64
65 struct userdata {
66 pa_module *module;
67 bool local_access;
68 bool remote_access;
69 uint32_t tcp_port;
70 char *tcp_listen;
71
72 struct server *local_server;
73 struct server *tcp_server;
74
75 pa_idxset *connections;
76
77 pa_defer_event *cleanup_event;
78
79 pa_dbus_protocol *dbus_protocol;
80 pa_dbusiface_core *core_iface;
81 };
82
83 struct server {
84 struct userdata *userdata;
85 enum server_type type;
86 DBusServer *dbus_server;
87 };
88
89 struct connection {
90 struct server *server;
91 pa_dbus_wrap_connection *wrap_conn;
92 pa_client *client;
93 };
94
95 static const char* const valid_modargs[] = {
96 "access",
97 "tcp_port",
98 "tcp_listen",
99 NULL
100 };
101
102 static void connection_free(struct connection *c) {
103 pa_assert(c);
104
105 pa_assert_se(pa_dbus_protocol_unregister_connection(c->server->userdata->dbus_protocol, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
106
107 pa_client_free(c->client);
108 pa_dbus_wrap_connection_free(c->wrap_conn);
109 pa_xfree(c);
110 }
111
112 /* Called from pa_client_kill(). */
113 static void client_kill_cb(pa_client *c) {
114 struct connection *conn;
115
116 pa_assert(c);
117 pa_assert(c->userdata);
118
119 conn = c->userdata;
120 pa_idxset_remove_by_data(conn->server->userdata->connections, conn, NULL);
121 connection_free(conn);
122 c->userdata = NULL;
123
124 pa_log_info("Connection killed.");
125 }
126
127 /* Called from pa_client_send_event(). */
128 static void client_send_event_cb(pa_client *c, const char *name, pa_proplist *data) {
129 struct connection *conn = NULL;
130 DBusMessage *signal_msg = NULL;
131 DBusMessageIter msg_iter;
132
133 pa_assert(c);
134 pa_assert(name);
135 pa_assert(data);
136 pa_assert(c->userdata);
137
138 conn = c->userdata;
139
140 pa_assert_se(signal_msg = dbus_message_new_signal(pa_dbusiface_core_get_client_path(conn->server->userdata->core_iface, c),
141 PA_DBUSIFACE_CLIENT_INTERFACE,
142 "ClientEvent"));
143 dbus_message_iter_init_append(signal_msg, &msg_iter);
144 pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
145 pa_dbus_append_proplist(&msg_iter, data);
146
147 pa_assert_se(dbus_connection_send(pa_dbus_wrap_connection_get(conn->wrap_conn), signal_msg, NULL));
148 dbus_message_unref(signal_msg);
149 }
150
151 /* Called by D-Bus at the authentication phase. */
152 static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
153 pa_log_debug("Allowing connection by user %lu.", uid);
154
155 return TRUE;
156 }
157
158 static DBusHandlerResult disconnection_filter_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
159 struct connection *c = user_data;
160
161 pa_assert(connection);
162 pa_assert(message);
163 pa_assert(c);
164
165 if (dbus_message_is_signal(message, "org.freedesktop.DBus.Local", "Disconnected")) {
166 /* The connection died. Now we want to free the connection object, but
167 * let's wait until this message is fully processed, in case someone
168 * else is interested in this signal too. */
169 c->server->userdata->module->core->mainloop->defer_enable(c->server->userdata->cleanup_event, 1);
170 }
171
172 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
173 }
174
175 /* Called by D-Bus when a new client connection is received. */
176 static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
177 struct server *s = data;
178 struct connection *c;
179 pa_client_new_data new_data;
180 pa_client *client;
181
182 pa_assert(new_connection);
183 pa_assert(s);
184
185 pa_client_new_data_init(&new_data);
186 new_data.module = s->userdata->module;
187 new_data.driver = __FILE__;
188 pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client");
189 client = pa_client_new(s->userdata->module->core, &new_data);
190 pa_client_new_data_done(&new_data);
191
192 if (!client) {
193 dbus_connection_close(new_connection);
194 return;
195 }
196
197 if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
198 /* FIXME: Here we allow anyone from anywhere to access the server,
199 * anonymously. Access control should be configurable. */
200 dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
201 dbus_connection_set_allow_anonymous(new_connection, TRUE);
202 }
203
204 c = pa_xnew(struct connection, 1);
205 c->server = s;
206 c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, true, new_connection);
207 c->client = client;
208
209 c->client->kill = client_kill_cb;
210 c->client->send_event = client_send_event_cb;
211 c->client->userdata = c;
212
213 pa_assert_se(dbus_connection_add_filter(new_connection, disconnection_filter_cb, c, NULL));
214
215 pa_idxset_put(s->userdata->connections, c, NULL);
216
217 pa_assert_se(pa_dbus_protocol_register_connection(s->userdata->dbus_protocol, new_connection, c->client) >= 0);
218 }
219
220 /* Called by PA mainloop when a D-Bus fd watch event needs handling. */
221 static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
222 unsigned int flags = 0;
223 DBusWatch *watch = userdata;
224
225 #if HAVE_DBUS_WATCH_GET_UNIX_FD
226 pa_assert(fd == dbus_watch_get_unix_fd(watch));
227 #else
228 pa_assert(fd == dbus_watch_get_fd(watch));
229 #endif
230
231 if (!dbus_watch_get_enabled(watch)) {
232 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
233 return;
234 }
235
236 if (events & PA_IO_EVENT_INPUT)
237 flags |= DBUS_WATCH_READABLE;
238 if (events & PA_IO_EVENT_OUTPUT)
239 flags |= DBUS_WATCH_WRITABLE;
240 if (events & PA_IO_EVENT_HANGUP)
241 flags |= DBUS_WATCH_HANGUP;
242 if (events & PA_IO_EVENT_ERROR)
243 flags |= DBUS_WATCH_ERROR;
244
245 dbus_watch_handle(watch, flags);
246 }
247
248 /* Called by PA mainloop when a D-Bus timer event needs handling. */
249 static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
250 DBusTimeout *timeout = userdata;
251
252 if (dbus_timeout_get_enabled(timeout)) {
253 struct timeval next = *tv;
254 dbus_timeout_handle(timeout);
255
256 /* restart it for the next scheduled time */
257 pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
258 mainloop->time_restart(e, &next);
259 }
260 }
261
262 /* Translates D-Bus fd watch event flags to PA IO event flags. */
263 static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
264 unsigned int flags;
265 pa_io_event_flags_t events = 0;
266
267 pa_assert(watch);
268
269 flags = dbus_watch_get_flags(watch);
270
271 /* no watch flags for disabled watches */
272 if (!dbus_watch_get_enabled(watch))
273 return PA_IO_EVENT_NULL;
274
275 if (flags & DBUS_WATCH_READABLE)
276 events |= PA_IO_EVENT_INPUT;
277 if (flags & DBUS_WATCH_WRITABLE)
278 events |= PA_IO_EVENT_OUTPUT;
279
280 return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
281 }
282
283 /* Called by D-Bus when a D-Bus fd watch event is added. */
284 static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
285 struct server *s = data;
286 pa_mainloop_api *mainloop;
287 pa_io_event *ev;
288
289 pa_assert(watch);
290 pa_assert(s);
291
292 mainloop = s->userdata->module->core->mainloop;
293
294 ev = mainloop->io_new(
295 mainloop,
296 #if HAVE_DBUS_WATCH_GET_UNIX_FD
297 dbus_watch_get_unix_fd(watch),
298 #else
299 dbus_watch_get_fd(watch),
300 #endif
301 get_watch_flags(watch), io_event_cb, watch);
302
303 dbus_watch_set_data(watch, ev, NULL);
304
305 return TRUE;
306 }
307
308 /* Called by D-Bus when a D-Bus fd watch event is removed. */
309 static void watch_remove_cb(DBusWatch *watch, void *data) {
310 struct server *s = data;
311 pa_io_event *ev;
312
313 pa_assert(watch);
314 pa_assert(s);
315
316 if ((ev = dbus_watch_get_data(watch)))
317 s->userdata->module->core->mainloop->io_free(ev);
318 }
319
320 /* Called by D-Bus when a D-Bus fd watch event is toggled. */
321 static void watch_toggled_cb(DBusWatch *watch, void *data) {
322 struct server *s = data;
323 pa_io_event *ev;
324
325 pa_assert(watch);
326 pa_assert(s);
327
328 pa_assert_se(ev = dbus_watch_get_data(watch));
329
330 /* get_watch_flags() checks if the watch is enabled */
331 s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
332 }
333
334 /* Called by D-Bus when a D-Bus timer event is added. */
335 static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
336 struct server *s = data;
337 pa_mainloop_api *mainloop;
338 pa_time_event *ev;
339 struct timeval tv;
340
341 pa_assert(timeout);
342 pa_assert(s);
343
344 if (!dbus_timeout_get_enabled(timeout))
345 return FALSE;
346
347 mainloop = s->userdata->module->core->mainloop;
348
349 pa_gettimeofday(&tv);
350 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
351
352 ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
353
354 dbus_timeout_set_data(timeout, ev, NULL);
355
356 return TRUE;
357 }
358
359 /* Called by D-Bus when a D-Bus timer event is removed. */
360 static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
361 struct server *s = data;
362 pa_time_event *ev;
363
364 pa_assert(timeout);
365 pa_assert(s);
366
367 if ((ev = dbus_timeout_get_data(timeout)))
368 s->userdata->module->core->mainloop->time_free(ev);
369 }
370
371 /* Called by D-Bus when a D-Bus timer event is toggled. */
372 static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
373 struct server *s = data;
374 pa_mainloop_api *mainloop;
375 pa_time_event *ev;
376
377 pa_assert(timeout);
378 pa_assert(s);
379
380 mainloop = s->userdata->module->core->mainloop;
381
382 pa_assert_se(ev = dbus_timeout_get_data(timeout));
383
384 if (dbus_timeout_get_enabled(timeout)) {
385 struct timeval tv;
386
387 pa_gettimeofday(&tv);
388 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
389
390 mainloop->time_restart(ev, &tv);
391 } else
392 mainloop->time_restart(ev, NULL);
393 }
394
395 static void server_free(struct server *s) {
396 pa_assert(s);
397
398 if (s->dbus_server) {
399 dbus_server_disconnect(s->dbus_server);
400 dbus_server_unref(s->dbus_server);
401 }
402
403 pa_xfree(s);
404 }
405
406 static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
407 /* XXX: We assume that when we unref the DBusServer instance at module
408 * shutdown, nobody else holds any references to it. If we stop assuming
409 * that someday, dbus_server_set_new_connection_function,
410 * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
411 * calls should probably register free callbacks, instead of providing NULL
412 * as they do now. */
413
414 struct server *s = NULL;
415 DBusError error;
416
417 pa_assert(u);
418 pa_assert(address);
419
420 dbus_error_init(&error);
421
422 s = pa_xnew0(struct server, 1);
423 s->userdata = u;
424 s->type = type;
425 s->dbus_server = dbus_server_listen(address, &error);
426
427 if (dbus_error_is_set(&error)) {
428 pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
429 goto fail;
430 }
431
432 dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
433
434 if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
435 pa_log("dbus_server_set_watch_functions() ran out of memory.");
436 goto fail;
437 }
438
439 if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
440 pa_log("dbus_server_set_timeout_functions() ran out of memory.");
441 goto fail;
442 }
443
444 return s;
445
446 fail:
447 if (s)
448 server_free(s);
449
450 dbus_error_free(&error);
451
452 return NULL;
453 }
454
455 static struct server *start_local_server(struct userdata *u) {
456 struct server *s = NULL;
457 char *address = NULL;
458
459 pa_assert(u);
460
461 address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
462
463 s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
464
465 pa_xfree(address);
466
467 return s;
468 }
469
470 static struct server *start_tcp_server(struct userdata *u) {
471 struct server *s = NULL;
472 char *address = NULL;
473
474 pa_assert(u);
475
476 address = pa_sprintf_malloc("tcp:host=%s,port=%u", u->tcp_listen, u->tcp_port);
477
478 s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
479
480 pa_xfree(address);
481
482 return s;
483 }
484
485 static int get_access_arg(pa_modargs *ma, bool *local_access, bool *remote_access) {
486 const char *value = NULL;
487
488 pa_assert(ma);
489 pa_assert(local_access);
490 pa_assert(remote_access);
491
492 if (!(value = pa_modargs_get_value(ma, "access", NULL)))
493 return 0;
494
495 if (pa_streq(value, "local")) {
496 *local_access = true;
497 *remote_access = false;
498 } else if (pa_streq(value, "remote")) {
499 *local_access = false;
500 *remote_access = true;
501 } else if (pa_streq(value, "local,remote")) {
502 *local_access = true;
503 *remote_access = true;
504 } else
505 return -1;
506
507 return 0;
508 }
509
510 /* Frees dead client connections. */
511 static void cleanup_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
512 struct userdata *u = userdata;
513 struct connection *conn = NULL;
514 uint32_t idx;
515
516 PA_IDXSET_FOREACH(conn, u->connections, idx) {
517 if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
518 pa_idxset_remove_by_data(u->connections, conn, NULL);
519 connection_free(conn);
520 }
521 }
522
523 u->module->core->mainloop->defer_enable(e, 0);
524 }
525
526 int pa__init(pa_module *m) {
527 struct userdata *u = NULL;
528 pa_modargs *ma = NULL;
529
530 pa_assert(m);
531
532 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
533 pa_log("Failed to parse module arguments.");
534 goto fail;
535 }
536
537 m->userdata = u = pa_xnew0(struct userdata, 1);
538 u->module = m;
539 u->local_access = true;
540 u->remote_access = false;
541 u->tcp_port = PA_DBUS_DEFAULT_PORT;
542
543 if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
544 pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
545 goto fail;
546 }
547
548 if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
549 pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
550 goto fail;
551 }
552
553 u->tcp_listen = pa_xstrdup(pa_modargs_get_value(ma, "tcp_listen", "0.0.0.0"));
554
555 if (u->local_access && !(u->local_server = start_local_server(u))) {
556 pa_log("Starting the local D-Bus server failed.");
557 goto fail;
558 }
559
560 if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
561 pa_log("Starting the D-Bus server for remote connections failed.");
562 goto fail;
563 }
564
565 u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
566
567 u->cleanup_event = m->core->mainloop->defer_new(m->core->mainloop, cleanup_cb, u);
568 m->core->mainloop->defer_enable(u->cleanup_event, 0);
569
570 u->dbus_protocol = pa_dbus_protocol_get(m->core);
571 u->core_iface = pa_dbusiface_core_new(m->core);
572
573 pa_modargs_free(ma);
574
575 return 0;
576
577 fail:
578 if (ma)
579 pa_modargs_free(ma);
580
581 pa__done(m);
582
583 return -1;
584 }
585
586 void pa__done(pa_module *m) {
587 struct userdata *u;
588
589 pa_assert(m);
590
591 if (!(u = m->userdata))
592 return;
593
594 if (u->core_iface)
595 pa_dbusiface_core_free(u->core_iface);
596
597 if (u->connections)
598 pa_idxset_free(u->connections, (pa_free_cb_t) connection_free);
599
600 /* This must not be called before the connections are freed, because if
601 * there are any connections left, they will emit the
602 * org.freedesktop.DBus.Local.Disconnected signal, and
603 * disconnection_filter_cb() will be called. disconnection_filter_cb() then
604 * tries to enable the defer event, and if it's already freed, an assertion
605 * will be hit in mainloop.c. */
606 if (u->cleanup_event)
607 m->core->mainloop->defer_free(u->cleanup_event);
608
609 if (u->tcp_server)
610 server_free(u->tcp_server);
611
612 if (u->local_server)
613 server_free(u->local_server);
614
615 if (u->dbus_protocol)
616 pa_dbus_protocol_unref(u->dbus_protocol);
617
618 pa_xfree(u->tcp_listen);
619 pa_xfree(u);
620 m->userdata = NULL;
621 }