1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <errno.h> 25 26#include <pulse/xmalloc.h> 27 28#include <pulsecore/core-error.h> 29#include <pulsecore/core-util.h> 30#include <pulsecore/i18n.h> 31#include <pulsecore/shared.h> 32 33#ifdef HAVE_DBUS 34#include <pulsecore/dbus-shared.h> 35#include "reserve.h" 36#include "reserve-monitor.h" 37#endif 38 39#include "reserve-wrap.h" 40 41struct pa_reserve_wrapper { 42 PA_REFCNT_DECLARE; 43 pa_core *core; 44 pa_hook hook; 45 char *shared_name; 46#ifdef HAVE_DBUS 47 pa_dbus_connection *connection; 48 struct rd_device *device; 49#endif 50}; 51 52struct pa_reserve_monitor_wrapper { 53 PA_REFCNT_DECLARE; 54 pa_core *core; 55 pa_hook hook; 56 char *shared_name; 57#ifdef HAVE_DBUS 58 pa_dbus_connection *connection; 59 struct rm_monitor *monitor; 60#endif 61}; 62 63static void reserve_wrapper_free(pa_reserve_wrapper *r) { 64 pa_assert(r); 65 66#ifdef HAVE_DBUS 67 if (r->device) 68 rd_release(r->device); 69 70 if (r->connection) 71 pa_dbus_connection_unref(r->connection); 72#endif 73 74 pa_hook_done(&r->hook); 75 76 if (r->shared_name) { 77 pa_assert_se(pa_shared_remove(r->core, r->shared_name) >= 0); 78 pa_xfree(r->shared_name); 79 } 80 81 pa_xfree(r); 82} 83 84#ifdef HAVE_DBUS 85static int request_cb(rd_device *d, int forced) { 86 pa_reserve_wrapper *r; 87 int k; 88 89 pa_assert(d); 90 pa_assert_se(r = rd_get_userdata(d)); 91 pa_assert(PA_REFCNT_VALUE(r) >= 1); 92 93 PA_REFCNT_INC(r); 94 95 k = pa_hook_fire(&r->hook, PA_INT_TO_PTR(forced)); 96 pa_log_debug("Device unlock of %s has been requested and %s.", r->shared_name, k < 0 ? "failed" : "succeeded"); 97 98 pa_reserve_wrapper_unref(r); 99 100 return k < 0 ? -1 : 1; 101} 102#endif 103 104pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name) { 105 pa_reserve_wrapper *r; 106 char *t; 107#ifdef HAVE_DBUS 108 int k; 109 DBusError error; 110 111 dbus_error_init(&error); 112#endif 113 114 pa_assert(c); 115 pa_assert(device_name); 116 117 t = pa_sprintf_malloc("reserve-wrapper@%s", device_name); 118 119 if ((r = pa_shared_get(c, t))) { 120 pa_xfree(t); 121 122 pa_assert(PA_REFCNT_VALUE(r) >= 1); 123 PA_REFCNT_INC(r); 124 125 return r; 126 } 127 128 r = pa_xnew0(pa_reserve_wrapper, 1); 129 PA_REFCNT_INIT(r); 130 r->core = c; 131 pa_hook_init(&r->hook, r); 132 r->shared_name = t; 133 134 pa_assert_se(pa_shared_set(c, r->shared_name, r) >= 0); 135 136#ifdef HAVE_DBUS 137 if (!(r->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { 138 pa_log_debug("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); 139 140 /* We don't treat this as error here because we want allow PA 141 * to run even when no session bus is available. */ 142 return r; 143 } 144 145 if ((k = rd_acquire( 146 &r->device, 147 pa_dbus_connection_get(r->connection), 148 device_name, 149 _("PulseAudio Sound Server"), 150 0, 151 request_cb, 152 NULL)) < 0) { 153 154 if (k == -EBUSY) { 155 pa_log_debug("Device '%s' already locked.", device_name); 156 goto fail; 157 } else { 158 pa_log_debug("Failed to acquire reservation lock on device '%s': %s", device_name, pa_cstrerror(-k)); 159 return r; 160 } 161 } 162 163 pa_log_debug("Successfully acquired reservation lock on device '%s'", device_name); 164 165 rd_set_userdata(r->device, r); 166 167 return r; 168fail: 169 dbus_error_free(&error); 170 171 reserve_wrapper_free(r); 172 173 return NULL; 174#else 175 return r; 176#endif 177} 178 179void pa_reserve_wrapper_unref(pa_reserve_wrapper *r) { 180 pa_assert(r); 181 pa_assert(PA_REFCNT_VALUE(r) >= 1); 182 183 if (PA_REFCNT_DEC(r) > 0) 184 return; 185 186 reserve_wrapper_free(r); 187} 188 189pa_hook* pa_reserve_wrapper_hook(pa_reserve_wrapper *r) { 190 pa_assert(r); 191 pa_assert(PA_REFCNT_VALUE(r) >= 1); 192 193 return &r->hook; 194} 195 196void pa_reserve_wrapper_set_application_device_name(pa_reserve_wrapper *r, const char *name) { 197 pa_assert(r); 198 pa_assert(PA_REFCNT_VALUE(r) >= 1); 199 200#ifdef HAVE_DBUS 201 rd_set_application_device_name(r->device, name); 202#endif 203} 204 205static void reserve_monitor_wrapper_free(pa_reserve_monitor_wrapper *w) { 206 pa_assert(w); 207 208#ifdef HAVE_DBUS 209 if (w->monitor) 210 rm_release(w->monitor); 211 212 if (w->connection) 213 pa_dbus_connection_unref(w->connection); 214#endif 215 216 pa_hook_done(&w->hook); 217 218 if (w->shared_name) { 219 pa_assert_se(pa_shared_remove(w->core, w->shared_name) >= 0); 220 pa_xfree(w->shared_name); 221 } 222 223 pa_xfree(w); 224} 225 226#ifdef HAVE_DBUS 227static void change_cb(rm_monitor *m) { 228 pa_reserve_monitor_wrapper *w; 229 int k; 230 231 pa_assert(m); 232 pa_assert_se(w = rm_get_userdata(m)); 233 pa_assert(PA_REFCNT_VALUE(w) >= 1); 234 235 PA_REFCNT_INC(w); 236 237 if ((k = rm_busy(w->monitor)) < 0) 238 return; 239 240 pa_hook_fire(&w->hook, PA_INT_TO_PTR(!!k)); 241 pa_log_debug("Device lock status of %s changed: %s", w->shared_name, k ? "busy" : "not busy"); 242 243 pa_reserve_monitor_wrapper_unref(w); 244} 245#endif 246 247pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name) { 248 pa_reserve_monitor_wrapper *w; 249 char *t; 250#ifdef HAVE_DBUS 251 int k; 252 DBusError error; 253 254 dbus_error_init(&error); 255#endif 256 257 pa_assert(c); 258 pa_assert(device_name); 259 260 t = pa_sprintf_malloc("reserve-monitor-wrapper@%s", device_name); 261 262 if ((w = pa_shared_get(c, t))) { 263 pa_xfree(t); 264 265 pa_assert(PA_REFCNT_VALUE(w) >= 1); 266 PA_REFCNT_INC(w); 267 268 return w; 269 } 270 271 w = pa_xnew0(pa_reserve_monitor_wrapper, 1); 272 PA_REFCNT_INIT(w); 273 w->core = c; 274 pa_hook_init(&w->hook, w); 275 w->shared_name = t; 276 277 pa_assert_se(pa_shared_set(c, w->shared_name, w) >= 0); 278 279#ifdef HAVE_DBUS 280 if (!(w->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { 281 pa_log_debug("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); 282 283 /* We don't treat this as error here because we want allow PA 284 * to run even when no session bus is available. */ 285 return w; 286 } 287 288 if ((k = rm_watch( 289 &w->monitor, 290 pa_dbus_connection_get(w->connection), 291 device_name, 292 change_cb, 293 NULL)) < 0) { 294 295 pa_log_debug("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k)); 296 goto fail; 297 } 298 299 pa_log_debug("Successfully create reservation lock monitor for device '%s'", device_name); 300 301 rm_set_userdata(w->monitor, w); 302 return w; 303 304fail: 305 dbus_error_free(&error); 306 307 reserve_monitor_wrapper_free(w); 308 309 return NULL; 310#else 311 return w; 312#endif 313} 314 315void pa_reserve_monitor_wrapper_unref(pa_reserve_monitor_wrapper *w) { 316 pa_assert(w); 317 pa_assert(PA_REFCNT_VALUE(w) >= 1); 318 319 if (PA_REFCNT_DEC(w) > 0) 320 return; 321 322 reserve_monitor_wrapper_free(w); 323} 324 325pa_hook* pa_reserve_monitor_wrapper_hook(pa_reserve_monitor_wrapper *w) { 326 pa_assert(w); 327 pa_assert(PA_REFCNT_VALUE(w) >= 1); 328 329 return &w->hook; 330} 331 332bool pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *w) { 333 pa_assert(w); 334 335 pa_assert(PA_REFCNT_VALUE(w) >= 1); 336 337#ifdef HAVE_DBUS 338 return rm_busy(w->monitor) > 0; 339#else 340 return false; 341#endif 342} 343