In combined --dbus and --classic compilation ode, use PulseAudio acquire/release...
[jack2.git] / dbus / reserve.c
blobbc698a6a55b8c0a562814f645b04f1da7818f034
1 /***
2 Copyright 2009 Lennart Poettering
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation files
6 (the "Software"), to deal in the Software without restriction,
7 including without limitation the rights to use, copy, modify, merge,
8 publish, distribute, sublicense, and/or sell copies of the Software,
9 and to permit persons to whom the Software is furnished to do so,
10 subject to the following conditions:
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 ***/
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <assert.h>
31 #include <stdint.h>
33 #include "reserve.h"
35 struct rd_device {
36 int ref;
38 char *device_name;
39 char *application_name;
40 char *application_device_name;
41 char *service_name;
42 char *object_path;
43 int32_t priority;
45 DBusConnection *connection;
47 int owning:1;
48 int registered:1;
49 int filtering:1;
50 int gave_up:1;
52 rd_request_cb_t request_cb;
53 void *userdata;
57 #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
58 #define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
60 static const char introspection[] =
61 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
62 "<node>"
63 " <!-- If you are looking for documentation make sure to check out\n"
64 " http://git.0pointer.de/?p=reserve.git;a=blob;f=reserve.txt -->\n"
65 " <interface name=\"org.freedesktop.ReserveDevice1\">"
66 " <method name=\"RequestRelease\">"
67 " <arg name=\"priority\" type=\"i\" direction=\"in\"/>"
68 " <arg name=\"result\" type=\"b\" direction=\"out\"/>"
69 " </method>"
70 " <property name=\"Priority\" type=\"i\" access=\"read\"/>"
71 " <property name=\"ApplicationName\" type=\"s\" access=\"read\"/>"
72 " <property name=\"ApplicationDeviceName\" type=\"s\" access=\"read\"/>"
73 " </interface>"
74 " <interface name=\"org.freedesktop.DBus.Properties\">"
75 " <method name=\"Get\">"
76 " <arg name=\"interface\" direction=\"in\" type=\"s\"/>"
77 " <arg name=\"property\" direction=\"in\" type=\"s\"/>"
78 " <arg name=\"value\" direction=\"out\" type=\"v\"/>"
79 " </method>"
80 " </interface>"
81 " <interface name=\"org.freedesktop.DBus.Introspectable\">"
82 " <method name=\"Introspect\">"
83 " <arg name=\"data\" type=\"s\" direction=\"out\"/>"
84 " </method>"
85 " </interface>"
86 "</node>";
88 static dbus_bool_t add_variant(
89 DBusMessage *m,
90 int type,
91 const void *data) {
93 DBusMessageIter iter, sub;
94 char t[2];
96 t[0] = (char) type;
97 t[1] = 0;
99 dbus_message_iter_init_append(m, &iter);
101 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub))
102 return FALSE;
104 if (!dbus_message_iter_append_basic(&sub, type, data))
105 return FALSE;
107 if (!dbus_message_iter_close_container(&iter, &sub))
108 return FALSE;
110 return TRUE;
113 static DBusHandlerResult object_handler(
114 DBusConnection *c,
115 DBusMessage *m,
116 void *userdata) {
118 rd_device *d;
119 DBusError error;
120 DBusMessage *reply = NULL;
122 dbus_error_init(&error);
124 d = (rd_device*)userdata;
125 assert(d->ref >= 1);
127 if (dbus_message_is_method_call(
129 "org.freedesktop.ReserveDevice1",
130 "RequestRelease")) {
132 int32_t priority;
133 dbus_bool_t ret;
135 if (!dbus_message_get_args(
137 &error,
138 DBUS_TYPE_INT32, &priority,
139 DBUS_TYPE_INVALID))
140 goto invalid;
142 ret = FALSE;
144 if (priority > d->priority && d->request_cb) {
145 d->ref++;
147 if (d->request_cb(d, 0) > 0) {
148 ret = TRUE;
149 d->gave_up = 1;
152 rd_release(d);
155 if (!(reply = dbus_message_new_method_return(m)))
156 goto oom;
158 if (!dbus_message_append_args(
159 reply,
160 DBUS_TYPE_BOOLEAN, &ret,
161 DBUS_TYPE_INVALID))
162 goto oom;
164 if (!dbus_connection_send(c, reply, NULL))
165 goto oom;
167 dbus_message_unref(reply);
169 return DBUS_HANDLER_RESULT_HANDLED;
171 } else if (dbus_message_is_method_call(
173 "org.freedesktop.DBus.Properties",
174 "Get")) {
176 const char *interface, *property;
178 if (!dbus_message_get_args(
180 &error,
181 DBUS_TYPE_STRING, &interface,
182 DBUS_TYPE_STRING, &property,
183 DBUS_TYPE_INVALID))
184 goto invalid;
186 if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) {
187 const char *empty = "";
189 if (strcmp(property, "ApplicationName") == 0 && d->application_name) {
190 if (!(reply = dbus_message_new_method_return(m)))
191 goto oom;
193 if (!add_variant(
194 reply,
195 DBUS_TYPE_STRING,
196 d->application_name ? (const char**) &d->application_name : &empty))
197 goto oom;
199 } else if (strcmp(property, "ApplicationDeviceName") == 0) {
200 if (!(reply = dbus_message_new_method_return(m)))
201 goto oom;
203 if (!add_variant(
204 reply,
205 DBUS_TYPE_STRING,
206 d->application_device_name ? (const char**) &d->application_device_name : &empty))
207 goto oom;
209 } else if (strcmp(property, "Priority") == 0) {
210 if (!(reply = dbus_message_new_method_return(m)))
211 goto oom;
213 if (!add_variant(
214 reply,
215 DBUS_TYPE_INT32,
216 &d->priority))
217 goto oom;
218 } else {
219 if (!(reply = dbus_message_new_error_printf(
221 DBUS_ERROR_UNKNOWN_METHOD,
222 "Unknown property %s",
223 property)))
224 goto oom;
227 if (!dbus_connection_send(c, reply, NULL))
228 goto oom;
230 dbus_message_unref(reply);
232 return DBUS_HANDLER_RESULT_HANDLED;
235 } else if (dbus_message_is_method_call(
237 "org.freedesktop.DBus.Introspectable",
238 "Introspect")) {
239 const char *i = introspection;
241 if (!(reply = dbus_message_new_method_return(m)))
242 goto oom;
244 if (!dbus_message_append_args(
245 reply,
246 DBUS_TYPE_STRING,
248 DBUS_TYPE_INVALID))
249 goto oom;
251 if (!dbus_connection_send(c, reply, NULL))
252 goto oom;
254 dbus_message_unref(reply);
256 return DBUS_HANDLER_RESULT_HANDLED;
259 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
261 invalid:
262 if (reply)
263 dbus_message_unref(reply);
265 if (!(reply = dbus_message_new_error(
267 DBUS_ERROR_INVALID_ARGS,
268 "Invalid arguments")))
269 goto oom;
271 if (!dbus_connection_send(c, reply, NULL))
272 goto oom;
274 dbus_message_unref(reply);
276 dbus_error_free(&error);
278 return DBUS_HANDLER_RESULT_HANDLED;
280 oom:
281 if (reply)
282 dbus_message_unref(reply);
284 dbus_error_free(&error);
286 return DBUS_HANDLER_RESULT_NEED_MEMORY;
289 static DBusHandlerResult filter_handler(
290 DBusConnection *c,
291 DBusMessage *m,
292 void *userdata) {
294 DBusMessage *reply;
295 rd_device *d;
296 DBusError error;
298 dbus_error_init(&error);
300 d = (rd_device*)userdata;
302 if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) {
303 const char *name;
305 if (!dbus_message_get_args(
307 &error,
308 DBUS_TYPE_STRING, &name,
309 DBUS_TYPE_INVALID))
310 goto invalid;
312 if (strcmp(name, d->service_name) == 0 && d->owning) {
313 d->owning = 0;
315 if (!d->gave_up) {
316 d->ref++;
318 if (d->request_cb)
319 d->request_cb(d, 1);
320 d->gave_up = 1;
322 rd_release(d);
325 return DBUS_HANDLER_RESULT_HANDLED;
329 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
331 invalid:
332 if (!(reply = dbus_message_new_error(
334 DBUS_ERROR_INVALID_ARGS,
335 "Invalid arguments")))
336 goto oom;
338 if (!dbus_connection_send(c, reply, NULL))
339 goto oom;
341 dbus_message_unref(reply);
343 dbus_error_free(&error);
345 return DBUS_HANDLER_RESULT_HANDLED;
347 oom:
348 if (reply)
349 dbus_message_unref(reply);
351 dbus_error_free(&error);
353 return DBUS_HANDLER_RESULT_NEED_MEMORY;
356 static DBusObjectPathVTable vtable;
358 int rd_acquire(
359 rd_device **_d,
360 DBusConnection *connection,
361 const char *device_name,
362 const char *application_name,
363 int32_t priority,
364 rd_request_cb_t request_cb,
365 DBusError *error) {
367 rd_device *d = NULL;
368 int r, k;
369 DBusError _error;
370 DBusMessage *m = NULL, *reply = NULL;
371 dbus_bool_t good;
373 vtable.message_function = object_handler;
375 if (!error)
376 error = &_error;
378 dbus_error_init(error);
380 if (!_d)
381 return -EINVAL;
383 if (!connection)
384 return -EINVAL;
386 if (!device_name)
387 return -EINVAL;
389 if (!request_cb && priority != INT32_MAX)
390 return -EINVAL;
392 if (!(d = (rd_device *)calloc(sizeof(rd_device), 1)))
393 return -ENOMEM;
395 d->ref = 1;
397 if (!(d->device_name = strdup(device_name))) {
398 r = -ENOMEM;
399 goto fail;
402 if (!(d->application_name = strdup(application_name))) {
403 r = -ENOMEM;
404 goto fail;
407 d->priority = priority;
408 d->connection = dbus_connection_ref(connection);
409 d->request_cb = request_cb;
411 if (!(d->service_name = (char*)malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
412 r = -ENOMEM;
413 goto fail;
415 sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name);
417 if (!(d->object_path = (char*)malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) {
418 r = -ENOMEM;
419 goto fail;
421 sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name);
423 if ((k = dbus_bus_request_name(
424 d->connection,
425 d->service_name,
426 DBUS_NAME_FLAG_DO_NOT_QUEUE|
427 (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0),
428 error)) < 0) {
429 r = -EIO;
430 goto fail;
433 if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
434 goto success;
436 if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) {
437 r = -EIO;
438 goto fail;
441 if (priority <= INT32_MIN) {
442 r = -EBUSY;
443 goto fail;
446 if (!(m = dbus_message_new_method_call(
447 d->service_name,
448 d->object_path,
449 "org.freedesktop.ReserveDevice1",
450 "RequestRelease"))) {
451 r = -ENOMEM;
452 goto fail;
455 if (!dbus_message_append_args(
457 DBUS_TYPE_INT32, &d->priority,
458 DBUS_TYPE_INVALID)) {
459 r = -ENOMEM;
460 goto fail;
463 if (!(reply = dbus_connection_send_with_reply_and_block(
464 d->connection,
466 5000, /* 5s */
467 error))) {
469 if (dbus_error_has_name(error, DBUS_ERROR_TIMED_OUT) ||
470 dbus_error_has_name(error, DBUS_ERROR_UNKNOWN_METHOD) ||
471 dbus_error_has_name(error, DBUS_ERROR_NO_REPLY)) {
472 /* This must be treated as denied. */
473 r = -EBUSY;
474 goto fail;
477 r = -EIO;
478 goto fail;
481 if (!dbus_message_get_args(
482 reply,
483 error,
484 DBUS_TYPE_BOOLEAN, &good,
485 DBUS_TYPE_INVALID)) {
486 r = -EIO;
487 goto fail;
490 if (!good) {
491 r = -EBUSY;
492 goto fail;
495 if ((k = dbus_bus_request_name(
496 d->connection,
497 d->service_name,
498 DBUS_NAME_FLAG_DO_NOT_QUEUE|
499 (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)|
500 DBUS_NAME_FLAG_REPLACE_EXISTING,
501 error)) < 0) {
502 r = -EIO;
503 goto fail;
506 if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
507 r = -EIO;
508 goto fail;
511 success:
512 d->owning = 1;
514 if (!(dbus_connection_register_object_path(
515 d->connection,
516 d->object_path,
517 &vtable,
518 d))) {
519 r = -ENOMEM;
520 goto fail;
523 d->registered = 1;
525 if (!dbus_connection_add_filter(
526 d->connection,
527 filter_handler,
529 NULL)) {
530 r = -ENOMEM;
531 goto fail;
534 d->filtering = 1;
536 *_d = d;
537 return 0;
539 fail:
540 if (m)
541 dbus_message_unref(m);
543 if (reply)
544 dbus_message_unref(reply);
546 if (&_error == error)
547 dbus_error_free(&_error);
549 if (d)
550 rd_release(d);
552 return r;
555 void rd_release(
556 rd_device *d) {
558 if (!d)
559 return;
561 assert(d->ref > 0);
563 if (--d->ref)
564 return;
567 if (d->filtering)
568 dbus_connection_remove_filter(
569 d->connection,
570 filter_handler,
573 if (d->registered)
574 dbus_connection_unregister_object_path(
575 d->connection,
576 d->object_path);
578 if (d->owning) {
579 DBusError error;
580 dbus_error_init(&error);
582 dbus_bus_release_name(
583 d->connection,
584 d->service_name,
585 &error);
587 dbus_error_free(&error);
590 free(d->device_name);
591 free(d->application_name);
592 free(d->application_device_name);
593 free(d->service_name);
594 free(d->object_path);
596 if (d->connection)
597 dbus_connection_unref(d->connection);
599 free(d);
602 int rd_set_application_device_name(rd_device *d, const char *n) {
603 char *t;
605 if (!d)
606 return -EINVAL;
608 assert(d->ref > 0);
610 if (!(t = strdup(n)))
611 return -ENOMEM;
613 free(d->application_device_name);
614 d->application_device_name = t;
615 return 0;
618 void rd_set_userdata(rd_device *d, void *userdata) {
620 if (!d)
621 return;
623 assert(d->ref > 0);
624 d->userdata = userdata;
627 void* rd_get_userdata(rd_device *d) {
629 if (!d)
630 return NULL;
632 assert(d->ref > 0);
634 return d->userdata;