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-common.h>
37 #include <pulsecore/dbus-util.h>
38 #include <pulsecore/idxset.h>
39 #include <pulsecore/macro.h>
40 #include <pulsecore/modargs.h>
41 #include <pulsecore/module.h>
43 #include <pulsecore/dbus-objs/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 */
62 pa_bool_t local_access
;
63 pa_bool_t remote_access
;
66 struct server
*local_server
;
67 struct server
*tcp_server
;
69 pa_idxset
*connections
;
71 pa_time_event
*cleanup_event
;
73 pa_dbusobj_core
*core_object
;
77 struct userdata
*userdata
;
78 DBusServer
*dbus_server
;
82 struct server
*server
;
83 pa_dbus_wrap_connection
*wrap_conn
;
87 static const char* const valid_modargs
[] = {
93 static void connection_free(struct connection
*c
) {
96 pa_assert_se(pa_dbus_unregister_connection(c
->server
->userdata
->module
->core
, pa_dbus_wrap_connection_get(c
->wrap_conn
)) >= 0);
98 pa_client_free(c
->client
);
99 pa_assert_se(pa_idxset_remove_by_data(c
->server
->userdata
->connections
, c
, NULL
));
100 pa_dbus_wrap_connection_free(c
->wrap_conn
);
104 /* Called from pa_client_kill(). */
105 static void client_kill_cb(pa_client
*c
) {
106 struct connection
*conn
;
109 pa_assert(c
->userdata
);
112 connection_free(conn
);
114 pa_log_info("Connection killed.");
117 /* Called by D-Bus when a new client connection is received. */
118 static void connection_new_cb(DBusServer
*dbus_server
, DBusConnection
*new_connection
, void *data
) {
119 struct server
*s
= data
;
120 struct connection
*c
;
121 pa_client_new_data new_data
;
124 pa_assert(new_connection
);
127 pa_client_new_data_init(&new_data
);
128 new_data
.module
= s
->userdata
->module
;
129 new_data
.driver
= __FILE__
;
130 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? */
131 client
= pa_client_new(s
->userdata
->module
->core
, &new_data
);
132 pa_client_new_data_done(&new_data
);
137 c
= pa_xnew(struct connection
, 1);
139 c
->wrap_conn
= pa_dbus_wrap_connection_new_from_existing(s
->userdata
->module
->core
->mainloop
, new_connection
);
142 c
->client
->kill
= client_kill_cb
;
143 c
->client
->send_event
= NULL
; /* TODO: Implement this. */
144 c
->client
->userdata
= c
;
146 pa_idxset_put(s
->userdata
->connections
, c
, NULL
);
148 pa_assert_se(pa_dbus_register_connection(s
->userdata
->module
->core
, new_connection
) >= 0);
151 /* Called by PA mainloop when a D-Bus fd watch event needs handling. */
152 static void io_event_cb(pa_mainloop_api
*mainloop
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
153 unsigned int flags
= 0;
154 DBusWatch
*watch
= userdata
;
156 #if HAVE_DBUS_WATCH_GET_UNIX_FD
157 pa_assert(fd
== dbus_watch_get_unix_fd(watch
));
159 pa_assert(fd
== dbus_watch_get_fd(watch
));
162 if (!dbus_watch_get_enabled(watch
)) {
163 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch
, fd
);
167 if (events
& PA_IO_EVENT_INPUT
)
168 flags
|= DBUS_WATCH_READABLE
;
169 if (events
& PA_IO_EVENT_OUTPUT
)
170 flags
|= DBUS_WATCH_WRITABLE
;
171 if (events
& PA_IO_EVENT_HANGUP
)
172 flags
|= DBUS_WATCH_HANGUP
;
173 if (events
& PA_IO_EVENT_ERROR
)
174 flags
|= DBUS_WATCH_ERROR
;
176 dbus_watch_handle(watch
, flags
);
179 /* Called by PA mainloop when a D-Bus timer event needs handling. */
180 static void time_event_cb(pa_mainloop_api
*mainloop
, pa_time_event
* e
, const struct timeval
*tv
, void *userdata
) {
181 DBusTimeout
*timeout
= userdata
;
183 if (dbus_timeout_get_enabled(timeout
)) {
184 struct timeval next
= *tv
;
185 dbus_timeout_handle(timeout
);
187 /* restart it for the next scheduled time */
188 pa_timeval_add(&next
, (pa_usec_t
) dbus_timeout_get_interval(timeout
) * 1000);
189 mainloop
->time_restart(e
, &next
);
193 /* Translates D-Bus fd watch event flags to PA IO event flags. */
194 static pa_io_event_flags_t
get_watch_flags(DBusWatch
*watch
) {
196 pa_io_event_flags_t events
= 0;
200 flags
= dbus_watch_get_flags(watch
);
202 /* no watch flags for disabled watches */
203 if (!dbus_watch_get_enabled(watch
))
204 return PA_IO_EVENT_NULL
;
206 if (flags
& DBUS_WATCH_READABLE
)
207 events
|= PA_IO_EVENT_INPUT
;
208 if (flags
& DBUS_WATCH_WRITABLE
)
209 events
|= PA_IO_EVENT_OUTPUT
;
211 return events
| PA_IO_EVENT_HANGUP
| PA_IO_EVENT_ERROR
;
214 /* Called by D-Bus when a D-Bus fd watch event is added. */
215 static dbus_bool_t
watch_add_cb(DBusWatch
*watch
, void *data
) {
216 struct server
*s
= data
;
217 pa_mainloop_api
*mainloop
;
223 mainloop
= s
->userdata
->module
->core
->mainloop
;
225 ev
= mainloop
->io_new(
227 #if HAVE_DBUS_WATCH_GET_UNIX_FD
228 dbus_watch_get_unix_fd(watch
),
230 dbus_watch_get_fd(watch
),
232 get_watch_flags(watch
), io_event_cb
, watch
);
234 dbus_watch_set_data(watch
, ev
, NULL
);
239 /* Called by D-Bus when a D-Bus fd watch event is removed. */
240 static void watch_remove_cb(DBusWatch
*watch
, void *data
) {
241 struct server
*s
= data
;
247 if ((ev
= dbus_watch_get_data(watch
)))
248 s
->userdata
->module
->core
->mainloop
->io_free(ev
);
251 /* Called by D-Bus when a D-Bus fd watch event is toggled. */
252 static void watch_toggled_cb(DBusWatch
*watch
, void *data
) {
253 struct server
*s
= data
;
259 pa_assert_se(ev
= dbus_watch_get_data(watch
));
261 /* get_watch_flags() checks if the watch is enabled */
262 s
->userdata
->module
->core
->mainloop
->io_enable(ev
, get_watch_flags(watch
));
265 /* Called by D-Bus when a D-Bus timer event is added. */
266 static dbus_bool_t
timeout_add_cb(DBusTimeout
*timeout
, void *data
) {
267 struct server
*s
= data
;
268 pa_mainloop_api
*mainloop
;
275 if (!dbus_timeout_get_enabled(timeout
))
278 mainloop
= s
->userdata
->module
->core
->mainloop
;
280 pa_gettimeofday(&tv
);
281 pa_timeval_add(&tv
, (pa_usec_t
) dbus_timeout_get_interval(timeout
) * 1000);
283 ev
= mainloop
->time_new(mainloop
, &tv
, time_event_cb
, timeout
);
285 dbus_timeout_set_data(timeout
, ev
, NULL
);
290 /* Called by D-Bus when a D-Bus timer event is removed. */
291 static void timeout_remove_cb(DBusTimeout
*timeout
, void *data
) {
292 struct server
*s
= data
;
298 if ((ev
= dbus_timeout_get_data(timeout
)))
299 s
->userdata
->module
->core
->mainloop
->time_free(ev
);
302 /* Called by D-Bus when a D-Bus timer event is toggled. */
303 static void timeout_toggled_cb(DBusTimeout
*timeout
, void *data
) {
304 struct server
*s
= data
;
305 pa_mainloop_api
*mainloop
;
311 mainloop
= s
->userdata
->module
->core
->mainloop
;
313 pa_assert_se(ev
= dbus_timeout_get_data(timeout
));
315 if (dbus_timeout_get_enabled(timeout
)) {
318 pa_gettimeofday(&tv
);
319 pa_timeval_add(&tv
, (pa_usec_t
) dbus_timeout_get_interval(timeout
) * 1000);
321 mainloop
->time_restart(ev
, &tv
);
323 mainloop
->time_restart(ev
, NULL
);
326 static void server_free(struct server
*s
) {
329 if (s
->dbus_server
) {
330 dbus_server_disconnect(s
->dbus_server
);
331 dbus_server_unref(s
->dbus_server
);
337 static struct server
*start_server(struct userdata
*u
, const char *address
) {
338 /* XXX: We assume that when we unref the DBusServer instance at module
339 * shutdown, nobody else holds any references to it. If we stop assuming
340 * that someday, dbus_server_set_new_connection_function,
341 * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
342 * calls should probably register free callbacks, instead of providing NULL
345 struct server
*s
= NULL
;
351 dbus_error_init(&error
);
353 s
= pa_xnew0(struct server
, 1);
355 s
->dbus_server
= dbus_server_listen(address
, &error
);
357 if (dbus_error_is_set(&error
)) {
358 pa_log("dbus_server_listen() failed: %s: %s", error
.name
, error
.message
);
362 dbus_server_set_new_connection_function(s
->dbus_server
, connection_new_cb
, s
, NULL
);
364 if (!dbus_server_set_watch_functions(s
->dbus_server
, watch_add_cb
, watch_remove_cb
, watch_toggled_cb
, s
, NULL
)) {
365 pa_log("dbus_server_set_watch_functions() ran out of memory.");
369 if (!dbus_server_set_timeout_functions(s
->dbus_server
, timeout_add_cb
, timeout_remove_cb
, timeout_toggled_cb
, s
, NULL
)) {
370 pa_log("dbus_server_set_timeout_functions() ran out of memory.");
380 dbus_error_free(&error
);
385 static struct server
*start_local_server(struct userdata
*u
) {
386 struct server
*s
= NULL
;
387 char *address
= NULL
;
391 address
= pa_get_dbus_address_from_server_type(u
->module
->core
->server_type
);
393 s
= start_server(u
, address
); /* May return NULL */
400 static struct server
*start_tcp_server(struct userdata
*u
) {
401 pa_log("start_tcp_server(): Not implemented!");
405 static int get_access_arg(pa_modargs
*ma
, pa_bool_t
*local_access
, pa_bool_t
*remote_access
) {
406 const char *value
= NULL
;
409 pa_assert(local_access
);
410 pa_assert(remote_access
);
412 if (!(value
= pa_modargs_get_value(ma
, "access", NULL
)))
415 if (!strcmp(value
, "local")) {
416 *local_access
= TRUE
;
417 *remote_access
= FALSE
;
418 } else if (!strcmp(value
, "remote")) {
419 *local_access
= FALSE
;
420 *remote_access
= TRUE
;
421 } else if (!strcmp(value
, "local,remote")) {
422 *local_access
= TRUE
;
423 *local_access
= TRUE
;
430 /* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
431 static void cleanup_cb(pa_mainloop_api
*a
, pa_time_event
*e
, const struct timeval
*tv
, void *userdata
) {
432 struct userdata
*u
= userdata
;
433 struct connection
*conn
= NULL
;
435 struct timeval cleanup_timeval
;
436 unsigned free_count
= 0;
438 for (conn
= pa_idxset_first(u
->connections
, &idx
); conn
; conn
= pa_idxset_next(u
->connections
, &idx
)) {
439 if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn
->wrap_conn
))) {
440 connection_free(conn
);
446 pa_log_debug("Freed %u dead D-Bus client connections.", free_count
);
448 pa_gettimeofday(&cleanup_timeval
);
449 cleanup_timeval
.tv_sec
+= CLEANUP_INTERVAL
;
450 u
->module
->core
->mainloop
->time_restart(e
, &cleanup_timeval
);
453 int pa__init(pa_module
*m
) {
454 struct userdata
*u
= NULL
;
455 pa_modargs
*ma
= NULL
;
456 struct timeval cleanup_timeval
;
460 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
461 pa_log("Failed to parse module arguments.");
465 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
467 u
->local_access
= TRUE
;
468 u
->remote_access
= FALSE
;
469 u
->tcp_port
= PA_DBUS_DEFAULT_PORT
;
471 if (get_access_arg(ma
, &u
->local_access
, &u
->remote_access
) < 0) {
472 pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma
, "access", NULL
));
476 if (pa_modargs_get_value_u32(ma
, "tcp_port", &u
->tcp_port
) < 0 || u
->tcp_port
< 1 || u
->tcp_port
> 49150) {
477 pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma
, "tcp_port", NULL
));
481 if (u
->local_access
&& !(u
->local_server
= start_local_server(u
))) {
482 pa_log("Starting the local D-Bus server failed.");
486 if (u
->remote_access
&& !(u
->tcp_server
= start_tcp_server(u
))) {
487 pa_log("Starting the D-Bus server for remote connections failed.");
491 u
->connections
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
493 pa_gettimeofday(&cleanup_timeval
);
494 cleanup_timeval
.tv_sec
+= CLEANUP_INTERVAL
;
495 u
->cleanup_event
= m
->core
->mainloop
->time_new(m
->core
->mainloop
, &cleanup_timeval
, cleanup_cb
, u
);
497 u
->core_object
= pa_dbusobj_core_new(m
->core
);
510 /* Called by idxset when the connection set is freed. */
511 static void connection_free_cb(void *p
, void *userdata
) {
512 struct connection
*conn
= p
;
516 connection_free(conn
);
519 void pa__done(pa_module
*m
) {
524 if (!(u
= m
->userdata
))
528 pa_dbusobj_core_free(u
->core_object
);
530 if (u
->cleanup_event
)
531 m
->core
->mainloop
->time_free(u
->cleanup_event
);
534 pa_idxset_free(u
->connections
, connection_free_cb
, NULL
);
537 server_free(u
->tcp_server
);
540 server_free(u
->local_server
);