From 94eea6f4dd3506f1bf9bfef7bd90cfeec0d0b8f9 Mon Sep 17 00:00:00 2001 From: Nedko Arnaudov Date: Thu, 21 Jul 2011 00:35:21 +0300 Subject: [PATCH 1/8] jackdbus: SessionManager interface --- dbus/controller.c | 1 + dbus/controller_iface_session_manager.c | 312 ++++++++++++++++++++++++ dbus/controller_internal.h | 1 + dbus/wscript | 1 + 4 files changed, 315 insertions(+) create mode 100644 dbus/controller_iface_session_manager.c diff --git a/dbus/controller.c b/dbus/controller.c index ce0f1ec9..42981c1c 100644 --- a/dbus/controller.c +++ b/dbus/controller.c @@ -38,6 +38,7 @@ struct jack_dbus_interface_descriptor * g_jackcontroller_interfaces[] = &g_jack_controller_iface_control, &g_jack_controller_iface_configure, &g_jack_controller_iface_patchbay, + &g_jack_controller_iface_session_manager, &g_jack_controller_iface_transport, NULL }; diff --git a/dbus/controller_iface_session_manager.c b/dbus/controller_iface_session_manager.c new file mode 100644 index 00000000..7292cd6c --- /dev/null +++ b/dbus/controller_iface_session_manager.c @@ -0,0 +1,312 @@ +/* -*- Mode: C ; c-basic-offset: 4 -*- */ +/* + Copyright (C) 2011 Nedko Arnaudov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "jackdbus.h" +#include "controller_internal.h" +#include "jack/session.h" + +#define controller_ptr ((struct jack_controller *)call->context) + +static +void +jack_controller_dbus_session_notify( + struct jack_dbus_method_call * call) +{ + 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)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + goto exit; + } + + 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); + goto exit; + } + + commands = jack_session_notify(controller_ptr->client, target, type, path); + if (commands == NULL) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_session_notify() failed"); + goto exit; + } + + call->reply = dbus_message_new_method_return(call->message); + if (call->reply == NULL) + { + goto oom; + } + + dbus_message_iter_init_append(call->reply, &top_iter); + + if (!dbus_message_iter_open_container(&top_iter, DBUS_TYPE_ARRAY, "(sssu)", &array_iter)) + { + goto unref; + } + + for (cmd_ptr = commands; cmd_ptr->uuid != NULL; cmd_ptr++) + { + if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) + { + goto close_array; + } + + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->uuid)) + { + goto close_struct; + } + + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->client_name)) + { + goto close_struct; + } + + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->command)) + { + goto close_struct; + } + + u32 = cmd_ptr->flags; + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &u32)) + { + goto close_struct; + } + + if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) + { + goto close_array; + } + } + + if (!dbus_message_iter_close_container(&top_iter, &array_iter)) + { + goto unref; + } + + goto free; + +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; +oom: + jack_error("Ran out of memory trying to construct method return"); +free: + jack_session_commands_free(commands); +exit: + return; +} + +static +void +jack_controller_dbus_get_uuid_for_client_name( + struct jack_dbus_method_call * call) +{ + const char * client_name; + char * client_uuid; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_name, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + client_uuid = jack_get_uuid_for_client_name(controller_ptr->client, client_name); + if (client_uuid == NULL) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_uuid_for_client_name(\"%s\") failed", client_name); + return; + } + + jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_uuid); + free(client_uuid); +} + +static +void +jack_controller_dbus_get_client_name_by_uuid( + struct jack_dbus_method_call * call) +{ + const char * client_uuid; + char * client_name; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_uuid, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + client_name = jack_get_client_name_by_uuid(controller_ptr->client, client_uuid); + if (client_name == NULL) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_client_name_by_uuid(\"%s\") failed", client_uuid); + return; + } + + jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_name); + free(client_name); +} + +static +void +jack_controller_dbus_reserve_client_name( + struct jack_dbus_method_call * call) +{ + int ret; + const char * client_name; + const char * client_uuid; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_name, + DBUS_TYPE_STRING, + &client_uuid, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + ret = jack_reserve_client_name(controller_ptr->client, client_name, client_uuid); + if (ret < 0) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_reserve_client_name(name=\"%s\", uuid=\"%s\") failed (%d)", client_name, client_uuid, ret); + return; + } + + jack_dbus_construct_method_return_empty(call); +} + +static +void +jack_controller_dbus_has_session_callback( + struct jack_dbus_method_call * call) +{ + int ret; + const char * client_name; + message_arg_t retval; + + if (!jack_dbus_get_method_args( + call, + DBUS_TYPE_STRING, + &client_name, + DBUS_TYPE_INVALID)) + { + /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */ + return; + } + + ret = jack_client_has_session_callback(controller_ptr->client, client_name); + if (ret < 0) + { + jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_client_has_session_callback(\"%s\") failed (%d)", client_name, ret); + return; + } + + retval.boolean = ret; + jack_dbus_construct_method_return_single(call, DBUS_TYPE_BOOLEAN, retval); +} + +#undef controller_ptr + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(Notify) + 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) + JACK_DBUS_METHOD_ARGUMENT("result", "a(sssu)", true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetUuidForClientName) + JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetClientNameByUuid) + JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, true) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ReserveClientName) + JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false) +JACK_DBUS_METHOD_ARGUMENTS_END + +JACK_DBUS_METHOD_ARGUMENTS_BEGIN(HasSessionCallback) + JACK_DBUS_METHOD_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING, false) + JACK_DBUS_METHOD_ARGUMENT("has_session_callback", DBUS_TYPE_BOOLEAN_AS_STRING, true) +JACK_DBUS_METHOD_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_METHODS_END + +JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_session_manager, "org.jackaudio.SessionManager") + JACK_DBUS_IFACE_EXPOSE_METHODS +JACK_DBUS_IFACE_END diff --git a/dbus/controller_internal.h b/dbus/controller_internal.h index 1b9c7129..1d4d40d3 100644 --- a/dbus/controller_internal.h +++ b/dbus/controller_internal.h @@ -183,6 +183,7 @@ extern struct jack_dbus_interface_descriptor g_jack_controller_iface_introspecta extern struct jack_dbus_interface_descriptor g_jack_controller_iface_control; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_configure; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_patchbay; +extern struct jack_dbus_interface_descriptor g_jack_controller_iface_session_manager; extern struct jack_dbus_interface_descriptor g_jack_controller_iface_transport; #endif /* #ifndef CONTROLLER_INTERNAL_H__04D54D51_3D79_49A2_A1DA_F8587E9E7F42__INCLUDED */ diff --git a/dbus/wscript b/dbus/wscript index fced985f..703c52c7 100644 --- a/dbus/wscript +++ b/dbus/wscript @@ -58,6 +58,7 @@ def build(bld): 'controller_iface_control.c', 'controller_iface_introspectable.c', 'controller_iface_patchbay.c', + 'controller_iface_session_manager.c', 'controller_iface_transport.c', 'xml.c', 'xml_expat.c', From 248d1f16e671a3ed192caf2b6f3b1a2048407816 Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Thu, 21 Jul 2011 17:56:41 +0200 Subject: [PATCH 2/8] Session API for internal clients in progress. --- common/JackEngine.cpp | 16 ++++++++-------- common/JackEngine.h | 2 +- common/JackInternalClientChannel.h | 29 ++++++++++++++++++++++++++++- common/JackLockedEngine.h | 4 ++-- common/JackRequest.h | 4 ++-- posix/JackSocketClientChannel.cpp | 8 ++++---- posix/JackSocketServerChannel.cpp | 2 +- 7 files changed, 46 insertions(+), 19 deletions(-) diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index dfc3f9af..ca653cc3 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -966,11 +966,11 @@ void JackEngine::SessionNotify(int refnum, const char *target, jack_session_even } char path_buf[JACK_PORT_NAME_SIZE]; - snprintf( path_buf, sizeof(path_buf), "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR ); + snprintf(path_buf, sizeof(path_buf), "%s%s%c", path, client->GetClientControl()->fName, DIR_SEPARATOR); int res = JackTools::MkDir(path_buf); if (res) - jack_error( "JackEngine::SessionNotify: can not create session directory '%s'", path_buf ); + jack_error("JackEngine::SessionNotify: can not create session directory '%s'", path_buf); int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int) type, 0); @@ -978,11 +978,11 @@ void JackEngine::SessionNotify(int refnum, const char *target, jack_session_even fSessionPendingReplies += 1; } else if (result == 1) { char uuid_buf[JACK_UUID_SIZE]; - snprintf( uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID ); - fSessionResult->fCommandList.push_back( JackSessionCommand( uuid_buf, - client->GetClientControl()->fName, - client->GetClientControl()->fSessionCommand, - client->GetClientControl()->fSessionFlags )); + snprintf(uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID); + fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf, + client->GetClientControl()->fName, + client->GetClientControl()->fSessionCommand, + client->GetClientControl()->fSessionFlags)); } } } @@ -1065,7 +1065,7 @@ void JackEngine::ReserveClientName(const char *name, const char *uuid, int *resu *result = 0; } -void JackEngine::ClientHasSessionCallbackRequest(const char *name, int *result) +void JackEngine::ClientHasSessionCallback(const char *name, int *result) { JackClientInterface* client = NULL; for (int i = 0; i < CLIENT_NUM; i++) { diff --git a/common/JackEngine.h b/common/JackEngine.h index 185c1b16..8fcd6307 100644 --- a/common/JackEngine.h +++ b/common/JackEngine.h @@ -151,7 +151,7 @@ class SERVER_EXPORT JackEngine : public JackLockAble void GetUUIDForClientName(const char *client_name, char *uuid_res, int *result); void GetClientNameForUUID(const char *uuid, char *name_res, int *result); void ReserveClientName(const char *name, const char *uuid, int *result); - void ClientHasSessionCallbackRequest(const char *name, int *result); + void ClientHasSessionCallback(const char *name, int *result); }; diff --git a/common/JackInternalClientChannel.h b/common/JackInternalClientChannel.h index dc81ac8c..650b0ccc 100644 --- a/common/JackInternalClientChannel.h +++ b/common/JackInternalClientChannel.h @@ -146,7 +146,34 @@ class JackInternalClientChannel : public detail::JackClientChannelInterface void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, jack_session_command_t** result) { - *result = NULL; + //*result = fEngine->SessionNotify(refnum, target, type, path, result); + // TODO + } + + void SessionReply(int refnum, int* result) + { + fEngine->SessionReply(refnum); + *result = 0; + } + + void GetUUIDForClientName(int refnum, const char* client_name, char* uuid_res, int* result) + { + fEngine->GetUUIDForClientName(client_name, uuid_res, result); + } + + void GetClientNameForUUID(int refnum, const char* uuid, char* name_res, int* result) + { + fEngine->GetClientNameForUUID(uuid, name_res, result); + } + + void ReserveClientName(int refnum, const char* client_name, const char *uuid, int* result) + { + fEngine->ReserveClientName(client_name, uuid, result); + } + + void ClientHasSessionCallback(const char* client_name, int* result) + { + fEngine->ClientHasSessionCallback(client_name, result); } diff --git a/common/JackLockedEngine.h b/common/JackLockedEngine.h index cba72a26..d96e8678 100644 --- a/common/JackLockedEngine.h +++ b/common/JackLockedEngine.h @@ -363,11 +363,11 @@ class SERVER_EXPORT JackLockedEngine CATCH_EXCEPTION } - void ClientHasSessionCallbackRequest(const char *name, int *result) + void ClientHasSessionCallback(const char *name, int *result) { TRY_CALL JackLock lock(&fEngine); - fEngine.ClientHasSessionCallbackRequest(name, result); + fEngine.ClientHasSessionCallback(name, result); CATCH_EXCEPTION } }; diff --git a/common/JackRequest.h b/common/JackRequest.h index 90d0644a..71f47b4b 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -1142,7 +1142,7 @@ struct JackSessionNotifyResult : public JackResult int Read(JackChannelTransaction* trans) { CheckRes(JackResult::Read(trans)); - while(1) { + while (true) { JackSessionCommand buffer; CheckRes(trans->Read(buffer.fUUID, sizeof(buffer.fUUID))); @@ -1164,7 +1164,7 @@ struct JackSessionNotifyResult : public JackResult terminator[0] = '\0'; CheckRes(JackResult::Write(trans)); - for (std::list::iterator i=fCommandList.begin(); i!=fCommandList.end(); i++) { + for (std::list::iterator i = fCommandList.begin(); i != fCommandList.end(); i++) { CheckRes(trans->Write(i->fUUID, sizeof(i->fUUID))); CheckRes(trans->Write(i->fClientName, sizeof(i->fClientName))); CheckRes(trans->Write(i->fCommand, sizeof(i->fCommand))); diff --git a/posix/JackSocketClientChannel.cpp b/posix/JackSocketClientChannel.cpp index 23555c1d..23d62e98 100644 --- a/posix/JackSocketClientChannel.cpp +++ b/posix/JackSocketClientChannel.cpp @@ -262,10 +262,10 @@ void JackSocketClientChannel::SessionNotify(int refnum, const char* target, jack jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (res.fCommandList.size() + 1)); int i = 0; - for (std::list::iterator ci=res.fCommandList.begin(); ci!=res.fCommandList.end(); ci++) { - session_command[i].uuid = strdup( ci->fUUID ); - session_command[i].client_name = strdup( ci->fClientName ); - session_command[i].command = strdup( ci->fCommand ); + for (std::list::iterator ci = res.fCommandList.begin(); ci != res.fCommandList.end(); ci++) { + session_command[i].uuid = strdup(ci->fUUID); + session_command[i].client_name = strdup(ci->fClientName); + session_command[i].command = strdup(ci->fCommand); session_command[i].flags = ci->fFlags; i += 1; } diff --git a/posix/JackSocketServerChannel.cpp b/posix/JackSocketServerChannel.cpp index 2c5f267e..fb3fa355 100644 --- a/posix/JackSocketServerChannel.cpp +++ b/posix/JackSocketServerChannel.cpp @@ -487,7 +487,7 @@ bool JackSocketServerChannel::HandleRequest(int fd) JackClientHasSessionCallbackRequest req; JackResult res; if (req.Read(socket) == 0) { - fServer->GetEngine()->ClientHasSessionCallbackRequest(req.fName, &res.fResult); + fServer->GetEngine()->ClientHasSessionCallback(req.fName, &res.fResult); } if (res.Write(socket) < 0) jack_error("JackRequest::ClientHasSessionCallback write error"); From 299bc1bfe9e536e9b25334364f60a7a75d33c21f Mon Sep 17 00:00:00 2001 From: Stephane Letz Date: Fri, 22 Jul 2011 11:15:13 +0200 Subject: [PATCH 3/8] Cleanup. --- common/JackClient.cpp | 24 ++++++++++------- common/JackClient.h | 2 +- common/JackEngine.cpp | 8 +++--- common/JackEngine.h | 4 +-- common/JackRequest.h | 33 +++++++++++++++-------- example-clients/session_notify.c | 14 +++++----- posix/JackSocketServerChannel.cpp | 1 - windows/JackWinNamedPipeClientChannel.cpp | 8 +++--- windows/JackWinNamedPipeServerChannel.cpp | 1 - 9 files changed, 54 insertions(+), 41 deletions(-) diff --git a/common/JackClient.cpp b/common/JackClient.cpp index 8c3c6de5..121e7f91 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -81,6 +81,8 @@ JackClient::JackClient(JackSynchro* table):fThread(this) fThreadFunArg = NULL; fSessionArg = NULL; fLatencyArg = NULL; + + fSessionReply = kPendingSessionReply; } JackClient::~JackClient() @@ -284,17 +286,18 @@ int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, case kSessionCallback: jack_log("JackClient::kSessionCallback"); if (fSession) { - jack_session_event_t *event = (jack_session_event_t *) malloc( sizeof(jack_session_event_t) ); + jack_session_event_t* event = (jack_session_event_t*)malloc( sizeof(jack_session_event_t)); char uuid_buf[JACK_UUID_SIZE]; - event->type = (jack_session_event_type_t) value1; - event->session_dir = strdup( message ); + event->type = (jack_session_event_type_t)value1; + event->session_dir = strdup(message); event->command_line = NULL; - event->flags = (jack_session_flags_t) 0; - snprintf( uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID ); - event->client_uuid = strdup( uuid_buf ); - fImmediateSessionReply = false; + event->flags = (jack_session_flags_t)0; + snprintf(uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID); + event->client_uuid = strdup(uuid_buf); + fSessionReply = kPendingSessionReply; + // Session callback may change fSessionReply by directly using jack_session_reply fSession(event, fSessionArg); - res = (fImmediateSessionReply) ? 1 : 2; + res = fSessionReply; } break; @@ -1232,8 +1235,9 @@ int JackClient::SessionReply(jack_session_event_t* ev) jack_log("JackClient::SessionReply... we are here"); if (fChannel->IsChannelThread()) { - jack_log( "JackClient::SessionReply... in callback reply"); - fImmediateSessionReply = true; + jack_log("JackClient::SessionReply... in callback reply"); + // OK, immediate reply... + fSessionReply = kImmediateSessionReply; return 0; } diff --git a/common/JackClient.h b/common/JackClient.h index 7d8e0864..889a36f1 100644 --- a/common/JackClient.h +++ b/common/JackClient.h @@ -93,7 +93,7 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable JackSynchro* fSynchroTable; std::list fPortList; - bool fImmediateSessionReply; + JackSessionReply fSessionReply; int StartThread(); void SetupDriverSync(bool freewheel); diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index ca653cc3..ff991fde 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -972,11 +972,11 @@ void JackEngine::SessionNotify(int refnum, const char *target, jack_session_even if (res) jack_error("JackEngine::SessionNotify: can not create session directory '%s'", path_buf); - int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int) type, 0); + int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path_buf, (int)type, 0); - if (result == 2) { + if (result == kPendingSessionReply) { fSessionPendingReplies += 1; - } else if (result == 1) { + } else if (result == kImmediateSessionReply) { char uuid_buf[JACK_UUID_SIZE]; snprintf(uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID); fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf, @@ -1000,7 +1000,7 @@ void JackEngine::SessionReply(int refnum) { JackClientInterface* client = fClientTable[refnum]; char uuid_buf[JACK_UUID_SIZE]; - snprintf( uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID); + snprintf(uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID); fSessionResult->fCommandList.push_back(JackSessionCommand(uuid_buf, client->GetClientControl()->fName, client->GetClientControl()->fSessionCommand, diff --git a/common/JackEngine.h b/common/JackEngine.h index 8fcd6307..179d7e07 100644 --- a/common/JackEngine.h +++ b/common/JackEngine.h @@ -54,8 +54,8 @@ class SERVER_EXPORT JackEngine : public JackLockAble jack_time_t fLastSwitchUsecs; int fSessionPendingReplies; - JackChannelTransaction *fSessionTransaction; - JackSessionNotifyResult *fSessionResult; + JackChannelTransaction* fSessionTransaction; + JackSessionNotifyResult* fSessionResult; std::map fReservationMap; int fMaxUUID; diff --git a/common/JackRequest.h b/common/JackRequest.h index 71f47b4b..f5e86543 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -31,7 +31,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. namespace Jack { -#define CheckRes(exp) { if ((exp) < 0) return -1;} +#define CheckRes(exp) { if ((exp) < 0) return -1; } + +/*! +\brief Session API constants. +*/ + +enum JackSessionReply { + + kImmediateSessionReply = 1, + kPendingSessionReply = 2 + +}; /*! \brief Request from client to server. @@ -1119,11 +1130,11 @@ struct JackSessionCommand JackSessionCommand() {} - JackSessionCommand( const char *uuid, const char *clientname, const char *command, jack_session_flags_t flags ) + JackSessionCommand(const char *uuid, const char *clientname, const char *command, jack_session_flags_t flags) { - strncpy( fUUID, uuid, sizeof(fUUID)); - strncpy( fClientName, clientname, sizeof(fClientName)); - strncpy( fCommand, command, sizeof(fCommand)); + strncpy(fUUID, uuid, sizeof(fUUID)); + strncpy(fClientName, clientname, sizeof(fClientName)); + strncpy(fCommand, command, sizeof(fCommand)); fFlags = flags; } }; @@ -1184,19 +1195,20 @@ struct JackSessionNotifyRequest : public JackRequest { char fPath[JACK_MESSAGE_SIZE + 1]; char fDst[JACK_CLIENT_NAME_SIZE + 1]; - jack_session_event_type_t fEventType; - int fRefNum; + jack_session_event_type_t fEventType; + int fRefNum; JackSessionNotifyRequest() {} - JackSessionNotifyRequest(int refnum, const char *path, jack_session_event_type_t type, const char *dst) + JackSessionNotifyRequest(int refnum, const char* path, jack_session_event_type_t type, const char* dst) : JackRequest(JackRequest::kSessionNotify), fEventType(type), fRefNum(refnum) { snprintf(fPath, sizeof(fPath), "%s", path); - if (dst) + if (dst) { snprintf(fDst, sizeof(fDst), "%s", dst); - else + } else { fDst[0] = '\0'; + } } int Read(JackChannelTransaction* trans) @@ -1276,7 +1288,6 @@ struct JackClientNameResult : public JackResult struct JackUUIDResult : public JackResult { - char fUUID[JACK_UUID_SIZE]; JackUUIDResult(): JackResult() diff --git a/example-clients/session_notify.c b/example-clients/session_notify.c index 9cb3f894..1872d8c4 100644 --- a/example-clients/session_notify.c +++ b/example-clients/session_notify.c @@ -2,7 +2,7 @@ * session_notify.c -- ultra minimal session manager * * Copyright (C) 2010 Torben Hohn. - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -141,15 +141,15 @@ int main(int argc, char *argv[]) retval = jack_session_notify( client, NULL, notify_type, save_path ); - for(i=0; retval[i].uuid; i++ ) { + for (i = 0; retval[i].uuid; i++) { printf( "export SESSION_DIR=\"%s%s/\"\n", save_path, retval[i].client_name ); printf( "%s &\n", retval[i].command ); - add_uuid_mapping(retval[i].uuid); + add_uuid_mapping(retval[i].uuid); } printf( "sleep 10\n" ); - for(k=0; retval[k].uuid; k++ ) { + for (k = 0; retval[k].uuid; k++) { char* port_regexp = alloca( jack_client_name_size()+3 ); char* client_name = jack_get_client_name_by_uuid( client, retval[k].uuid ); @@ -163,12 +163,12 @@ int main(int argc, char *argv[]) const char **connections; if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) { for (j = 0; connections[j]; j++) { - char *src = map_port_name_to_uuid_port( ports[i] ); - char *dst = map_port_name_to_uuid_port( connections[j] ); + char *src = map_port_name_to_uuid_port( ports[i] ); + char *dst = map_port_name_to_uuid_port( connections[j] ); printf( "jack_connect -u \"%s\" \"%s\"\n", src, dst ); } jack_free (connections); - } + } } jack_free(ports); diff --git a/posix/JackSocketServerChannel.cpp b/posix/JackSocketServerChannel.cpp index fb3fa355..caaac23c 100644 --- a/posix/JackSocketServerChannel.cpp +++ b/posix/JackSocketServerChannel.cpp @@ -426,7 +426,6 @@ bool JackSocketServerChannel::HandleRequest(int fd) case JackRequest::kSessionNotify: { jack_log("JackRequest::SessionNotify"); JackSessionNotifyRequest req; - JackSessionNotifyResult res; if (req.Read(socket) == 0) { fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, socket); } diff --git a/windows/JackWinNamedPipeClientChannel.cpp b/windows/JackWinNamedPipeClientChannel.cpp index f29f7ff5..e866e80d 100644 --- a/windows/JackWinNamedPipeClientChannel.cpp +++ b/windows/JackWinNamedPipeClientChannel.cpp @@ -263,10 +263,10 @@ void JackWinNamedPipeClientChannel::SessionNotify(int refnum, const char* target jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (res.fCommandList.size() + 1)); int i = 0; - for (std::list::iterator ci=res.fCommandList.begin(); ci!=res.fCommandList.end(); ci++) { - session_command[i].uuid = strdup( ci->fUUID ); - session_command[i].client_name = strdup( ci->fClientName ); - session_command[i].command = strdup( ci->fCommand ); + for (std::list::iterator ci = res.fCommandList.begin(); ci != res.fCommandList.end(); ci++) { + session_command[i].uuid = strdup(ci->fUUID); + session_command[i].client_name = strdup(ci->fClientName); + session_command[i].command = strdup(ci->fCommand); session_command[i].flags = ci->fFlags; i += 1; } diff --git a/windows/JackWinNamedPipeServerChannel.cpp b/windows/JackWinNamedPipeServerChannel.cpp index fd5a8eda..8b3bbdc5 100644 --- a/windows/JackWinNamedPipeServerChannel.cpp +++ b/windows/JackWinNamedPipeServerChannel.cpp @@ -339,7 +339,6 @@ bool JackClientPipeThread::HandleRequest() case JackRequest::kSessionNotify: { jack_log("JackRequest::SessionNotify"); JackSessionNotifyRequest req; - JackSessionNotifyResult res; if (req.Read(fPipe) == 0) { fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, fPipe); } From 1e962ef0797bdc56e8fca0f0d3f83658b40ea0d6 Mon Sep 17 00:00:00 2001 From: Nedko Arnaudov Date: Sun, 7 Aug 2011 04:21:58 +0300 Subject: [PATCH 4/8] Move session command reply array creation to JackSessionNotifyResult This eliminates code duplication in JackWinNamedPipeClientChannel and JackSocketClientChannel. --- common/JackRequest.h | 21 +++++++++++++++++++++ posix/JackSocketClientChannel.cpp | 19 +------------------ windows/JackWinNamedPipeClientChannel.cpp | 19 +------------------ 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/common/JackRequest.h b/common/JackRequest.h index 07ec219c..95a1b7e7 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "types.h" #include #include +#include #include namespace Jack @@ -1188,6 +1189,26 @@ struct JackSessionNotifyResult : public JackResult return 0; } + jack_session_command_t* GetCommands() + { + jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (fCommandList.size() + 1)); + int i = 0; + + for (std::list::iterator ci = fCommandList.begin(); ci != fCommandList.end(); ci++) { + session_command[i].uuid = strdup(ci->fUUID); + session_command[i].client_name = strdup(ci->fClientName); + session_command[i].command = strdup(ci->fCommand); + session_command[i].flags = ci->fFlags; + i += 1; + } + + session_command[i].uuid = NULL; + session_command[i].client_name = NULL; + session_command[i].command = NULL; + session_command[i].flags = (jack_session_flags_t)0; + + return session_command; + } }; /*! diff --git a/posix/JackSocketClientChannel.cpp b/posix/JackSocketClientChannel.cpp index f40ae39a..0dd580a0 100644 --- a/posix/JackSocketClientChannel.cpp +++ b/posix/JackSocketClientChannel.cpp @@ -259,24 +259,7 @@ void JackSocketClientChannel::SessionNotify(int refnum, const char* target, jack JackSessionNotifyResult res; int intresult; ServerSyncCall(&req, &res, &intresult); - - jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (res.fCommandList.size() + 1)); - int i = 0; - - for (std::list::iterator ci = res.fCommandList.begin(); ci != res.fCommandList.end(); ci++) { - session_command[i].uuid = strdup(ci->fUUID); - session_command[i].client_name = strdup(ci->fClientName); - session_command[i].command = strdup(ci->fCommand); - session_command[i].flags = ci->fFlags; - i += 1; - } - - session_command[i].uuid = NULL; - session_command[i].client_name = NULL; - session_command[i].command = NULL; - session_command[i].flags = (jack_session_flags_t)0; - - *result = session_command; + *result = res.GetCommands(); } void JackSocketClientChannel::SessionReply(int refnum, int* result) diff --git a/windows/JackWinNamedPipeClientChannel.cpp b/windows/JackWinNamedPipeClientChannel.cpp index 9bf5d0b9..56481531 100644 --- a/windows/JackWinNamedPipeClientChannel.cpp +++ b/windows/JackWinNamedPipeClientChannel.cpp @@ -263,24 +263,7 @@ void JackWinNamedPipeClientChannel::SessionNotify(int refnum, const char* target JackSessionNotifyResult res; int intresult; ServerSyncCall(&req, &res, &intresult); - - jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (res.fCommandList.size() + 1)); - int i = 0; - - for (std::list::iterator ci = res.fCommandList.begin(); ci != res.fCommandList.end(); ci++) { - session_command[i].uuid = strdup(ci->fUUID); - session_command[i].client_name = strdup(ci->fClientName); - session_command[i].command = strdup(ci->fCommand); - session_command[i].flags = ci->fFlags; - i += 1; - } - - session_command[i].uuid = NULL; - session_command[i].client_name = NULL; - session_command[i].command = NULL; - session_command[i].flags = (jack_session_flags_t)0; - - *result = session_command; + *result = res.GetCommands(); } void JackWinNamedPipeClientChannel::SessionReply(int refnum, int* result) From d9064f5866f011cafb297873cc107db86b414238 Mon Sep 17 00:00:00 2001 From: Nedko Arnaudov Date: Sun, 7 Aug 2011 05:17:16 +0300 Subject: [PATCH 5/8] Implement session api for internal clients --- common/JackEngine.cpp | 18 +++++++++++++--- common/JackEngine.h | 2 +- common/JackInternalClientChannel.h | 12 +++++++++-- common/JackLockedEngine.h | 4 ++-- common/JackRequest.h | 26 +++++++++++++++++++++-- posix/JackSocketServerChannel.cpp | 2 +- windows/JackWinNamedPipeServerChannel.cpp | 2 +- 7 files changed, 54 insertions(+), 12 deletions(-) diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index e4342f81..a1b0a9d2 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -932,12 +932,15 @@ int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name) // Session management //-------------------- -void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket) +void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket, JackSessionNotifyResult** result) { if (fSessionPendingReplies != 0) { JackSessionNotifyResult res(-1); res.Write(socket); jack_log("JackEngine::SessionNotify ... busy"); + if (result != NULL) { + *result = NULL; + } return; } @@ -982,9 +985,15 @@ void JackEngine::SessionNotify(int refnum, const char *target, jack_session_even } } + if (result != NULL) { + *result = fSessionResult; + } + if (fSessionPendingReplies == 0) { fSessionResult->Write(socket); - delete fSessionResult; + if (result == NULL) { + delete fSessionResult; + } fSessionResult = NULL; } else { fSessionTransaction = socket; @@ -1004,7 +1013,10 @@ void JackEngine::SessionReply(int refnum) if (fSessionPendingReplies == 0) { fSessionResult->Write(fSessionTransaction); - delete fSessionResult; + if (fSessionTransaction != NULL) + { + delete fSessionResult; + } fSessionResult = NULL; } } diff --git a/common/JackEngine.h b/common/JackEngine.h index 179d7e07..ca38089e 100644 --- a/common/JackEngine.h +++ b/common/JackEngine.h @@ -145,7 +145,7 @@ class SERVER_EXPORT JackEngine : public JackLockAble void NotifyQuit(); // Session management - void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket); + void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket, JackSessionNotifyResult** result); void SessionReply(int refnum); void GetUUIDForClientName(const char *client_name, char *uuid_res, int *result); diff --git a/common/JackInternalClientChannel.h b/common/JackInternalClientChannel.h index 69588a99..e2ead4b7 100644 --- a/common/JackInternalClientChannel.h +++ b/common/JackInternalClientChannel.h @@ -146,8 +146,16 @@ class JackInternalClientChannel : public detail::JackClientChannelInterface void SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, jack_session_command_t** result) { - //*result = fEngine->SessionNotify(refnum, target, type, path, result); - // TODO + JackSessionNotifyResult* res; + fEngine->SessionNotify(refnum, target, type, path, NULL, &res); + if (res == NULL) + { + *result = NULL; + return; + } + + *result = res->GetCommands(); + delete(res); } void SessionReply(int refnum, int* result) diff --git a/common/JackLockedEngine.h b/common/JackLockedEngine.h index d96e8678..bdb71ed0 100644 --- a/common/JackLockedEngine.h +++ b/common/JackLockedEngine.h @@ -325,11 +325,11 @@ class SERVER_EXPORT JackLockedEngine CATCH_EXCEPTION } - void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket) + void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket, JackSessionNotifyResult** result) { TRY_CALL JackLock lock(&fEngine); - fEngine.SessionNotify(refnum, target, type, path, socket); + fEngine.SessionNotify(refnum, target, type, path, socket, result); CATCH_EXCEPTION } diff --git a/common/JackRequest.h b/common/JackRequest.h index 95a1b7e7..bc70d38c 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "JackConstants.h" #include "JackPlatformPlug.h" +#include "JackTime.h" #include "types.h" #include #include @@ -1147,15 +1148,21 @@ struct JackSessionNotifyResult : public JackResult { std::list fCommandList; + bool fDone; - JackSessionNotifyResult(): JackResult() + JackSessionNotifyResult(): JackResult(), fDone(false) {} JackSessionNotifyResult(int32_t result) - : JackResult(result) + : JackResult(result), fDone(false) {} int Read(JackChannelTransaction* trans) { + if (trans == NULL) + { + return 0; + } + CheckRes(JackResult::Read(trans)); while (true) { JackSessionCommand buffer; @@ -1170,11 +1177,20 @@ struct JackSessionNotifyResult : public JackResult fCommandList.push_back(buffer); } + + fDone = true; + return 0; } int Write(JackChannelTransaction* trans) { + if (trans == NULL) + { + fDone = true; + return 0; + } + char terminator[JACK_UUID_SIZE]; terminator[0] = '\0'; @@ -1191,6 +1207,12 @@ struct JackSessionNotifyResult : public JackResult jack_session_command_t* GetCommands() { + /* TODO: some kind of signal should be used instead */ + while (!fDone) + { + JackSleep(50000); /* 50 ms */ + } + jack_session_command_t* session_command = (jack_session_command_t *)malloc(sizeof(jack_session_command_t) * (fCommandList.size() + 1)); int i = 0; diff --git a/posix/JackSocketServerChannel.cpp b/posix/JackSocketServerChannel.cpp index 73a16b16..50480ee7 100644 --- a/posix/JackSocketServerChannel.cpp +++ b/posix/JackSocketServerChannel.cpp @@ -430,7 +430,7 @@ bool JackSocketServerChannel::HandleRequest(int fd) jack_log("JackRequest::SessionNotify"); JackSessionNotifyRequest req; if (req.Read(socket) == 0) { - fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, socket); + fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, socket, NULL); } break; } diff --git a/windows/JackWinNamedPipeServerChannel.cpp b/windows/JackWinNamedPipeServerChannel.cpp index b557a33c..194e68ac 100644 --- a/windows/JackWinNamedPipeServerChannel.cpp +++ b/windows/JackWinNamedPipeServerChannel.cpp @@ -343,7 +343,7 @@ bool JackClientPipeThread::HandleRequest() jack_log("JackRequest::SessionNotify"); JackSessionNotifyRequest req; if (req.Read(fPipe) == 0) { - fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, fPipe); + fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, fPipe, NULL); } res.Write(fPipe); break; From e93e526957d2d6f9a63c1af586098fc417971192 Mon Sep 17 00:00:00 2001 From: Nedko Arnaudov Date: Sun, 7 Aug 2011 05:32:19 +0300 Subject: [PATCH 6/8] dbus: info log messages for session notify --- dbus/controller_iface_session_manager.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dbus/controller_iface_session_manager.c b/dbus/controller_iface_session_manager.c index 7292cd6c..128f702c 100644 --- a/dbus/controller_iface_session_manager.c +++ b/dbus/controller_iface_session_manager.c @@ -60,6 +60,8 @@ jack_controller_dbus_session_notify( goto exit; } + jack_info("Session notify initiated. target='%s', type=%"PRIu32", path='%s'", target, u32, path); + if (*target == 0) { target = NULL; @@ -82,6 +84,8 @@ jack_controller_dbus_session_notify( goto exit; } + jack_info("Session notify complete, commands follow:"); + call->reply = dbus_message_new_method_return(call->message); if (call->reply == NULL) { @@ -123,12 +127,16 @@ jack_controller_dbus_session_notify( goto close_struct; } + jack_info("uuid='%s', client='%s', command='%s', flags=0x%"PRIX32, cmd_ptr->uuid, cmd_ptr->client_name, cmd_ptr->command, u32); + if (!dbus_message_iter_close_container(&array_iter, &struct_iter)) { goto close_array; } } + jack_info("End of session commands."); + if (!dbus_message_iter_close_container(&top_iter, &array_iter)) { goto unref; From b35647d838134ebc8f218340fae9ad46dee9de0c Mon Sep 17 00:00:00 2001 From: Nedko Arnaudov Date: Thu, 11 Aug 2011 00:31:40 +0300 Subject: [PATCH 7/8] Fix JackEngine::ClientHasSessionCallback() duplicate variable scope bug --- common/JackEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index a1b0a9d2..69b28a6a 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -1076,7 +1076,7 @@ void JackEngine::ClientHasSessionCallback(const char *name, int *result) { JackClientInterface* client = NULL; for (int i = 0; i < CLIENT_NUM; i++) { - JackClientInterface* client = fClientTable[i]; + client = fClientTable[i]; if (client && (strcmp(client->GetClientControl()->fName, name) == 0)) break; } From 0f439c74ddde1a635c4058a6f4f02cd45f9f5288 Mon Sep 17 00:00:00 2001 From: Nedko Arnaudov Date: Thu, 11 Aug 2011 08:33:34 +0300 Subject: [PATCH 8/8] jackdbus: implement session command queue --- dbus/controller.c | 33 ++- dbus/controller_iface_session_manager.c | 341 +++++++++++++++++++++--- dbus/controller_internal.h | 14 + dbus/jackdbus.c | 6 + 4 files changed, 347 insertions(+), 47 deletions(-) diff --git a/dbus/controller.c b/dbus/controller.c index 42981c1c..14d28e5c 100644 --- a/dbus/controller.c +++ b/dbus/controller.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "controller.h" #include "controller_internal.h" @@ -287,6 +288,15 @@ jack_controller_stop_server( { int ret; + pthread_mutex_lock(&controller_ptr->lock); + if (!list_empty(&controller_ptr->session_pending_commands)) + { + pthread_mutex_unlock(&controller_ptr->lock); + jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Refusing to stop JACK server because of pending session commands"); + return false; + } + pthread_mutex_unlock(&controller_ptr->lock); + jack_info("Stopping jack server..."); assert(controller_ptr->started); /* should be ensured by caller */ @@ -507,6 +517,7 @@ void * jack_controller_create( DBusConnection *connection) { + int error; struct jack_controller *controller_ptr; const char * address[PARAM_ADDRESS_SIZE]; DBusObjectPathVTable vtable = @@ -523,11 +534,20 @@ jack_controller_create( goto fail; } + error = pthread_mutex_init(&controller_ptr->lock, NULL); + if (error != 0) + { + jack_error("Failed to initialize mutex. error %d", error); + goto fail_free; + } + + INIT_LIST_HEAD(&controller_ptr->session_pending_commands); + controller_ptr->server = jackctl_server_create(on_device_acquire, on_device_release); if (controller_ptr->server == NULL) { jack_error("Failed to create server object"); - goto fail_free; + goto fail_uninit_mutex; } controller_ptr->params = jack_params_create(controller_ptr->server); @@ -585,6 +605,9 @@ fail_destroy_params: fail_destroy_server: jackctl_server_destroy(controller_ptr->server); +fail_uninit_mutex: + pthread_mutex_destroy(&controller_ptr->lock); + fail_free: free(controller_ptr); @@ -739,13 +762,17 @@ jack_controller_destroy( { if (controller_ptr->started) { - jack_controller_stop_server(controller_ptr, NULL); + while (!jack_controller_stop_server(controller_ptr, NULL)) + { + jack_info("jack server failed to stop, retrying in 3 seconds..."); + usleep(3000000); + } } jack_controller_remove_slave_drivers(controller_ptr); jack_params_destroy(controller_ptr->params); jackctl_server_destroy(controller_ptr->server); - + pthread_mutex_destroy(&controller_ptr->lock); free(controller_ptr); } diff --git a/dbus/controller_iface_session_manager.c b/dbus/controller_iface_session_manager.c index 128f702c..82839e4e 100644 --- a/dbus/controller_iface_session_manager.c +++ b/dbus/controller_iface_session_manager.c @@ -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 diff --git a/dbus/controller_internal.h b/dbus/controller_internal.h index 1d4d40d3..9dbce2e3 100644 --- a/dbus/controller_internal.h +++ b/dbus/controller_internal.h @@ -25,6 +25,7 @@ #include "jslist.h" #include "jack/control.h" #include "jack/jack.h" +#include "jack/session.h" #include "jackdbus.h" #include "list.h" #include "params.h" @@ -37,6 +38,16 @@ struct jack_controller_slave_driver bool loaded; }; +struct jack_session_pending_command +{ + struct list_head siblings; + DBusConnection * connection; + DBusMessage * message; + jack_session_event_type_t type; + const char * target; + const char * path; +}; + struct jack_controller { jackctl_server_t *server; @@ -54,6 +65,9 @@ struct jack_controller union jackctl_parameter_value slave_drivers_vparam_value; struct jack_dbus_object_descriptor dbus_descriptor; + + pthread_mutex_t lock; + struct list_head session_pending_commands; }; #define DEFAULT_DRIVER "dummy" diff --git a/dbus/jackdbus.c b/dbus/jackdbus.c index cb9fd14d..685bba3d 100644 --- a/dbus/jackdbus.c +++ b/dbus/jackdbus.c @@ -105,6 +105,12 @@ void jack_dbus_send_method_return( struct jack_dbus_method_call * call) { + if (call->message == NULL) + { + /* async call */ + return; + } + if (call->reply) { retry_send: