|
|
@@ -30,69 +30,96 @@ |
|
|
|
#include "jackdbus.h" |
|
|
|
#include "controller_internal.h" |
|
|
|
#include "jack/session.h" |
|
|
|
#include "common/JackError.h" |
|
|
|
|
|
|
|
#define controller_ptr ((struct jack_controller *)call->context) |
|
|
|
#define JACK_DBUS_IFACE_NAME "org.jackaudio.SessionManager" |
|
|
|
|
|
|
|
static |
|
|
|
void |
|
|
|
jack_controller_dbus_session_notify( |
|
|
|
struct jack_dbus_method_call * call) |
|
|
|
jack_controller_control_send_signal_session_state_changed( |
|
|
|
jack_session_event_type_t type, |
|
|
|
const char * target) |
|
|
|
{ |
|
|
|
const char * target; |
|
|
|
dbus_uint32_t u32; |
|
|
|
const char * path; |
|
|
|
jack_session_event_type_t type; |
|
|
|
jack_session_command_t * commands; |
|
|
|
const jack_session_command_t * cmd_ptr; |
|
|
|
DBusMessageIter top_iter, array_iter, struct_iter; |
|
|
|
|
|
|
|
if (!jack_dbus_get_method_args( |
|
|
|
call, |
|
|
|
DBUS_TYPE_STRING, |
|
|
|
&target, |
|
|
|
DBUS_TYPE_UINT32, |
|
|
|
&u32, |
|
|
|
DBUS_TYPE_STRING, |
|
|
|
&path, |
|
|
|
DBUS_TYPE_INVALID)) |
|
|
|
u32 = type; |
|
|
|
if (target == NULL) |
|
|
|
{ |
|
|
|
/* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ |
|
|
|
goto exit; |
|
|
|
target = ""; |
|
|
|
} |
|
|
|
|
|
|
|
jack_info("Session notify initiated. target='%s', type=%"PRIu32", path='%s'", target, u32, path); |
|
|
|
jack_dbus_send_signal( |
|
|
|
JACK_CONTROLLER_OBJECT_PATH, |
|
|
|
JACK_DBUS_IFACE_NAME, |
|
|
|
"StateChanged", |
|
|
|
DBUS_TYPE_UINT32, |
|
|
|
&u32, |
|
|
|
DBUS_TYPE_STRING, |
|
|
|
&target, |
|
|
|
DBUS_TYPE_INVALID); |
|
|
|
} |
|
|
|
|
|
|
|
if (*target == 0) |
|
|
|
static bool start_detached_thread(void * (* start_routine)(void *), void * arg) |
|
|
|
{ |
|
|
|
int ret; |
|
|
|
static pthread_attr_t attr; |
|
|
|
pthread_t tid; |
|
|
|
|
|
|
|
ret = pthread_attr_init(&attr); |
|
|
|
if (ret != 0) |
|
|
|
{ |
|
|
|
target = NULL; |
|
|
|
jack_error("pthread_attr_init() failed with %d", ret); |
|
|
|
goto exit; |
|
|
|
} |
|
|
|
|
|
|
|
type = (jack_session_event_type_t)u32; |
|
|
|
ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
|
|
|
if (ret != 0) |
|
|
|
{ |
|
|
|
jack_error("pthread_attr_setdetachstate() failed with %d", ret); |
|
|
|
goto destroy_attr; |
|
|
|
} |
|
|
|
|
|
|
|
if (type != JackSessionSave && |
|
|
|
type != JackSessionSaveAndQuit && |
|
|
|
type != JackSessionSaveTemplate) |
|
|
|
ret = pthread_create(&tid, &attr, start_routine, arg); |
|
|
|
if (ret != 0) |
|
|
|
{ |
|
|
|
jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid session event type %" PRIu32, u32); |
|
|
|
goto exit; |
|
|
|
jack_error("pthread_create() failed with %d", ret); |
|
|
|
goto destroy_attr; |
|
|
|
} |
|
|
|
|
|
|
|
commands = jack_session_notify(controller_ptr->client, target, type, path); |
|
|
|
jack_log("Detached thread %d created", (int)tid); |
|
|
|
|
|
|
|
destroy_attr: |
|
|
|
pthread_attr_destroy(&attr); |
|
|
|
exit: |
|
|
|
return ret == 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void send_session_notify_reply(struct jack_session_pending_command * pending_cmd_ptr, jack_session_command_t * commands) |
|
|
|
{ |
|
|
|
struct jack_dbus_method_call call; |
|
|
|
const jack_session_command_t * cmd_ptr; |
|
|
|
DBusMessageIter top_iter, array_iter, struct_iter; |
|
|
|
dbus_uint32_t u32; |
|
|
|
|
|
|
|
/* jack_dbus_error() wants call struct */ |
|
|
|
call.message = pending_cmd_ptr->message; |
|
|
|
call.connection = pending_cmd_ptr->connection; |
|
|
|
|
|
|
|
if (commands == NULL) |
|
|
|
{ |
|
|
|
jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_session_notify() failed"); |
|
|
|
goto exit; |
|
|
|
jack_dbus_error(&call, JACK_DBUS_ERROR_GENERIC, "jack_session_notify() failed"); |
|
|
|
goto send_reply; |
|
|
|
} |
|
|
|
|
|
|
|
jack_info("Session notify complete, commands follow:"); |
|
|
|
|
|
|
|
call->reply = dbus_message_new_method_return(call->message); |
|
|
|
if (call->reply == NULL) |
|
|
|
call.reply = dbus_message_new_method_return(pending_cmd_ptr->message); |
|
|
|
if (call.reply == NULL) |
|
|
|
{ |
|
|
|
goto oom; |
|
|
|
} |
|
|
|
|
|
|
|
dbus_message_iter_init_append(call->reply, &top_iter); |
|
|
|
dbus_message_iter_init_append(call.reply, &top_iter); |
|
|
|
|
|
|
|
if (!dbus_message_iter_open_container(&top_iter, DBUS_TYPE_ARRAY, "(sssu)", &array_iter)) |
|
|
|
{ |
|
|
@@ -142,21 +169,180 @@ jack_controller_dbus_session_notify( |
|
|
|
goto unref; |
|
|
|
} |
|
|
|
|
|
|
|
goto free; |
|
|
|
goto send_reply; |
|
|
|
|
|
|
|
close_struct: |
|
|
|
dbus_message_iter_close_container(&array_iter, &struct_iter); |
|
|
|
close_array: |
|
|
|
dbus_message_iter_close_container(&top_iter, &array_iter); |
|
|
|
unref: |
|
|
|
dbus_message_unref(call->reply); |
|
|
|
call->reply = NULL; |
|
|
|
dbus_message_unref(call.reply); |
|
|
|
goto oom; |
|
|
|
|
|
|
|
send_reply: |
|
|
|
if (call.reply != NULL) |
|
|
|
{ |
|
|
|
if (!dbus_connection_send(pending_cmd_ptr->connection, call.reply, NULL)) |
|
|
|
{ |
|
|
|
jack_error("Ran out of memory trying to queue method return"); |
|
|
|
} |
|
|
|
|
|
|
|
dbus_connection_flush(pending_cmd_ptr->connection); |
|
|
|
dbus_message_unref(call.reply); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
oom: |
|
|
|
jack_error("Ran out of memory trying to construct method return"); |
|
|
|
free: |
|
|
|
jack_session_commands_free(commands); |
|
|
|
exit: |
|
|
|
return; |
|
|
|
jack_error("Ran out of memory trying to construct method return"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#define controller_ptr ((struct jack_controller *)context) |
|
|
|
void * jack_controller_process_session_command_thread(void * context) |
|
|
|
{ |
|
|
|
struct jack_session_pending_command * pending_cmd_ptr; |
|
|
|
jack_session_command_t * commands; |
|
|
|
|
|
|
|
jack_log("jack_controller_process_session_command_thread enter"); |
|
|
|
|
|
|
|
pthread_mutex_lock(&controller_ptr->lock); |
|
|
|
loop: |
|
|
|
/* get next command */ |
|
|
|
assert(!list_empty(&controller_ptr->session_pending_commands)); |
|
|
|
pending_cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings); |
|
|
|
pthread_mutex_unlock(&controller_ptr->lock); |
|
|
|
|
|
|
|
jack_info("Session notify initiated. target='%s', type=%d, path='%s'", pending_cmd_ptr->target, (int)pending_cmd_ptr->type, pending_cmd_ptr->path); |
|
|
|
|
|
|
|
jack_controller_control_send_signal_session_state_changed(pending_cmd_ptr->type, pending_cmd_ptr->target); |
|
|
|
|
|
|
|
commands = jack_session_notify(controller_ptr->client, pending_cmd_ptr->target, pending_cmd_ptr->type, pending_cmd_ptr->path); |
|
|
|
usleep(5000000); |
|
|
|
send_session_notify_reply(pending_cmd_ptr, commands); |
|
|
|
if (commands != NULL) |
|
|
|
{ |
|
|
|
jack_session_commands_free(commands); |
|
|
|
} |
|
|
|
|
|
|
|
pthread_mutex_lock(&controller_ptr->lock); |
|
|
|
|
|
|
|
/* keep state consistent by sending signal after to lock */ |
|
|
|
/* otherwise the main thread may receive not-to-be-queued request and fail */ |
|
|
|
jack_controller_control_send_signal_session_state_changed(0, NULL); |
|
|
|
|
|
|
|
/* remove the head of the list (queue) */ |
|
|
|
assert(!list_empty(&controller_ptr->session_pending_commands)); |
|
|
|
assert(pending_cmd_ptr == list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings)); |
|
|
|
list_del(&pending_cmd_ptr->siblings); |
|
|
|
|
|
|
|
/* command cleanup */ |
|
|
|
dbus_message_unref(pending_cmd_ptr->message); |
|
|
|
dbus_connection_ref(pending_cmd_ptr->connection); |
|
|
|
free(pending_cmd_ptr); |
|
|
|
|
|
|
|
/* If there are more commands, process them. Otherwise - exit the thread */ |
|
|
|
if (!list_empty(&controller_ptr->session_pending_commands)) |
|
|
|
{ |
|
|
|
goto loop; |
|
|
|
} |
|
|
|
|
|
|
|
pthread_mutex_unlock(&controller_ptr->lock); |
|
|
|
|
|
|
|
jack_log("jack_controller_process_session_command_thread exit"); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
#undef controller_ptr |
|
|
|
#define controller_ptr ((struct jack_controller *)call->context) |
|
|
|
|
|
|
|
static |
|
|
|
void |
|
|
|
jack_controller_dbus_session_notify( |
|
|
|
struct jack_dbus_method_call * call) |
|
|
|
{ |
|
|
|
dbus_bool_t queue; |
|
|
|
const char * target; |
|
|
|
dbus_uint32_t u32; |
|
|
|
const char * path; |
|
|
|
jack_session_event_type_t type; |
|
|
|
struct jack_session_pending_command * cmd_ptr; |
|
|
|
|
|
|
|
if (!controller_ptr->started) |
|
|
|
{ |
|
|
|
jack_dbus_only_error(call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute method '%s' with stopped JACK server", call->method_name); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!jack_dbus_get_method_args( |
|
|
|
call, |
|
|
|
DBUS_TYPE_BOOLEAN, |
|
|
|
&queue, |
|
|
|
DBUS_TYPE_STRING, |
|
|
|
&target, |
|
|
|
DBUS_TYPE_UINT32, |
|
|
|
&u32, |
|
|
|
DBUS_TYPE_STRING, |
|
|
|
&path, |
|
|
|
DBUS_TYPE_INVALID)) |
|
|
|
{ |
|
|
|
/* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (*target == 0) |
|
|
|
{ |
|
|
|
target = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
type = (jack_session_event_type_t)u32; |
|
|
|
|
|
|
|
if (type != JackSessionSave && |
|
|
|
type != JackSessionSaveAndQuit && |
|
|
|
type != JackSessionSaveTemplate) |
|
|
|
{ |
|
|
|
jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid session event type %" PRIu32, u32); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
pthread_mutex_lock(&controller_ptr->lock); |
|
|
|
if (list_empty(&controller_ptr->session_pending_commands)) |
|
|
|
{ |
|
|
|
if (!start_detached_thread(jack_controller_process_session_command_thread, controller_ptr)) |
|
|
|
{ |
|
|
|
jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Cannot start thread to process the command"); |
|
|
|
goto unlock; |
|
|
|
} |
|
|
|
|
|
|
|
jack_log("Session notify thread started"); |
|
|
|
} |
|
|
|
else if (!queue) |
|
|
|
{ |
|
|
|
jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Busy"); |
|
|
|
goto unlock; |
|
|
|
} |
|
|
|
|
|
|
|
cmd_ptr = malloc(sizeof(struct jack_session_pending_command)); |
|
|
|
if (cmd_ptr == NULL) |
|
|
|
{ |
|
|
|
jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "malloc() failed for jack_session_pending_command struct"); |
|
|
|
goto unlock; |
|
|
|
} |
|
|
|
|
|
|
|
cmd_ptr->message = dbus_message_ref(call->message); |
|
|
|
call->message = NULL; /* mark that reply will be sent asynchronously */ |
|
|
|
cmd_ptr->connection = dbus_connection_ref(call->connection); |
|
|
|
|
|
|
|
/* it is safe to use the retrived pointers because we already made an additional message reference */ |
|
|
|
cmd_ptr->type = type; |
|
|
|
cmd_ptr->target = target; |
|
|
|
cmd_ptr->path = path; |
|
|
|
|
|
|
|
list_add_tail(&cmd_ptr->siblings, &controller_ptr->session_pending_commands); |
|
|
|
|
|
|
|
jack_log("Session notify scheduled. target='%s', type=%"PRIu32", path='%s'", target, u32, path); |
|
|
|
|
|
|
|
unlock: |
|
|
|
pthread_mutex_unlock(&controller_ptr->lock); |
|
|
|
} |
|
|
|
|
|
|
|
static |
|
|
@@ -278,9 +464,60 @@ jack_controller_dbus_has_session_callback( |
|
|
|
jack_dbus_construct_method_return_single(call, DBUS_TYPE_BOOLEAN, retval); |
|
|
|
} |
|
|
|
|
|
|
|
static |
|
|
|
void |
|
|
|
jack_controller_dbus_get_session_state( |
|
|
|
struct jack_dbus_method_call * call) |
|
|
|
{ |
|
|
|
DBusMessageIter iter; |
|
|
|
struct jack_session_pending_command * cmd_ptr; |
|
|
|
const char * target; |
|
|
|
dbus_uint32_t type; |
|
|
|
bool append_failed; |
|
|
|
|
|
|
|
call->reply = dbus_message_new_method_return(call->message); |
|
|
|
if (call->reply == NULL) |
|
|
|
{ |
|
|
|
goto oom; |
|
|
|
} |
|
|
|
|
|
|
|
dbus_message_iter_init_append(call->reply, &iter); |
|
|
|
|
|
|
|
pthread_mutex_lock(&controller_ptr->lock); |
|
|
|
|
|
|
|
if (list_empty(&controller_ptr->session_pending_commands)) |
|
|
|
{ |
|
|
|
type = 0; |
|
|
|
target = ""; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings); |
|
|
|
type = (dbus_uint32_t)cmd_ptr->type; |
|
|
|
target = cmd_ptr->target; |
|
|
|
} |
|
|
|
|
|
|
|
append_failed = |
|
|
|
!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &type) || |
|
|
|
!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &target); |
|
|
|
|
|
|
|
pthread_mutex_unlock(&controller_ptr->lock); |
|
|
|
|
|
|
|
if (!append_failed) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
dbus_message_unref(call->reply); |
|
|
|
call->reply = NULL; |
|
|
|
oom: |
|
|
|
jack_error("Ran out of memory trying to construct method return"); |
|
|
|
} |
|
|
|
|
|
|
|
#undef controller_ptr |
|
|
|
|
|
|
|
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(Notify) |
|
|
|
JACK_DBUS_METHOD_ARGUMENT("queue", DBUS_TYPE_BOOLEAN_AS_STRING, false) |
|
|
|
JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, false) |
|
|
|
JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, false) |
|
|
|
JACK_DBUS_METHOD_ARGUMENT("path", DBUS_TYPE_STRING_AS_STRING, false) |
|
|
@@ -307,14 +544,30 @@ JACK_DBUS_METHOD_ARGUMENTS_BEGIN(HasSessionCallback) |
|
|
|
JACK_DBUS_METHOD_ARGUMENT("has_session_callback", DBUS_TYPE_BOOLEAN_AS_STRING, true) |
|
|
|
JACK_DBUS_METHOD_ARGUMENTS_END |
|
|
|
|
|
|
|
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetState) |
|
|
|
JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, true) |
|
|
|
JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, true) |
|
|
|
JACK_DBUS_METHOD_ARGUMENTS_END |
|
|
|
|
|
|
|
JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(StateChanged) |
|
|
|
JACK_DBUS_SIGNAL_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING) |
|
|
|
JACK_DBUS_SIGNAL_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING) |
|
|
|
JACK_DBUS_SIGNAL_ARGUMENTS_END |
|
|
|
|
|
|
|
JACK_DBUS_METHODS_BEGIN |
|
|
|
JACK_DBUS_METHOD_DESCRIBE(Notify, jack_controller_dbus_session_notify) |
|
|
|
JACK_DBUS_METHOD_DESCRIBE(GetUuidForClientName, jack_controller_dbus_get_uuid_for_client_name) |
|
|
|
JACK_DBUS_METHOD_DESCRIBE(GetClientNameByUuid, jack_controller_dbus_get_client_name_by_uuid) |
|
|
|
JACK_DBUS_METHOD_DESCRIBE(ReserveClientName, jack_controller_dbus_reserve_client_name) |
|
|
|
JACK_DBUS_METHOD_DESCRIBE(HasSessionCallback, jack_controller_dbus_has_session_callback) |
|
|
|
JACK_DBUS_METHOD_DESCRIBE(GetState, jack_controller_dbus_get_session_state) |
|
|
|
JACK_DBUS_METHODS_END |
|
|
|
|
|
|
|
JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_session_manager, "org.jackaudio.SessionManager") |
|
|
|
JACK_DBUS_SIGNALS_BEGIN |
|
|
|
JACK_DBUS_SIGNAL_DESCRIBE(StateChanged) |
|
|
|
JACK_DBUS_SIGNALS_END |
|
|
|
|
|
|
|
JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_session_manager, JACK_DBUS_IFACE_NAME) |
|
|
|
JACK_DBUS_IFACE_EXPOSE_METHODS |
|
|
|
JACK_DBUS_IFACE_EXPOSE_SIGNALS |
|
|
|
JACK_DBUS_IFACE_END |