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
34 #include "jack/control.h"
36 #define RESERVE_ERROR_NO_MEMORY "org.freedesktop.ReserveDevice1.Error.NoMemory"
37 #define RESERVE_ERROR_PROTOCOL_VIOLATION "org.freedesktop.ReserveDevice1.Error.Protocol"
38 #define RESERVE_ERROR_RELEASE_DENIED "org.freedesktop.ReserveDevice1.Error.ReleaseDenied"
44 char *application_name
;
45 char *application_device_name
;
50 DBusConnection
*connection
;
57 rd_request_cb_t request_cb
;
62 #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
63 #define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
65 static const char introspection
[] =
66 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
68 " <!-- If you are looking for documentation make sure to check out\n"
69 " http://git.0pointer.de/?p=reserve.git;a=blob;f=reserve.txt -->\n"
70 " <interface name=\"org.freedesktop.ReserveDevice1\">"
71 " <method name=\"RequestRelease\">"
72 " <arg name=\"priority\" type=\"i\" direction=\"in\"/>"
73 " <arg name=\"result\" type=\"b\" direction=\"out\"/>"
75 " <property name=\"Priority\" type=\"i\" access=\"read\"/>"
76 " <property name=\"ApplicationName\" type=\"s\" access=\"read\"/>"
77 " <property name=\"ApplicationDeviceName\" type=\"s\" access=\"read\"/>"
79 " <interface name=\"org.freedesktop.DBus.Properties\">"
80 " <method name=\"Get\">"
81 " <arg name=\"interface\" direction=\"in\" type=\"s\"/>"
82 " <arg name=\"property\" direction=\"in\" type=\"s\"/>"
83 " <arg name=\"value\" direction=\"out\" type=\"v\"/>"
86 " <interface name=\"org.freedesktop.DBus.Introspectable\">"
87 " <method name=\"Introspect\">"
88 " <arg name=\"data\" type=\"s\" direction=\"out\"/>"
93 static dbus_bool_t
add_variant(
98 DBusMessageIter iter
, sub
;
104 dbus_message_iter_init_append(m
, &iter
);
106 if (!dbus_message_iter_open_container(&iter
, DBUS_TYPE_VARIANT
, t
, &sub
))
109 if (!dbus_message_iter_append_basic(&sub
, type
, data
))
112 if (!dbus_message_iter_close_container(&iter
, &sub
))
118 static DBusHandlerResult
object_handler(
125 DBusMessage
*reply
= NULL
;
127 dbus_error_init(&error
);
129 d
= (rd_device
*)userdata
;
132 if (dbus_message_is_method_call(
134 "org.freedesktop.ReserveDevice1",
140 if (!dbus_message_get_args(
143 DBUS_TYPE_INT32
, &priority
,
149 if (priority
> d
->priority
&& d
->request_cb
) {
152 if (d
->request_cb(d
, 0) > 0) {
160 if (!(reply
= dbus_message_new_method_return(m
)))
163 if (!dbus_message_append_args(
165 DBUS_TYPE_BOOLEAN
, &ret
,
169 if (!dbus_connection_send(c
, reply
, NULL
))
172 dbus_message_unref(reply
);
174 return DBUS_HANDLER_RESULT_HANDLED
;
176 } else if (dbus_message_is_method_call(
178 "org.freedesktop.DBus.Properties",
181 const char *interface
, *property
;
183 if (!dbus_message_get_args(
186 DBUS_TYPE_STRING
, &interface
,
187 DBUS_TYPE_STRING
, &property
,
191 if (strcmp(interface
, "org.freedesktop.ReserveDevice1") == 0) {
192 const char *empty
= "";
194 if (strcmp(property
, "ApplicationName") == 0 && d
->application_name
) {
195 if (!(reply
= dbus_message_new_method_return(m
)))
201 d
->application_name
? (const char**) &d
->application_name
: &empty
))
204 } else if (strcmp(property
, "ApplicationDeviceName") == 0) {
205 if (!(reply
= dbus_message_new_method_return(m
)))
211 d
->application_device_name
? (const char**) &d
->application_device_name
: &empty
))
214 } else if (strcmp(property
, "Priority") == 0) {
215 if (!(reply
= dbus_message_new_method_return(m
)))
224 if (!(reply
= dbus_message_new_error_printf(
226 DBUS_ERROR_UNKNOWN_METHOD
,
227 "Unknown property %s",
232 if (!dbus_connection_send(c
, reply
, NULL
))
235 dbus_message_unref(reply
);
237 return DBUS_HANDLER_RESULT_HANDLED
;
240 } else if (dbus_message_is_method_call(
242 "org.freedesktop.DBus.Introspectable",
244 const char *i
= introspection
;
246 if (!(reply
= dbus_message_new_method_return(m
)))
249 if (!dbus_message_append_args(
256 if (!dbus_connection_send(c
, reply
, NULL
))
259 dbus_message_unref(reply
);
261 return DBUS_HANDLER_RESULT_HANDLED
;
264 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
268 dbus_message_unref(reply
);
270 if (!(reply
= dbus_message_new_error(
272 DBUS_ERROR_INVALID_ARGS
,
273 "Invalid arguments")))
276 if (!dbus_connection_send(c
, reply
, NULL
))
279 dbus_message_unref(reply
);
281 dbus_error_free(&error
);
283 return DBUS_HANDLER_RESULT_HANDLED
;
287 dbus_message_unref(reply
);
289 dbus_error_free(&error
);
291 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
294 static DBusHandlerResult
filter_handler(
303 dbus_error_init(&error
);
305 d
= (rd_device
*)userdata
;
307 if (dbus_message_is_signal(m
, "org.freedesktop.DBus", "NameLost")) {
310 if (!dbus_message_get_args(
313 DBUS_TYPE_STRING
, &name
,
317 if (strcmp(name
, d
->service_name
) == 0 && d
->owning
) {
330 return DBUS_HANDLER_RESULT_HANDLED
;
334 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
337 if (!(reply
= dbus_message_new_error(
339 DBUS_ERROR_INVALID_ARGS
,
340 "Invalid arguments")))
343 if (!dbus_connection_send(c
, reply
, NULL
))
346 dbus_message_unref(reply
);
348 dbus_error_free(&error
);
350 return DBUS_HANDLER_RESULT_HANDLED
;
354 dbus_message_unref(reply
);
356 dbus_error_free(&error
);
358 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
361 static DBusObjectPathVTable vtable
;
365 DBusConnection
*connection
,
366 const char *device_name
,
367 const char *application_name
,
369 rd_request_cb_t request_cb
,
375 DBusMessage
*m
= NULL
, *reply
= NULL
;
377 vtable
.message_function
= object_handler
;
381 dbus_error_init(error
);
402 if (!request_cb
&& priority
!= INT32_MAX
) {
408 if (!(d
= (rd_device
*)calloc(sizeof(rd_device
), 1))) {
409 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot allocate memory for rd_device struct");
416 if (!(d
->device_name
= strdup(device_name
))) {
417 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot duplicate device name string");
422 if (!(d
->application_name
= strdup(application_name
))) {
423 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot duplicate application name string");
428 d
->priority
= priority
;
429 d
->connection
= dbus_connection_ref(connection
);
430 d
->request_cb
= request_cb
;
432 if (!(d
->service_name
= (char*)malloc(sizeof(SERVICE_PREFIX
) + strlen(device_name
)))) {
433 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot allocate memory for service name string");
437 sprintf(d
->service_name
, SERVICE_PREFIX
"%s", d
->device_name
);
439 if (!(d
->object_path
= (char*)malloc(sizeof(OBJECT_PREFIX
) + strlen(device_name
)))) {
440 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot allocate memory for object path string");
444 sprintf(d
->object_path
, OBJECT_PREFIX
"%s", d
->device_name
);
446 if ((k
= dbus_bus_request_name(
449 DBUS_NAME_FLAG_DO_NOT_QUEUE
|
450 (priority
< INT32_MAX
? DBUS_NAME_FLAG_ALLOW_REPLACEMENT
: 0),
452 jack_error("dbus_bus_request_name() failed. (1)");
458 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
:
459 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER
:
461 case DBUS_REQUEST_NAME_REPLY_EXISTS
:
463 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE
: /* DBUS_NAME_FLAG_DO_NOT_QUEUE was specified */
464 default: /* unknown reply returned */
465 jack_error("request name reply with unexpected value %d.", k
);
471 if (priority
<= INT32_MIN
) {
473 dbus_set_error(error
, RESERVE_ERROR_RELEASE_DENIED
, "Device reservation request with priority %"PRIi32
" denied for \"%s\"", priority
, device_name
);
477 if (!(m
= dbus_message_new_method_call(
480 "org.freedesktop.ReserveDevice1",
481 "RequestRelease"))) {
482 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot allocate memory for RequestRelease method call");
487 if (!dbus_message_append_args(
489 DBUS_TYPE_INT32
, &d
->priority
,
490 DBUS_TYPE_INVALID
)) {
491 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot append args for RequestRelease method call");
496 if (!(reply
= dbus_connection_send_with_reply_and_block(
502 if (dbus_error_has_name(error
, DBUS_ERROR_TIMED_OUT
) ||
503 dbus_error_has_name(error
, DBUS_ERROR_UNKNOWN_METHOD
) ||
504 dbus_error_has_name(error
, DBUS_ERROR_NO_REPLY
)) {
505 /* This must be treated as denied. */
506 jack_info("Device reservation request with priority %"PRIi32
" denied for \"%s\": %s (%s)", priority
, device_name
, error
->name
, error
->message
);
511 jack_error("dbus_connection_send_with_reply_and_block(RequestRelease) failed.");
516 if (!dbus_message_get_args(
519 DBUS_TYPE_BOOLEAN
, &good
,
520 DBUS_TYPE_INVALID
)) {
521 jack_error("RequestRelease() reply is invalid.");
527 dbus_set_error(error
, RESERVE_ERROR_RELEASE_DENIED
, "Device reservation request with priority %"PRIi32
" denied for \"%s\" via RequestRelease()", priority
, device_name
);
532 if ((k
= dbus_bus_request_name(
535 DBUS_NAME_FLAG_DO_NOT_QUEUE
|
536 (priority
< INT32_MAX
? DBUS_NAME_FLAG_ALLOW_REPLACEMENT
: 0)|
537 DBUS_NAME_FLAG_REPLACE_EXISTING
,
539 jack_error("dbus_bus_request_name() failed. (2)");
544 if (k
!= DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
) {
545 /* this is racy, another contender may have acquired the device */
546 dbus_set_error(error
, RESERVE_ERROR_PROTOCOL_VIOLATION
, "request name reply is not DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER but %d.", k
);
554 if (!(dbus_connection_try_register_object_path(
560 jack_error("cannot register object path \"%s\": %s", d
->object_path
, error
->message
);
567 if (!dbus_connection_add_filter(
572 dbus_set_error(error
, RESERVE_ERROR_NO_MEMORY
, "Cannot add filter");
584 dbus_message_unref(m
);
587 dbus_message_unref(reply
);
589 if (&_error
== error
)
590 dbus_error_free(&_error
);
611 dbus_connection_remove_filter(
617 dbus_connection_unregister_object_path(
623 dbus_error_init(&error
);
625 dbus_bus_release_name(
630 dbus_error_free(&error
);
633 free(d
->device_name
);
634 free(d
->application_name
);
635 free(d
->application_device_name
);
636 free(d
->service_name
);
637 free(d
->object_path
);
640 dbus_connection_unref(d
->connection
);
645 int rd_set_application_device_name(rd_device
*d
, const char *n
) {
653 if (!(t
= strdup(n
)))
656 free(d
->application_device_name
);
657 d
->application_device_name
= t
;
661 void rd_set_userdata(rd_device
*d
, void *userdata
) {
667 d
->userdata
= userdata
;
670 void* rd_get_userdata(rd_device
*d
) {