dbus-protocol: Connection handling for local connections.

This commit is contained in:
Tanu Kaskinen 2009-06-16 19:03:22 +03:00
parent 5babbaafb2
commit c8d819a5ad
11 changed files with 644 additions and 69 deletions

View file

@ -783,6 +783,7 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/core-scache.c pulsecore/core-scache.h \ pulsecore/core-scache.c pulsecore/core-scache.h \
pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
pulsecore/core.c pulsecore/core.h \ pulsecore/core.c pulsecore/core.h \
pulsecore/dbus-common.c pulsecore/dbus-common.h \
pulsecore/envelope.c pulsecore/envelope.h \ pulsecore/envelope.c pulsecore/envelope.h \
pulsecore/fdsem.c pulsecore/fdsem.h \ pulsecore/fdsem.c pulsecore/fdsem.h \
pulsecore/g711.c pulsecore/g711.h \ pulsecore/g711.c pulsecore/g711.h \
@ -796,6 +797,7 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
pulsecore/object.c pulsecore/object.h \ pulsecore/object.c pulsecore/object.h \
pulsecore/play-memblockq.c pulsecore/play-memblockq.h \ pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
pulsecore/play-memchunk.c pulsecore/play-memchunk.h \ pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
pulsecore/protocol-dbus.h \
pulsecore/resampler.c pulsecore/resampler.h \ pulsecore/resampler.c pulsecore/resampler.h \
pulsecore/rtpoll.c pulsecore/rtpoll.h \ pulsecore/rtpoll.c pulsecore/rtpoll.h \
pulsecore/rtsig.c pulsecore/rtsig.h \ pulsecore/rtsig.c pulsecore/rtsig.h \

View file

@ -28,6 +28,7 @@
#include <pulsecore/log.h> #include <pulsecore/log.h>
#include <pulsecore/macro.h> #include <pulsecore/macro.h>
#include <pulsecore/core.h>
#include <pulsecore/core-util.h> #include <pulsecore/core-util.h>
#ifdef HAVE_SYS_RESOURCE_H #ifdef HAVE_SYS_RESOURCE_H
@ -48,13 +49,6 @@ typedef enum pa_daemon_conf_cmd {
PA_CMD_CLEANUP_SHM PA_CMD_CLEANUP_SHM
} pa_daemon_conf_cmd_t; } pa_daemon_conf_cmd_t;
typedef enum pa_daemon_conf_server_type {
PA_SERVER_TYPE_UNSET,
PA_SERVER_TYPE_USER,
PA_SERVER_TYPE_SYSTEM,
PA_SERVER_TYPE_NONE
} pa_daemon_conf_server_type_t;
#ifdef HAVE_SYS_RESOURCE_H #ifdef HAVE_SYS_RESOURCE_H
typedef struct pa_rlimit { typedef struct pa_rlimit {
rlim_t value; rlim_t value;
@ -81,7 +75,7 @@ typedef struct pa_daemon_conf {
log_meta, log_meta,
log_time, log_time,
flat_volumes; flat_volumes;
pa_daemon_conf_server_type_t local_server_type; pa_server_type_t local_server_type;
int exit_idle_time, int exit_idle_time,
scache_idle_time, scache_idle_time,
auto_log_target, auto_log_target,

View file

@ -1028,6 +1028,7 @@ after_caps_setup:
c->running_as_daemon = !!conf->daemonize; c->running_as_daemon = !!conf->daemonize;
c->disallow_exit = conf->disallow_exit; c->disallow_exit = conf->disallow_exit;
c->flat_volumes = conf->flat_volumes; c->flat_volumes = conf->flat_volumes;
c->server_type = conf->local_server_type;
pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0); pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
pa_signal_new(SIGINT, signal_callback, c); pa_signal_new(SIGINT, signal_callback, c);
@ -1105,7 +1106,7 @@ after_caps_setup:
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
if (!conf->system_instance) { if (!conf->system_instance) {
if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type))) if (!(server_lookup = pa_dbusobj_server_lookup_new(c)))
goto finish; goto finish;
if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio"))) if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio")))
goto finish; goto finish;

View file

@ -28,16 +28,18 @@
#include <pulse/client-conf.h> #include <pulse/client-conf.h>
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulsecore/core.h>
#include <pulsecore/core-util.h> #include <pulsecore/core-util.h>
#include <pulsecore/dbus-common.h>
#include <pulsecore/dbus-shared.h> #include <pulsecore/dbus-shared.h>
#include <pulsecore/macro.h> #include <pulsecore/macro.h>
#include "server-lookup.h" #include "server-lookup.h"
struct pa_dbusobj_server_lookup { struct pa_dbusobj_server_lookup {
pa_core *core;
pa_dbus_connection *conn; pa_dbus_connection *conn;
pa_bool_t path_registered; pa_bool_t path_registered;
pa_daemon_conf_server_type_t server_type;
}; };
static const char introspection[] = static const char introspection[] =
@ -46,7 +48,7 @@ static const char introspection[] =
" <!-- If you are looking for documentation make sure to check out\n" " <!-- If you are looking for documentation make sure to check out\n"
" http://pulseaudio.org/wiki/DBusInterface -->\n" " http://pulseaudio.org/wiki/DBusInterface -->\n"
" <interface name=\"org.pulseaudio.ServerLookup\">" " <interface name=\"org.pulseaudio.ServerLookup\">"
" <method name=\"GetDBusServers\">" " <method name=\"GetDBusAddress\">"
" <arg name=\"result\" type=\"s\" direction=\"out\"/>" " <arg name=\"result\" type=\"s\" direction=\"out\"/>"
" </method>" " </method>"
" </interface>" " </interface>"
@ -99,40 +101,10 @@ oom:
return DBUS_HANDLER_RESULT_NEED_MEMORY; return DBUS_HANDLER_RESULT_NEED_MEMORY;
} }
/* Caller frees the string. */ static DBusHandlerResult handle_get_dbus_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) {
char *server_string = NULL;
char *runtime_dir = NULL;
switch (server_type) {
case PA_SERVER_TYPE_USER:
runtime_dir = pa_get_runtime_dir();
if (!runtime_dir)
return NULL;
server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir);
break;
case PA_SERVER_TYPE_SYSTEM:
server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket");
break;
case PA_SERVER_TYPE_NONE:
server_string = pa_xnew0(char, 1);
break;
default:
pa_assert_not_reached();
}
return server_string;
}
static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
DBusMessage *reply = NULL; DBusMessage *reply = NULL;
pa_client_conf *conf = NULL; pa_client_conf *conf = NULL;
char *server_string = NULL; char *address = NULL;
pa_assert(conn); pa_assert(conn);
pa_assert(msg); pa_assert(msg);
@ -148,12 +120,13 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa
return DBUS_HANDLER_RESULT_HANDLED; return DBUS_HANDLER_RESULT_HANDLED;
} }
server_string = pa_xstrdup(conf->default_dbus_server);
pa_client_conf_free(conf); pa_client_conf_free(conf);
if (!server_string) { if (conf->default_dbus_server) {
if (!(server_string = get_dbus_server_from_type(sl->server_type))) { if (!(address = dbus_address_escape_value(conf->default_dbus_server)))
goto oom;
} else {
if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) {
if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed."))) if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed.")))
goto fail; goto fail;
if (!dbus_connection_send(conn, reply, NULL)) if (!dbus_connection_send(conn, reply, NULL))
@ -165,15 +138,15 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa
if (!(reply = dbus_message_new_method_return(msg))) if (!(reply = dbus_message_new_method_return(msg)))
goto oom; goto oom;
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID)) if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID))
goto fail; goto fail;
if (!dbus_connection_send(conn, reply, NULL)) if (!dbus_connection_send(conn, reply, NULL))
goto oom; goto oom;
pa_log("Sent reply with server_string '%s'.", server_string); pa_log_debug("handle_get_dbus_address(): Sent reply with address '%s'.", address);
pa_xfree(server_string); pa_xfree(address);
dbus_message_unref(reply); dbus_message_unref(reply);
@ -183,7 +156,7 @@ fail:
if (conf) if (conf)
pa_client_conf_free(conf); pa_client_conf_free(conf);
pa_xfree(server_string); pa_xfree(address);
if (reply) if (reply)
dbus_message_unref(reply); dbus_message_unref(reply);
@ -194,7 +167,7 @@ oom:
if (conf) if (conf)
pa_client_conf_free(conf); pa_client_conf_free(conf);
pa_xfree(server_string); pa_xfree(address);
if (reply) if (reply)
dbus_message_unref(reply); dbus_message_unref(reply);
@ -214,8 +187,8 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void
if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
return handle_introspect(conn, msg, sl); return handle_introspect(conn, msg, sl);
if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers")) if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusAddress"))
return handle_get_dbus_servers(conn, msg, sl); return handle_get_dbus_address(conn, msg, sl);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
} }
@ -229,15 +202,15 @@ static DBusObjectPathVTable vtable = {
.dbus_internal_pad4 = NULL .dbus_internal_pad4 = NULL
}; };
pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type) { pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) {
pa_dbusobj_server_lookup *sl; pa_dbusobj_server_lookup *sl;
DBusError error; DBusError error;
dbus_error_init(&error); dbus_error_init(&error);
sl = pa_xnew(pa_dbusobj_server_lookup, 1); sl = pa_xnew(pa_dbusobj_server_lookup, 1);
sl->core = c;
sl->path_registered = FALSE; sl->path_registered = FALSE;
sl->server_type = server_type;
if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message); pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message);

View file

@ -32,11 +32,9 @@
#include <pulsecore/core.h> #include <pulsecore/core.h>
#include "daemon-conf.h"
typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup; typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type); pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c);
void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl); void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
#endif #endif

View file

@ -2,6 +2,8 @@
This file is part of PulseAudio. This file is part of PulseAudio.
Copyright 2009 Tanu Kaskinen Copyright 2009 Tanu Kaskinen
Copyright 2006 Lennart Poettering
Copyright 2006 Shams E. King
PulseAudio is free software; you can redistribute it and/or modify PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published it under the terms of the GNU Lesser General Public License as published
@ -23,37 +25,487 @@
#include <config.h> #include <config.h>
#endif #endif
#include <dbus/dbus.h>
#include <pulse/mainloop-api.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulsecore/client.h>
#include <pulsecore/core-util.h>
#include <pulsecore/dbus-common.h>
#include <pulsecore/dbus-util.h>
#include <pulsecore/idxset.h>
#include <pulsecore/macro.h> #include <pulsecore/macro.h>
#include <pulsecore/modargs.h>
#include <pulsecore/module.h> #include <pulsecore/module.h>
#include "module-dbus-protocol-symdef.h" #include "module-dbus-protocol-symdef.h"
PA_MODULE_DESCRIPTION("D-Bus interface"); PA_MODULE_DESCRIPTION("D-Bus interface");
PA_MODULE_USAGE("<no module arguments>"); PA_MODULE_USAGE(
"access=local|remote|local,remote "
"tcp_port=<port number>");
PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_AUTHOR("Tanu Kaskinen"); PA_MODULE_AUTHOR("Tanu Kaskinen");
PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_VERSION(PACKAGE_VERSION);
#define CLEANUP_INTERVAL 10 /* seconds */
struct server;
struct connection;
struct userdata { struct userdata {
pa_module *module; pa_module *module;
pa_bool_t local_access;
pa_bool_t remote_access;
uint32_t tcp_port;
struct server *local_server;
struct server *tcp_server;
pa_idxset *connections;
pa_time_event *cleanup_event;
}; };
int pa__init(pa_module *m) { struct server {
struct userdata *u = NULL; struct userdata *userdata;
DBusServer *dbus_server;
};
pa_assert(m); struct connection {
struct server *server;
pa_dbus_wrap_connection *wrap_conn;
pa_client *client;
};
m->userdata = u = pa_xnew0(struct userdata, 1); static const char* const valid_modargs[] = {
u->module = m; "access",
"tcp_port",
NULL
};
pa_log_notice("Hello, world!"); static void connection_free(struct connection *c) {
pa_assert(c);
pa_client_free(c->client);
pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
pa_dbus_wrap_connection_free(c->wrap_conn);
pa_xfree(c);
}
static void client_kill_cb(pa_client *c) {
struct connection *conn;
pa_assert(c);
pa_assert(c->userdata);
conn = c->userdata;
connection_free(conn);
pa_log_info("Connection killed.");
}
/* Called by D-Bus when a new client connection is received. */
static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
struct server *s = data;
struct connection *c;
pa_client_new_data new_data;
pa_client *client;
pa_assert(new_connection);
pa_assert(s);
pa_client_new_data_init(&new_data);
new_data.module = s->userdata->module;
new_data.driver = __FILE__;
pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: Fancier name. */
client = pa_client_new(s->userdata->module->core, &new_data);
pa_client_new_data_done(&new_data);
if (!client)
return;
c = pa_xnew(struct connection, 1);
c->server = s;
c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, new_connection);
c->client = client;
c->client->kill = client_kill_cb;
c->client->send_event = NULL;
c->client->userdata = c;
pa_idxset_put(s->userdata->connections, c, NULL);
}
/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
unsigned int flags = 0;
DBusWatch *watch = userdata;
#if HAVE_DBUS_WATCH_GET_UNIX_FD
pa_assert(fd == dbus_watch_get_unix_fd(watch));
#else
pa_assert(fd == dbus_watch_get_fd(watch));
#endif
if (!dbus_watch_get_enabled(watch)) {
pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
return;
}
if (events & PA_IO_EVENT_INPUT)
flags |= DBUS_WATCH_READABLE;
if (events & PA_IO_EVENT_OUTPUT)
flags |= DBUS_WATCH_WRITABLE;
if (events & PA_IO_EVENT_HANGUP)
flags |= DBUS_WATCH_HANGUP;
if (events & PA_IO_EVENT_ERROR)
flags |= DBUS_WATCH_ERROR;
dbus_watch_handle(watch, flags);
}
/* Called by PA mainloop when a D-Bus timer event needs handling. */
static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
DBusTimeout *timeout = userdata;
if (dbus_timeout_get_enabled(timeout)) {
struct timeval next = *tv;
dbus_timeout_handle(timeout);
/* restart it for the next scheduled time */
pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
mainloop->time_restart(e, &next);
}
}
/* Translates D-Bus fd watch event flags to PA IO event flags. */
static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
unsigned int flags;
pa_io_event_flags_t events = 0;
pa_assert(watch);
flags = dbus_watch_get_flags(watch);
/* no watch flags for disabled watches */
if (!dbus_watch_get_enabled(watch))
return PA_IO_EVENT_NULL;
if (flags & DBUS_WATCH_READABLE)
events |= PA_IO_EVENT_INPUT;
if (flags & DBUS_WATCH_WRITABLE)
events |= PA_IO_EVENT_OUTPUT;
return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
}
/* Called by D-Bus when a D-Bus fd watch event is added. */
static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
struct server *s = data;
pa_mainloop_api *mainloop;
pa_io_event *ev;
pa_assert(watch);
pa_assert(s);
mainloop = s->userdata->module->core->mainloop;
ev = mainloop->io_new(
mainloop,
#if HAVE_DBUS_WATCH_GET_UNIX_FD
dbus_watch_get_unix_fd(watch),
#else
dbus_watch_get_fd(watch),
#endif
get_watch_flags(watch), io_event_cb, watch);
dbus_watch_set_data(watch, ev, NULL);
return TRUE;
}
/* Called by D-Bus when a D-Bus fd watch event is removed. */
static void watch_remove_cb(DBusWatch *watch, void *data) {
struct server *s = data;
pa_io_event *ev;
pa_assert(watch);
pa_assert(s);
if ((ev = dbus_watch_get_data(watch)))
s->userdata->module->core->mainloop->io_free(ev);
}
/* Called by D-Bus when a D-Bus fd watch event is toggled. */
static void watch_toggled_cb(DBusWatch *watch, void *data) {
struct server *s = data;
pa_io_event *ev;
pa_assert(watch);
pa_assert(s);
pa_assert_se(ev = dbus_watch_get_data(watch));
/* get_watch_flags() checks if the watch is enabled */
s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
}
/* Called by D-Bus when a D-Bus timer event is added. */
static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
struct server *s = data;
pa_mainloop_api *mainloop;
pa_time_event *ev;
struct timeval tv;
pa_assert(timeout);
pa_assert(s);
if (!dbus_timeout_get_enabled(timeout))
return FALSE;
mainloop = s->userdata->module->core->mainloop;
pa_gettimeofday(&tv);
pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
dbus_timeout_set_data(timeout, ev, NULL);
return TRUE;
}
/* Called by D-Bus when a D-Bus timer event is removed. */
static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
struct server *s = data;
pa_time_event *ev;
pa_assert(timeout);
pa_assert(s);
if ((ev = dbus_timeout_get_data(timeout)))
s->userdata->module->core->mainloop->time_free(ev);
}
/* Called by D-Bus when a D-Bus timer event is toggled. */
static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
struct server *s = data;
pa_mainloop_api *mainloop;
pa_time_event *ev;
pa_assert(timeout);
pa_assert(s);
mainloop = s->userdata->module->core->mainloop;
pa_assert_se(ev = dbus_timeout_get_data(timeout));
if (dbus_timeout_get_enabled(timeout)) {
struct timeval tv;
pa_gettimeofday(&tv);
pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
mainloop->time_restart(ev, &tv);
} else
mainloop->time_restart(ev, NULL);
}
static void server_free(struct server *s) {
pa_assert(s);
if (s->dbus_server) {
dbus_server_disconnect(s->dbus_server);
dbus_server_unref(s->dbus_server);
}
pa_xfree(s);
}
static struct server *start_server(struct userdata *u, const char *address) {
/* XXX: We assume that when we unref the DBusServer instance at module
* shutdown, nobody else holds any references to it. If we stop assuming
* that someday, dbus_server_set_new_connection_function,
* dbus_server_set_watch_functions and dbus_server_set_timeout_functions
* calls should probably register free callbacks, instead of providing NULL
* as they do now. */
struct server *s = NULL;
DBusError error;
pa_assert(u);
pa_assert(address);
dbus_error_init(&error);
s = pa_xnew0(struct server, 1);
s->userdata = u;
s->dbus_server = dbus_server_listen(address, &error);
if (dbus_error_is_set(&error)) {
pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
goto fail;
}
dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
pa_log("dbus_server_set_watch_functions() ran out of memory.");
goto fail;
}
if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
pa_log("dbus_server_set_timeout_functions() ran out of memory.");
goto fail;
}
return s;
fail:
if (s)
server_free(s);
dbus_error_free(&error);
return NULL;
}
static struct server *start_local_server(struct userdata *u) {
struct server *s = NULL;
char *address = NULL;
pa_assert(u);
address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
s = start_server(u, address); /* May return NULL */
pa_xfree(address);
return s;
}
static struct server *start_tcp_server(struct userdata *u) {
pa_log("start_tcp_server(): Not implemented!");
return NULL;
}
static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) {
const char *value = NULL;
pa_assert(ma);
pa_assert(local_access);
pa_assert(remote_access);
if (!(value = pa_modargs_get_value(ma, "access", NULL)))
return 0;
if (!strcmp(value, "local")) {
*local_access = TRUE;
*remote_access = FALSE;
} else if (!strcmp(value, "remote")) {
*local_access = FALSE;
*remote_access = TRUE;
} else if (!strcmp(value, "local,remote")) {
*local_access = TRUE;
*local_access = TRUE;
} else
return -1;
return 0; return 0;
} }
void pa__done(pa_module*m) { /* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
struct userdata *u = userdata;
struct connection *conn = NULL;
uint32_t idx;
struct timeval cleanup_timeval;
unsigned free_count = 0;
for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) {
if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
connection_free(conn);
++free_count;
}
}
if (free_count > 0)
pa_log_debug("Freed %u dead D-Bus client connections.", free_count);
pa_gettimeofday(&cleanup_timeval);
cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
u->module->core->mainloop->time_restart(e, &cleanup_timeval);
}
int pa__init(pa_module *m) {
struct userdata *u = NULL;
pa_modargs *ma = NULL;
struct timeval cleanup_timeval;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
u->local_access = TRUE;
u->remote_access = FALSE;
u->tcp_port = PA_DBUS_DEFAULT_PORT;
if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
goto fail;
}
if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
goto fail;
}
if (u->local_access && !(u->local_server = start_local_server(u))) {
pa_log("Starting the local D-Bus server failed.");
goto fail;
}
if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
pa_log("Starting the D-Bus server for remote connections failed.");
goto fail;
}
u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
pa_gettimeofday(&cleanup_timeval);
cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
return 0;
fail:
if (ma)
pa_modargs_free(ma);
pa__done(m);
return -1;
}
/* Called by idxset when the connection set is freed. */
static void connection_free_cb(void *p, void *userdata) {
struct connection *conn = p;
pa_assert(conn);
connection_free(conn);
}
void pa__done(pa_module *m) {
struct userdata *u; struct userdata *u;
pa_assert(m); pa_assert(m);
@ -61,6 +513,18 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata)) if (!(u = m->userdata))
return; return;
if (u->cleanup_event)
m->core->mainloop->time_free(u->cleanup_event);
if (u->connections)
pa_idxset_free(u->connections, connection_free_cb, NULL);
if (u->tcp_server)
server_free(u->tcp_server);
if (u->local_server)
server_free(u->local_server);
pa_xfree(u); pa_xfree(u);
m->userdata = NULL; m->userdata = NULL;
} }

View file

@ -42,6 +42,13 @@ typedef struct pa_core pa_core;
#include <pulsecore/sink-input.h> #include <pulsecore/sink-input.h>
#include <pulsecore/msgobject.h> #include <pulsecore/msgobject.h>
typedef enum pa_server_type {
PA_SERVER_TYPE_UNSET,
PA_SERVER_TYPE_USER,
PA_SERVER_TYPE_SYSTEM,
PA_SERVER_TYPE_NONE
} pa_server_type_t;
typedef enum pa_core_state { typedef enum pa_core_state {
PA_CORE_STARTUP, PA_CORE_STARTUP,
PA_CORE_RUNNING, PA_CORE_RUNNING,
@ -152,6 +159,8 @@ struct pa_core {
pa_resample_method_t resample_method; pa_resample_method_t resample_method;
int realtime_priority; int realtime_priority;
pa_server_type_t server_type;
/* hooks */ /* hooks */
pa_hook hooks[PA_CORE_HOOK_MAX]; pa_hook hooks[PA_CORE_HOOK_MAX];
}; };

View file

@ -0,0 +1,73 @@
/***
This file is part of PulseAudio.
Copyright 2009 Tanu Kaskinen
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <dbus/dbus.h>
#include <pulsecore/core-util.h>
#include "dbus-common.h"
char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
char *address = NULL;
char *runtime_path = NULL;
char *escaped_path = NULL;
switch (server_type) {
case PA_SERVER_TYPE_USER:
if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) {
pa_log("pa_runtime_path() failed.");
break;
}
if (!(escaped_path = dbus_address_escape_value(runtime_path))) {
pa_log("dbus_address_escape_value() failed.");
break;
}
address = pa_sprintf_malloc("unix:path=%s", escaped_path);
break;
case PA_SERVER_TYPE_SYSTEM:
if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) {
pa_log("dbus_address_escape_value() failed.");
break;
}
address = pa_sprintf_malloc("unix:path=%s", escaped_path);
break;
case PA_SERVER_TYPE_NONE:
address = pa_xnew0(char, 1);
break;
default:
pa_assert_not_reached();
}
pa_xfree(runtime_path);
pa_xfree(escaped_path);
return address;
}

View file

@ -0,0 +1,39 @@
#ifndef foodbuscommonhfoo
#define foodbuscommonhfoo
/***
This file is part of PulseAudio.
Copyright 2009 Tanu Kaskinen
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/macro.h>
#define PA_DBUS_DEFAULT_PORT 24883
#define PA_DBUS_SOCKET_NAME "dbus_socket"
#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
/* Returns the default address of the server type in the escaped form. For
* PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
* string. This function may fail in some rare cases, in which case NULL is
* returned. */
char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
#endif

View file

@ -276,6 +276,27 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus
return pconn; return pconn;
} }
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) {
pa_dbus_wrap_connection *pconn;
pa_assert(m);
pa_assert(conn);
pconn = pa_xnew(pa_dbus_wrap_connection, 1);
pconn->mainloop = m;
pconn->connection = dbus_connection_ref(conn);
dbus_connection_set_exit_on_disconnect(conn, FALSE);
dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
return pconn;
}
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) { void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
pa_assert(c); pa_assert(c);

View file

@ -31,6 +31,7 @@
typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error); pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error);
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, DBusConnection *conn);
void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);