2009-02-24 18:41:38 +02:00
/***
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 <string.h>
# include <unistd.h>
# include <errno.h>
# include <stdlib.h>
# include <stdio.h>
# include <assert.h>
2009-07-16 13:32:15 +03:00
# include <stdint.h>
2009-02-24 18:41:38 +02:00
# include "reserve.h"
2023-11-07 01:54:43 +02:00
# include <jack/control.h>
2012-11-24 04:29:36 +02:00
# define RESERVE_ERROR_NO_MEMORY "org.freedesktop.ReserveDevice1.Error.NoMemory"
# define RESERVE_ERROR_PROTOCOL_VIOLATION "org.freedesktop.ReserveDevice1.Error.Protocol"
# define RESERVE_ERROR_RELEASE_DENIED "org.freedesktop.ReserveDevice1.Error.ReleaseDenied"
2009-02-24 18:41:38 +02:00
struct rd_device {
int ref ;
char * device_name ;
char * application_name ;
char * application_device_name ;
char * service_name ;
char * object_path ;
int32_t priority ;
DBusConnection * connection ;
2023-11-07 10:52:55 +02:00
unsigned int owning : 1 ;
unsigned int registered : 1 ;
unsigned int filtering : 1 ;
unsigned int gave_up : 1 ;
2009-02-24 18:41:38 +02:00
rd_request_cb_t request_cb ;
void * userdata ;
} ;
# define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
# define OBJECT_PREFIX " / org / freedesktop / ReserveDevice1 / "
static const char introspection [ ] =
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
" <node> "
" <!-- If you are looking for documentation make sure to check out \n "
" http://git.0pointer.de/?p=reserve.git;a=blob;f=reserve.txt --> \n "
" <interface name= \" org.freedesktop.ReserveDevice1 \" > "
" <method name= \" RequestRelease \" > "
" <arg name= \" priority \" type= \" i \" direction= \" in \" /> "
" <arg name= \" result \" type= \" b \" direction= \" out \" /> "
" </method> "
" <property name= \" Priority \" type= \" i \" access= \" read \" /> "
" <property name= \" ApplicationName \" type= \" s \" access= \" read \" /> "
" <property name= \" ApplicationDeviceName \" type= \" s \" access= \" read \" /> "
" </interface> "
" <interface name= \" org.freedesktop.DBus.Properties \" > "
" <method name= \" Get \" > "
" <arg name= \" interface \" direction= \" in \" type= \" s \" /> "
" <arg name= \" property \" direction= \" in \" type= \" s \" /> "
" <arg name= \" value \" direction= \" out \" type= \" v \" /> "
" </method> "
" </interface> "
" <interface name= \" org.freedesktop.DBus.Introspectable \" > "
" <method name= \" Introspect \" > "
" <arg name= \" data \" type= \" s \" direction= \" out \" /> "
" </method> "
" </interface> "
" </node> " ;
static dbus_bool_t add_variant (
DBusMessage * m ,
int type ,
const void * data ) {
DBusMessageIter iter , sub ;
char t [ 2 ] ;
t [ 0 ] = ( char ) type ;
t [ 1 ] = 0 ;
dbus_message_iter_init_append ( m , & iter ) ;
if ( ! dbus_message_iter_open_container ( & iter , DBUS_TYPE_VARIANT , t , & sub ) )
return FALSE ;
if ( ! dbus_message_iter_append_basic ( & sub , type , data ) )
return FALSE ;
if ( ! dbus_message_iter_close_container ( & iter , & sub ) )
return FALSE ;
return TRUE ;
}
static DBusHandlerResult object_handler (
DBusConnection * c ,
DBusMessage * m ,
void * userdata ) {
rd_device * d ;
DBusError error ;
DBusMessage * reply = NULL ;
dbus_error_init ( & error ) ;
2009-07-16 13:32:15 +03:00
d = ( rd_device * ) userdata ;
2009-02-24 18:41:38 +02:00
assert ( d - > ref > = 1 ) ;
if ( dbus_message_is_method_call (
m ,
" org.freedesktop.ReserveDevice1 " ,
" RequestRelease " ) ) {
int32_t priority ;
dbus_bool_t ret ;
if ( ! dbus_message_get_args (
m ,
& error ,
DBUS_TYPE_INT32 , & priority ,
DBUS_TYPE_INVALID ) )
goto invalid ;
ret = FALSE ;
if ( priority > d - > priority & & d - > request_cb ) {
d - > ref + + ;
if ( d - > request_cb ( d , 0 ) > 0 ) {
ret = TRUE ;
d - > gave_up = 1 ;
}
rd_release ( d ) ;
}
if ( ! ( reply = dbus_message_new_method_return ( m ) ) )
goto oom ;
if ( ! dbus_message_append_args (
reply ,
DBUS_TYPE_BOOLEAN , & ret ,
DBUS_TYPE_INVALID ) )
goto oom ;
if ( ! dbus_connection_send ( c , reply , NULL ) )
goto oom ;
dbus_message_unref ( reply ) ;
return DBUS_HANDLER_RESULT_HANDLED ;
} else if ( dbus_message_is_method_call (
m ,
" org.freedesktop.DBus.Properties " ,
" Get " ) ) {
const char * interface , * property ;
if ( ! dbus_message_get_args (
m ,
& error ,
DBUS_TYPE_STRING , & interface ,
DBUS_TYPE_STRING , & property ,
DBUS_TYPE_INVALID ) )
goto invalid ;
if ( strcmp ( interface , " org.freedesktop.ReserveDevice1 " ) = = 0 ) {
const char * empty = " " ;
if ( strcmp ( property , " ApplicationName " ) = = 0 & & d - > application_name ) {
if ( ! ( reply = dbus_message_new_method_return ( m ) ) )
goto oom ;
if ( ! add_variant (
reply ,
DBUS_TYPE_STRING ,
d - > application_name ? ( const char * * ) & d - > application_name : & empty ) )
goto oom ;
} else if ( strcmp ( property , " ApplicationDeviceName " ) = = 0 ) {
if ( ! ( reply = dbus_message_new_method_return ( m ) ) )
goto oom ;
if ( ! add_variant (
reply ,
DBUS_TYPE_STRING ,
d - > application_device_name ? ( const char * * ) & d - > application_device_name : & empty ) )
goto oom ;
} else if ( strcmp ( property , " Priority " ) = = 0 ) {
if ( ! ( reply = dbus_message_new_method_return ( m ) ) )
goto oom ;
if ( ! add_variant (
reply ,
DBUS_TYPE_INT32 ,
& d - > priority ) )
goto oom ;
} else {
if ( ! ( reply = dbus_message_new_error_printf (
m ,
DBUS_ERROR_UNKNOWN_METHOD ,
" Unknown property %s " ,
property ) ) )
goto oom ;
}
if ( ! dbus_connection_send ( c , reply , NULL ) )
goto oom ;
dbus_message_unref ( reply ) ;
return DBUS_HANDLER_RESULT_HANDLED ;
}
} else if ( dbus_message_is_method_call (
m ,
" org.freedesktop.DBus.Introspectable " ,
" Introspect " ) ) {
const char * i = introspection ;
if ( ! ( reply = dbus_message_new_method_return ( m ) ) )
goto oom ;
if ( ! dbus_message_append_args (
reply ,
DBUS_TYPE_STRING ,
& i ,
DBUS_TYPE_INVALID ) )
goto oom ;
if ( ! dbus_connection_send ( c , reply , NULL ) )
goto oom ;
dbus_message_unref ( reply ) ;
return DBUS_HANDLER_RESULT_HANDLED ;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED ;
invalid :
if ( reply )
dbus_message_unref ( reply ) ;
if ( ! ( reply = dbus_message_new_error (
m ,
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 ;
}
static DBusHandlerResult filter_handler (
DBusConnection * c ,
DBusMessage * m ,
void * userdata ) {
DBusMessage * reply ;
rd_device * d ;
DBusError error ;
dbus_error_init ( & error ) ;
2009-07-16 13:32:15 +03:00
d = ( rd_device * ) userdata ;
2009-02-24 18:41:38 +02:00
if ( dbus_message_is_signal ( m , " org.freedesktop.DBus " , " NameLost " ) ) {
const char * name ;
if ( ! dbus_message_get_args (
m ,
& error ,
DBUS_TYPE_STRING , & name ,
DBUS_TYPE_INVALID ) )
goto invalid ;
if ( strcmp ( name , d - > service_name ) = = 0 & & d - > owning ) {
d - > owning = 0 ;
if ( ! d - > gave_up ) {
d - > ref + + ;
if ( d - > request_cb )
d - > request_cb ( d , 1 ) ;
d - > gave_up = 1 ;
rd_release ( d ) ;
}
return DBUS_HANDLER_RESULT_HANDLED ;
}
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED ;
invalid :
if ( ! ( reply = dbus_message_new_error (
m ,
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 ;
}
2009-07-16 13:32:15 +03:00
static DBusObjectPathVTable vtable ;
2009-02-24 18:41:38 +02:00
int rd_acquire (
rd_device * * _d ,
DBusConnection * connection ,
const char * device_name ,
const char * application_name ,
int32_t priority ,
rd_request_cb_t request_cb ,
DBusError * error ) {
rd_device * d = NULL ;
int r , k ;
DBusError _error ;
DBusMessage * m = NULL , * reply = NULL ;
dbus_bool_t good ;
2011-05-27 15:37:06 +03:00
vtable . message_function = object_handler ;
2009-07-16 13:32:15 +03:00
2012-11-24 04:29:36 +02:00
if ( ! error ) {
2009-02-24 18:41:38 +02:00
error = & _error ;
2012-11-24 04:29:36 +02:00
dbus_error_init ( error ) ;
}
2009-02-24 18:41:38 +02:00
2012-11-24 04:29:36 +02:00
if ( ! _d ) {
assert ( 0 ) ;
r = - EINVAL ;
goto fail ;
}
2009-02-24 18:41:38 +02:00
2012-11-24 04:29:36 +02:00
if ( ! connection ) {
assert ( 0 ) ;
r = - EINVAL ;
goto fail ;
}
2009-02-24 18:41:38 +02:00
2012-11-24 04:29:36 +02:00
if ( ! device_name ) {
assert ( 0 ) ;
r = - EINVAL ;
goto fail ;
}
2009-02-24 18:41:38 +02:00
2012-11-24 04:29:36 +02:00
if ( ! request_cb & & priority ! = INT32_MAX ) {
assert ( 0 ) ;
r = - EINVAL ;
goto fail ;
}
2009-02-24 18:41:38 +02:00
2012-11-24 04:29:36 +02:00
if ( ! ( d = ( rd_device * ) calloc ( sizeof ( rd_device ) , 1 ) ) ) {
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot allocate memory for rd_device struct " ) ;
r = - ENOMEM ;
goto fail ;
}
2009-02-24 18:41:38 +02:00
d - > ref = 1 ;
if ( ! ( d - > device_name = strdup ( device_name ) ) ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot duplicate device name string " ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
if ( ! ( d - > application_name = strdup ( application_name ) ) ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot duplicate application name string " ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
d - > priority = priority ;
d - > connection = dbus_connection_ref ( connection ) ;
d - > request_cb = request_cb ;
2009-07-16 13:32:15 +03:00
if ( ! ( d - > service_name = ( char * ) malloc ( sizeof ( SERVICE_PREFIX ) + strlen ( device_name ) ) ) ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot allocate memory for service name string " ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
sprintf ( d - > service_name , SERVICE_PREFIX " %s " , d - > device_name ) ;
2009-07-16 13:32:15 +03:00
if ( ! ( d - > object_path = ( char * ) malloc ( sizeof ( OBJECT_PREFIX ) + strlen ( device_name ) ) ) ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot allocate memory for object path string " ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
sprintf ( d - > object_path , OBJECT_PREFIX " %s " , d - > device_name ) ;
if ( ( k = dbus_bus_request_name (
d - > connection ,
d - > service_name ,
DBUS_NAME_FLAG_DO_NOT_QUEUE |
( priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0 ) ,
error ) ) < 0 ) {
2012-11-24 04:29:36 +02:00
jack_error ( " dbus_bus_request_name() failed. (1) " ) ;
2009-02-24 18:41:38 +02:00
r = - EIO ;
goto fail ;
}
2012-11-24 04:29:36 +02:00
switch ( k ) {
case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER :
case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER :
2009-02-24 18:41:38 +02:00
goto success ;
2012-11-24 04:29:36 +02:00
case DBUS_REQUEST_NAME_REPLY_EXISTS :
break ;
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE : /* DBUS_NAME_FLAG_DO_NOT_QUEUE was specified */
default : /* unknown reply returned */
jack_error ( " request name reply with unexpected value %d. " , k ) ;
assert ( 0 ) ;
2009-02-24 18:41:38 +02:00
r = - EIO ;
goto fail ;
}
if ( priority < = INT32_MIN ) {
r = - EBUSY ;
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_RELEASE_DENIED , " Device reservation request with priority % " PRIi32 " denied for \" %s \" " , priority , device_name ) ;
2009-02-24 18:41:38 +02:00
goto fail ;
}
if ( ! ( m = dbus_message_new_method_call (
d - > service_name ,
d - > object_path ,
" org.freedesktop.ReserveDevice1 " ,
" RequestRelease " ) ) ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot allocate memory for RequestRelease method call " ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
if ( ! dbus_message_append_args (
m ,
DBUS_TYPE_INT32 , & d - > priority ,
DBUS_TYPE_INVALID ) ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot append args for RequestRelease method call " ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
if ( ! ( reply = dbus_connection_send_with_reply_and_block (
d - > connection ,
m ,
5000 , /* 5s */
error ) ) ) {
if ( dbus_error_has_name ( error , DBUS_ERROR_TIMED_OUT ) | |
dbus_error_has_name ( error , DBUS_ERROR_UNKNOWN_METHOD ) | |
dbus_error_has_name ( error , DBUS_ERROR_NO_REPLY ) ) {
/* This must be treated as denied. */
2012-11-24 04:29:36 +02:00
jack_info ( " Device reservation request with priority % " PRIi32 " denied for \" %s \" : %s (%s) " , priority , device_name , error - > name , error - > message ) ;
2009-02-24 18:41:38 +02:00
r = - EBUSY ;
goto fail ;
}
2012-11-24 04:29:36 +02:00
jack_error ( " dbus_connection_send_with_reply_and_block(RequestRelease) failed. " ) ;
2009-02-24 18:41:38 +02:00
r = - EIO ;
goto fail ;
}
if ( ! dbus_message_get_args (
reply ,
error ,
DBUS_TYPE_BOOLEAN , & good ,
DBUS_TYPE_INVALID ) ) {
2012-11-24 04:29:36 +02:00
jack_error ( " RequestRelease() reply is invalid. " ) ;
2009-02-24 18:41:38 +02:00
r = - EIO ;
goto fail ;
}
if ( ! good ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_RELEASE_DENIED , " Device reservation request with priority % " PRIi32 " denied for \" %s \" via RequestRelease() " , priority , device_name ) ;
2009-02-24 18:41:38 +02:00
r = - EBUSY ;
goto fail ;
}
if ( ( k = dbus_bus_request_name (
d - > connection ,
d - > service_name ,
DBUS_NAME_FLAG_DO_NOT_QUEUE |
( priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0 ) |
DBUS_NAME_FLAG_REPLACE_EXISTING ,
error ) ) < 0 ) {
2012-11-24 04:29:36 +02:00
jack_error ( " dbus_bus_request_name() failed. (2) " ) ;
2009-02-24 18:41:38 +02:00
r = - EIO ;
goto fail ;
}
if ( k ! = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ) {
2012-11-24 04:29:36 +02:00
/* this is racy, another contender may have acquired the device */
dbus_set_error ( error , RESERVE_ERROR_PROTOCOL_VIOLATION , " request name reply is not DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER but %d. " , k ) ;
2009-02-24 18:41:38 +02:00
r = - EIO ;
goto fail ;
}
success :
d - > owning = 1 ;
2012-11-24 04:29:36 +02:00
if ( ! ( dbus_connection_try_register_object_path (
2009-02-24 18:41:38 +02:00
d - > connection ,
d - > object_path ,
& vtable ,
2012-11-24 04:29:36 +02:00
d ,
error ) ) ) {
jack_error ( " cannot register object path \" %s \" : %s " , d - > object_path , error - > message ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
d - > registered = 1 ;
if ( ! dbus_connection_add_filter (
d - > connection ,
filter_handler ,
d ,
NULL ) ) {
2012-11-24 04:29:36 +02:00
dbus_set_error ( error , RESERVE_ERROR_NO_MEMORY , " Cannot add filter " ) ;
2009-02-24 18:41:38 +02:00
r = - ENOMEM ;
goto fail ;
}
d - > filtering = 1 ;
* _d = d ;
return 0 ;
fail :
if ( m )
dbus_message_unref ( m ) ;
if ( reply )
dbus_message_unref ( reply ) ;
if ( & _error = = error )
dbus_error_free ( & _error ) ;
if ( d )
rd_release ( d ) ;
return r ;
}
void rd_release (
rd_device * d ) {
if ( ! d )
return ;
assert ( d - > ref > 0 ) ;
if ( - - d - > ref )
return ;
if ( d - > filtering )
dbus_connection_remove_filter (
d - > connection ,
filter_handler ,
d ) ;
if ( d - > registered )
dbus_connection_unregister_object_path (
d - > connection ,
d - > object_path ) ;
if ( d - > owning ) {
DBusError error ;
dbus_error_init ( & error ) ;
dbus_bus_release_name (
d - > connection ,
d - > service_name ,
& error ) ;
dbus_error_free ( & error ) ;
}
free ( d - > device_name ) ;
free ( d - > application_name ) ;
free ( d - > application_device_name ) ;
free ( d - > service_name ) ;
free ( d - > object_path ) ;
if ( d - > connection )
dbus_connection_unref ( d - > connection ) ;
free ( d ) ;
}
int rd_set_application_device_name ( rd_device * d , const char * n ) {
char * t ;
if ( ! d )
return - EINVAL ;
assert ( d - > ref > 0 ) ;
if ( ! ( t = strdup ( n ) ) )
return - ENOMEM ;
free ( d - > application_device_name ) ;
d - > application_device_name = t ;
return 0 ;
}
void rd_set_userdata ( rd_device * d , void * userdata ) {
if ( ! d )
return ;
assert ( d - > ref > 0 ) ;
d - > userdata = userdata ;
}
void * rd_get_userdata ( rd_device * d ) {
if ( ! d )
return NULL ;
assert ( d - > ref > 0 ) ;
return d - > userdata ;
}