2 This file is part of PulseAudio.
4 Copyright 2009 Tanu Kaskinen
5 Copyright 2006 Lennart Poettering
6 Copyright 2006 Shams E. King
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.
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.
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
28 #include <dbus/dbus.h>
30 #include <pulse/mainloop-api.h>
31 #include <pulse/timeval.h>
32 #include <pulse/xmalloc.h>
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>
43 #include "iface-core.h"
45 #include "module-dbus-protocol-symdef.h"
47 PA_MODULE_DESCRIPTION("D-Bus interface");
49 "access=local|remote|local,remote "
50 "tcp_port=<port number>");
51 PA_MODULE_LOAD_ONCE(TRUE
);
52 PA_MODULE_AUTHOR("Tanu Kaskinen");
53 PA_MODULE_VERSION(PACKAGE_VERSION
);
55 #define CLEANUP_INTERVAL 10 /* seconds */
67 pa_bool_t local_access
;
68 pa_bool_t remote_access
;
71 struct server
*local_server
;
72 struct server
*tcp_server
;
74 pa_idxset
*connections
;
76 pa_time_event
*cleanup_event
;
78 pa_dbus_protocol
*dbus_protocol
;
79 pa_dbusiface_core
*core_iface
;
83 struct userdata
*userdata
;
84 enum server_type type
;
85 DBusServer
*dbus_server
;
89 struct server
*server
;
90 pa_dbus_wrap_connection
*wrap_conn
;
94 static const char* const valid_modargs
[] = {
100 static void connection_free(struct connection
*c
) {
103 pa_assert_se(pa_dbus_protocol_unregister_connection(c
->server
->userdata
->dbus_protocol
, pa_dbus_wrap_connection_get(c
->wrap_conn
)) >= 0);
105 pa_client_free(c
->client
);
106 pa_assert_se(pa_idxset_remove_by_data(c
->server
->userdata
->connections
, c
, NULL
));
107 pa_dbus_wrap_connection_free(c
->wrap_conn
);
111 /* Called from pa_client_kill(). */
112 static void client_kill_cb(pa_client
*c
) {
113 struct connection
*conn
;
116 pa_assert(c
->userdata
);
119 connection_free(conn
);
121 pa_log_info("Connection killed.");
124 static dbus_bool_t
user_check_cb(DBusConnection
*connection
, unsigned long uid
, void *data
) {
125 pa_log_debug("Allowing connection by user %lu.", uid
);
130 /* Called by D-Bus when a new client connection is received. */
131 static void connection_new_cb(DBusServer
*dbus_server
, DBusConnection
*new_connection
, void *data
) {
132 struct server
*s
= data
;
133 struct connection
*c
;
134 pa_client_new_data new_data
;
137 pa_assert(new_connection
);
140 pa_client_new_data_init(&new_data
);
141 new_data
.module
= s
->userdata
->module
;
142 new_data
.driver
= __FILE__
;
143 pa_proplist_sets(new_data
.proplist
, PA_PROP_APPLICATION_NAME
, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */
144 client
= pa_client_new(s
->userdata
->module
->core
, &new_data
);
145 pa_client_new_data_done(&new_data
);
148 dbus_connection_close(new_connection
);
152 if (s
->type
== SERVER_TYPE_TCP
|| s
->userdata
->module
->core
->server_type
== PA_SERVER_TYPE_SYSTEM
) {
153 /* FIXME: Here we allow anyone from anywhere to access the server,
154 * anonymously. Access control should be configurable. */
155 dbus_connection_set_unix_user_function(new_connection
, user_check_cb
, NULL
, NULL
);
156 dbus_connection_set_allow_anonymous(new_connection
, TRUE
);
159 c
= pa_xnew(struct connection
, 1);
161 c
->wrap_conn
= pa_dbus_wrap_connection_new_from_existing(s
->userdata
->module
->core
->mainloop
, TRUE
, new_connection
);
164 c
->client
->kill
= client_kill_cb
;
165 c
->client
->send_event
= NULL
; /* TODO: Implement this. */
166 c
->client
->userdata
= c
;
168 pa_idxset_put(s
->userdata
->connections
, c
, NULL
);
170 pa_assert_se(pa_dbus_protocol_register_connection(s
->userdata
->dbus_protocol
, new_connection
, c
->client
) >= 0);
173 /* Called by PA mainloop when a D-Bus fd watch event needs handling. */
174 static void io_event_cb(pa_mainloop_api
*mainloop
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
175 unsigned int flags
= 0;
176 DBusWatch
*watch
= userdata
;
178 #if HAVE_DBUS_WATCH_GET_UNIX_FD
179 pa_assert(fd
== dbus_watch_get_unix_fd(watch
));
181 pa_assert(fd
== dbus_watch_get_fd(watch
));
184 if (!dbus_watch_get_enabled(watch
)) {
185 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch
, fd
);
189 if (events
& PA_IO_EVENT_INPUT
)
190 flags
|= DBUS_WATCH_READABLE
;
191 if (events
& PA_IO_EVENT_OUTPUT
)
192 flags
|= DBUS_WATCH_WRITABLE
;
193 if (events
& PA_IO_EVENT_HANGUP
)
194 flags
|= DBUS_WATCH_HANGUP
;
195 if (events
& PA_IO_EVENT_ERROR
)
196 flags
|= DBUS_WATCH_ERROR
;
198 dbus_watch_handle(watch
, flags
);
201 /* Called by PA mainloop when a D-Bus timer event needs handling. */
202 static void time_event_cb(pa_mainloop_api
*mainloop
, pa_time_event
* e
, const struct timeval
*tv
, void *userdata
) {
203 DBusTimeout
*timeout
= userdata
;
205 if (dbus_timeout_get_enabled(timeout
)) {
206 struct timeval next
= *tv
;
207 dbus_timeout_handle(timeout
);
209 /* restart it for the next scheduled time */
210 pa_timeval_add(&next
, (pa_usec_t
) dbus_timeout_get_interval(timeout
) * 1000);
211 mainloop
->time_restart(e
, &next
);
215 /* Translates D-Bus fd watch event flags to PA IO event flags. */
216 static pa_io_event_flags_t
get_watch_flags(DBusWatch
*watch
) {
218 pa_io_event_flags_t events
= 0;
222 flags
= dbus_watch_get_flags(watch
);
224 /* no watch flags for disabled watches */
225 if (!dbus_watch_get_enabled(watch
))
226 return PA_IO_EVENT_NULL
;
228 if (flags
& DBUS_WATCH_READABLE
)
229 events
|= PA_IO_EVENT_INPUT
;
230 if (flags
& DBUS_WATCH_WRITABLE
)
231 events
|= PA_IO_EVENT_OUTPUT
;
233 return events
| PA_IO_EVENT_HANGUP
| PA_IO_EVENT_ERROR
;
236 /* Called by D-Bus when a D-Bus fd watch event is added. */
237 static dbus_bool_t
watch_add_cb(DBusWatch
*watch
, void *data
) {
238 struct server
*s
= data
;
239 pa_mainloop_api
*mainloop
;
245 mainloop
= s
->userdata
->module
->core
->mainloop
;
247 ev
= mainloop
->io_new(
249 #if HAVE_DBUS_WATCH_GET_UNIX_FD
250 dbus_watch_get_unix_fd(watch
),
252 dbus_watch_get_fd(watch
),
254 get_watch_flags(watch
), io_event_cb
, watch
);
256 dbus_watch_set_data(watch
, ev
, NULL
);
261 /* Called by D-Bus when a D-Bus fd watch event is removed. */
262 static void watch_remove_cb(DBusWatch
*watch
, void *data
) {
263 struct server
*s
= data
;
269 if ((ev
= dbus_watch_get_data(watch
)))
270 s
->userdata
->module
->core
->mainloop
->io_free(ev
);
273 /* Called by D-Bus when a D-Bus fd watch event is toggled. */
274 static void watch_toggled_cb(DBusWatch
*watch
, void *data
) {
275 struct server
*s
= data
;
281 pa_assert_se(ev
= dbus_watch_get_data(watch
));
283 /* get_watch_flags() checks if the watch is enabled */
284 s
->userdata
->module
->core
->mainloop
->io_enable(ev
, get_watch_flags(watch
));
287 /* Called by D-Bus when a D-Bus timer event is added. */
288 static dbus_bool_t
timeout_add_cb(DBusTimeout
*timeout
, void *data
) {
289 struct server
*s
= data
;
290 pa_mainloop_api
*mainloop
;
297 if (!dbus_timeout_get_enabled(timeout
))
300 mainloop
= s
->userdata
->module
->core
->mainloop
;
302 pa_gettimeofday(&tv
);
303 pa_timeval_add(&tv
, (pa_usec_t
) dbus_timeout_get_interval(timeout
) * 1000);
305 ev
= mainloop
->time_new(mainloop
, &tv
, time_event_cb
, timeout
);
307 dbus_timeout_set_data(timeout
, ev
, NULL
);
312 /* Called by D-Bus when a D-Bus timer event is removed. */
313 static void timeout_remove_cb(DBusTimeout
*timeout
, void *data
) {
314 struct server
*s
= data
;
320 if ((ev
= dbus_timeout_get_data(timeout
)))
321 s
->userdata
->module
->core
->mainloop
->time_free(ev
);
324 /* Called by D-Bus when a D-Bus timer event is toggled. */
325 static void timeout_toggled_cb(DBusTimeout
*timeout
, void *data
) {
326 struct server
*s
= data
;
327 pa_mainloop_api
*mainloop
;
333 mainloop
= s
->userdata
->module
->core
->mainloop
;
335 pa_assert_se(ev
= dbus_timeout_get_data(timeout
));
337 if (dbus_timeout_get_enabled(timeout
)) {
340 pa_gettimeofday(&tv
);
341 pa_timeval_add(&tv
, (pa_usec_t
) dbus_timeout_get_interval(timeout
) * 1000);
343 mainloop
->time_restart(ev
, &tv
);
345 mainloop
->time_restart(ev
, NULL
);
348 static void server_free(struct server
*s
) {
351 if (s
->dbus_server
) {
352 dbus_server_disconnect(s
->dbus_server
);
353 dbus_server_unref(s
->dbus_server
);
359 static struct server
*start_server(struct userdata
*u
, const char *address
, enum server_type type
) {
360 /* XXX: We assume that when we unref the DBusServer instance at module
361 * shutdown, nobody else holds any references to it. If we stop assuming
362 * that someday, dbus_server_set_new_connection_function,
363 * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
364 * calls should probably register free callbacks, instead of providing NULL
367 struct server
*s
= NULL
;
373 dbus_error_init(&error
);
375 s
= pa_xnew0(struct server
, 1);
377 s
->dbus_server
= dbus_server_listen(address
, &error
);
379 if (dbus_error_is_set(&error
)) {
380 pa_log("dbus_server_listen() failed: %s: %s", error
.name
, error
.message
);
384 dbus_server_set_new_connection_function(s
->dbus_server
, connection_new_cb
, s
, NULL
);
386 if (!dbus_server_set_watch_functions(s
->dbus_server
, watch_add_cb
, watch_remove_cb
, watch_toggled_cb
, s
, NULL
)) {
387 pa_log("dbus_server_set_watch_functions() ran out of memory.");
391 if (!dbus_server_set_timeout_functions(s
->dbus_server
, timeout_add_cb
, timeout_remove_cb
, timeout_toggled_cb
, s
, NULL
)) {
392 pa_log("dbus_server_set_timeout_functions() ran out of memory.");
402 dbus_error_free(&error
);
407 static struct server
*start_local_server(struct userdata
*u
) {
408 struct server
*s
= NULL
;
409 char *address
= NULL
;
413 address
= pa_get_dbus_address_from_server_type(u
->module
->core
->server_type
);
415 s
= start_server(u
, address
, SERVER_TYPE_LOCAL
); /* May return NULL */
422 static struct server
*start_tcp_server(struct userdata
*u
) {
423 struct server
*s
= NULL
;
424 char *address
= NULL
;
428 address
= pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u
->tcp_port
);
430 s
= start_server(u
, address
, SERVER_TYPE_TCP
); /* May return NULL */
437 static int get_access_arg(pa_modargs
*ma
, pa_bool_t
*local_access
, pa_bool_t
*remote_access
) {
438 const char *value
= NULL
;
441 pa_assert(local_access
);
442 pa_assert(remote_access
);
444 if (!(value
= pa_modargs_get_value(ma
, "access", NULL
)))
447 if (!strcmp(value
, "local")) {
448 *local_access
= TRUE
;
449 *remote_access
= FALSE
;
450 } else if (!strcmp(value
, "remote")) {
451 *local_access
= FALSE
;
452 *remote_access
= TRUE
;
453 } else if (!strcmp(value
, "local,remote")) {
454 *local_access
= TRUE
;
455 *remote_access
= TRUE
;
462 /* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
463 static void cleanup_cb(pa_mainloop_api
*a
, pa_time_event
*e
, const struct timeval
*tv
, void *userdata
) {
464 struct userdata
*u
= userdata
;
465 struct connection
*conn
= NULL
;
467 struct timeval cleanup_timeval
;
468 unsigned free_count
= 0;
470 for (conn
= pa_idxset_first(u
->connections
, &idx
); conn
; conn
= pa_idxset_next(u
->connections
, &idx
)) {
471 if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn
->wrap_conn
))) {
472 connection_free(conn
);
478 pa_log_debug("Freed %u dead D-Bus client connections.", free_count
);
480 pa_gettimeofday(&cleanup_timeval
);
481 cleanup_timeval
.tv_sec
+= CLEANUP_INTERVAL
;
482 u
->module
->core
->mainloop
->time_restart(e
, &cleanup_timeval
);
485 int pa__init(pa_module
*m
) {
486 struct userdata
*u
= NULL
;
487 pa_modargs
*ma
= NULL
;
488 struct timeval cleanup_timeval
;
492 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
493 pa_log("Failed to parse module arguments.");
497 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
499 u
->local_access
= TRUE
;
500 u
->remote_access
= FALSE
;
501 u
->tcp_port
= PA_DBUS_DEFAULT_PORT
;
503 if (get_access_arg(ma
, &u
->local_access
, &u
->remote_access
) < 0) {
504 pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma
, "access", NULL
));
508 if (pa_modargs_get_value_u32(ma
, "tcp_port", &u
->tcp_port
) < 0 || u
->tcp_port
< 1 || u
->tcp_port
> 49150) {
509 pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma
, "tcp_port", NULL
));
513 if (u
->local_access
&& !(u
->local_server
= start_local_server(u
))) {
514 pa_log("Starting the local D-Bus server failed.");
518 if (u
->remote_access
&& !(u
->tcp_server
= start_tcp_server(u
))) {
519 pa_log("Starting the D-Bus server for remote connections failed.");
523 u
->connections
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
525 pa_gettimeofday(&cleanup_timeval
);
526 cleanup_timeval
.tv_sec
+= CLEANUP_INTERVAL
;
527 u
->cleanup_event
= m
->core
->mainloop
->time_new(m
->core
->mainloop
, &cleanup_timeval
, cleanup_cb
, u
);
529 u
->dbus_protocol
= pa_dbus_protocol_get(m
->core
);
530 u
->core_iface
= pa_dbusiface_core_new(m
->core
);
543 /* Called by idxset when the connection set is freed. */
544 static void connection_free_cb(void *p
, void *userdata
) {
545 struct connection
*conn
= p
;
549 connection_free(conn
);
552 void pa__done(pa_module
*m
) {
557 if (!(u
= m
->userdata
))
561 pa_dbusiface_core_free(u
->core_iface
);
563 if (u
->cleanup_event
)
564 m
->core
->mainloop
->time_free(u
->cleanup_event
);
567 pa_idxset_free(u
->connections
, connection_free_cb
, NULL
);
570 server_free(u
->tcp_server
);
573 server_free(u
->local_server
);
575 if (u
->dbus_protocol
)
576 pa_dbus_protocol_unref(u
->dbus_protocol
);