From 3af5f8cb553e512305c1f3603b57424c36c9c82c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Jun 2009 19:03:16 +0200 Subject: reserve: wrap device reservation monitor reference implementation --- src/Makefile.am | 4 +- src/modules/reserve-monitor.c | 259 ++++++++++++++++++++++++++++++++++++++++++ src/modules/reserve-monitor.h | 62 ++++++++++ src/modules/reserve-wrap.c | 149 +++++++++++++++++++++++- src/modules/reserve-wrap.h | 9 +- 5 files changed, 479 insertions(+), 4 deletions(-) create mode 100644 src/modules/reserve-monitor.c create mode 100644 src/modules/reserve-monitor.h diff --git a/src/Makefile.am b/src/Makefile.am index a7ec6917..c0e1806f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1358,7 +1358,7 @@ libalsa_util_la_CFLAGS += $(UDEV_CFLAGS) endif if HAVE_DBUS -libalsa_util_la_SOURCES += modules/reserve.h modules/reserve.c +libalsa_util_la_SOURCES += modules/reserve.h modules/reserve.c modules/reserve-monitor.h modules/reserve-monitor.c libalsa_util_la_LIBADD += $(DBUS_LIBS) libalsa_util_la_CFLAGS += $(DBUS_CFLAGS) endif @@ -1670,7 +1670,7 @@ update-sbc: done update-reserve: - for i in reserve.c reserve.h ; do \ + for i in reserve.c reserve.h reserve-monitor.c reserve-monitor.h ; do \ wget -O modules/$$i http://git.0pointer.de/\?p=reserve.git\;a=blob_plain\;f=$$i\;hb=master ; \ done diff --git a/src/modules/reserve-monitor.c b/src/modules/reserve-monitor.c new file mode 100644 index 00000000..64d2a7cc --- /dev/null +++ b/src/modules/reserve-monitor.c @@ -0,0 +1,259 @@ +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include +#include +#include +#include +#include + +#include "reserve-monitor.h" + +struct rm_monitor { + int ref; + + char *device_name; + char *service_name; + + DBusConnection *connection; + + unsigned busy:1; + unsigned filtering:1; + unsigned matching:1; + + rm_change_cb_t change_cb; + void *userdata; +}; + +#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1." + +static DBusHandlerResult filter_handler( + DBusConnection *c, + DBusMessage *s, + void *userdata) { + + DBusMessage *reply; + rm_monitor *m; + DBusError error; + + dbus_error_init(&error); + + m = userdata; + assert(m->ref >= 1); + + if (dbus_message_is_signal(s, "org.freedesktop.DBus", "NameOwnerChanged")) { + const char *name, *old, *new; + + if (!dbus_message_get_args( + s, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) + goto invalid; + + if (strcmp(name, m->service_name) == 0) { + + m->busy = !!(new && *new); + + if (m->change_cb) { + m->ref++; + m->change_cb(m); + rm_release(m); + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +invalid: + if (!(reply = dbus_message_new_error( + s, + DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"))) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +int rm_watch( + rm_monitor **_m, + DBusConnection *connection, + const char*device_name, + rm_change_cb_t change_cb, + DBusError *error) { + + rm_monitor *m = NULL; + int r; + DBusError _error; + + if (!error) + error = &_error; + + dbus_error_init(error); + + if (!_m) + return -EINVAL; + + if (!connection) + return -EINVAL; + + if (!device_name) + return -EINVAL; + + if (!(m = calloc(sizeof(rm_monitor), 1))) + return -ENOMEM; + + m->ref = 1; + + if (!(m->device_name = strdup(device_name))) { + r = -ENOMEM; + goto fail; + } + + m->connection = dbus_connection_ref(connection); + m->change_cb = change_cb; + + if (!(m->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) { + r = -ENOMEM; + goto fail; + } + sprintf(m->service_name, SERVICE_PREFIX "%s", m->device_name); + + if (!(dbus_connection_add_filter(m->connection, filter_handler, m, NULL))) { + r = -ENOMEM; + goto fail; + } + + m->filtering = 1; + + dbus_bus_add_match(m->connection, + "type='signal'," + "sender='" DBUS_SERVICE_DBUS "'," + "interface='" DBUS_INTERFACE_DBUS "'," + "member='NameOwnerChanged'", error); + + if (dbus_error_is_set(error)) { + r = -EIO; + goto fail; + } + + m->matching = 1; + + m->busy = dbus_bus_name_has_owner(m->connection, m->service_name, error); + + if (dbus_error_is_set(error)) { + r = -EIO; + goto fail; + } + + *_m = m; + return 0; + +fail: + if (&_error == error) + dbus_error_free(&_error); + + if (m) + rm_release(m); + + return r; +} + +void rm_release(rm_monitor *m) { + if (!m) + return; + + assert(m->ref > 0); + + if (--m->ref > 0) + return; + + if (m->matching) + dbus_bus_remove_match( + m->connection, + "type='signal'," + "sender='" DBUS_SERVICE_DBUS "'," + "interface='" DBUS_INTERFACE_DBUS "'," + "member='NameOwnerChanged'", NULL); + + if (m->filtering) + dbus_connection_remove_filter( + m->connection, + filter_handler, + m); + + free(m->device_name); + free(m->service_name); + + if (m->connection) + dbus_connection_unref(m->connection); + + free(m); +} + +int rm_busy(rm_monitor *m) { + if (!m) + return -EINVAL; + + assert(m->ref > 0); + + return m->busy; +} + +void rm_set_userdata(rm_monitor *m, void *userdata) { + + if (!m) + return; + + assert(m->ref > 0); + m->userdata = userdata; +} + +void* rm_get_userdata(rm_monitor *m) { + + if (!m) + return NULL; + + assert(m->ref > 0); + + return m->userdata; +} diff --git a/src/modules/reserve-monitor.h b/src/modules/reserve-monitor.h new file mode 100644 index 00000000..4f4a8332 --- /dev/null +++ b/src/modules/reserve-monitor.h @@ -0,0 +1,62 @@ +#ifndef fooreservemonitorhfoo +#define fooreservemonitorhfoo + +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include + +typedef struct rm_monitor rm_monitor; + +/* Prototype for a function that is called whenever the reservation + * device of a device changes. Use rm_monitor_busy() to find out the + * new state.*/ +typedef void (*rm_change_cb_t)(rm_monitor *m); + +/* Creates a monitor for watching the lock status of a device. Returns + * 0 on success, a negative errno style return value on error. The + * DBus error might be set as well if the error was caused D-Bus. */ +int rm_watch( + rm_monitor **m, /* On success a pointer to the newly allocated rm_device object will be filled in here */ + DBusConnection *connection, /* Session bus (when D-Bus learns about user busses we should switchg to user busses) */ + const char *device_name, /* The device to monitor, e.g. "Audio0" */ + rm_change_cb_t change_cb, /* Will be called whenever the lock status changes. May be NULL */ + DBusError *error); /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */ + +/* Free a rm_monitor object */ +void rm_release(rm_monitor *m); + +/* Checks whether the device is currently reserved, and returns 1 + * then, 0 if not, negative errno style error code value on error. */ +int rm_busy(rm_monitor *m); + +/* Attach a userdata pointer to an rm_monitor */ +void rm_set_userdata(rm_monitor *m, void *userdata); + +/* Query the userdata pointer from an rm_monitor. Returns NULL if no + * userdata was set. */ +void* rm_get_userdata(rm_monitor *m); + +#endif diff --git a/src/modules/reserve-wrap.c b/src/modules/reserve-wrap.c index d0d014d3..07b592d3 100644 --- a/src/modules/reserve-wrap.c +++ b/src/modules/reserve-wrap.c @@ -35,6 +35,7 @@ #ifdef HAVE_DBUS #include #include "reserve.h" +#include "reserve-monitor.h" #endif #include "reserve-wrap.h" @@ -50,6 +51,17 @@ struct pa_reserve_wrapper { #endif }; +struct pa_reserve_monitor_wrapper { + PA_REFCNT_DECLARE; + pa_core *core; + pa_hook hook; + char *shared_name; +#ifdef HAVE_DBUS + pa_dbus_connection *connection; + struct rm_monitor *monitor; +#endif +}; + static void reserve_wrapper_free(pa_reserve_wrapper *r) { pa_assert(r); @@ -83,7 +95,7 @@ static int request_cb(rd_device *d, int forced) { PA_REFCNT_INC(r); k = pa_hook_fire(&r->hook, PA_INT_TO_PTR(forced)); - pa_log_debug("Device unlock has been requested and %s.", k < 0 ? "failed" : "succeeded"); + pa_log_debug("Device unlock of %s has been requested and %s.", r->shared_name, k < 0 ? "failed" : "succeeded"); pa_reserve_wrapper_unref(r); @@ -191,3 +203,138 @@ void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const rd_set_application_device_name(r->device, name); #endif } + +static void reserve_monitor_wrapper_free(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + +#ifdef HAVE_DBUS + if (w->monitor) + rm_release(w->monitor); + + if (w->connection) + pa_dbus_connection_unref(w->connection); +#endif + + pa_hook_done(&w->hook); + + if (w->shared_name) { + pa_assert_se(pa_shared_remove(w->core, w->shared_name) >= 0); + pa_xfree(w->shared_name); + } + + pa_xfree(w); +} + +#ifdef HAVE_DBUS +static void change_cb(rm_monitor *m) { + pa_reserve_monitor_wrapper *w; + int k; + + pa_assert(m); + pa_assert_se(w = rm_get_userdata(m)); + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + PA_REFCNT_INC(w); + + if ((k = rm_busy(w->monitor)) < 0) + return; + + pa_hook_fire(&w->hook, PA_INT_TO_PTR(!!k)); + pa_log_debug("Device lock status of %s changed: %s", w->shared_name, k ? "busy" : "not busy"); + + pa_reserve_monitor_wrapper_unref(w); +} +#endif + +pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name) { + pa_reserve_monitor_wrapper *w; + int k; + char *t; +#ifdef HAVE_DBUS + DBusError error; + + dbus_error_init(&error); +#endif + + pa_assert(c); + pa_assert(device_name); + + t = pa_sprintf_malloc("reserve-monitor-wrapper@%s", device_name); + + if ((w = pa_shared_get(c, t))) { + pa_xfree(t); + + pa_assert(PA_REFCNT_VALUE(w) >= 1); + PA_REFCNT_INC(w); + + return w; + } + + w = pa_xnew0(pa_reserve_monitor_wrapper, 1); + PA_REFCNT_INIT(w); + w->core = c; + pa_hook_init(&w->hook, w); + w->shared_name = t; + + pa_assert_se(pa_shared_set(c, w->shared_name, w) >= 0); + +#ifdef HAVE_DBUS + if (!(w->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { + pa_log_warn("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); + + /* We don't treat this as error here because we want allow PA + * to run even when no session bus is available. */ + return w; + } + + if ((k = rm_watch( + &w->monitor, + pa_dbus_connection_get(w->connection), + device_name, + change_cb, + NULL)) < 0) { + + pa_log_warn("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k)); + goto fail; + } + + pa_log_debug("Successfully create reservation lock monitor for device '%s'", device_name); + + rm_set_userdata(w->monitor, w); + return w; + +fail: + dbus_error_free(&error); + + reserve_monitor_wrapper_free(w); + + return NULL; +#else + return w; +#endif +} + +void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + if (PA_REFCNT_DEC(w) > 0) + return; + + reserve_monitor_wrapper_free(w); +} + +pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + return &w->hook; +} + +pa_bool_t pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *w) { + pa_assert(w); + + pa_assert(PA_REFCNT_VALUE(w) >= 1); + + return rm_busy(w->monitor) > 0; +} diff --git a/src/modules/reserve-wrap.h b/src/modules/reserve-wrap.h index 2b97c91c..2de6c093 100644 --- a/src/modules/reserve-wrap.h +++ b/src/modules/reserve-wrap.h @@ -28,11 +28,18 @@ typedef struct pa_reserve_wrapper pa_reserve_wrapper; pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name); - void pa_reserve_wrapper_unref(pa_reserve_wrapper *r); pa_hook* pa_reserve_wrapper_hook(pa_reserve_wrapper *r); void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const char *name); +typedef struct pa_reserve_monitor_wrapper pa_reserve_monitor_wrapper; + +pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name); +void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *m); + +pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *m); +pa_bool_t pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *m); + #endif -- cgit