From fed6f1e66dfb4b78f8b55467d5130e587e07da87 Mon Sep 17 00:00:00 2001 From: Rui Nuno Capela Date: Wed, 10 Oct 2018 15:06:14 +0100 Subject: [PATCH] [metadata] Metadata properties implementation. (#383) * [metadata] Metadata properties implementation. * [metadata] Fixed for shared server metadata-base accessor; alsofixed coding/naming style. * [metadata] Fixed a tab for space. * [metadata] Use of Berkeley DB is now truly optional on configure time. * [metadata] Fixed tabs for spaces, again. * [metadata] Fixed for shared metadata-base initialization and external clients. * [metadata] Blind-fix for windows codebase. * [metadata] Metadata API moved into client-side library only. * [metadata] Fixed jack_port_uuid() stubbiness, now returning a proper UUID from port index. * [metadata] Uniform method names. * [metadata] Fixed PropertyChangeNotify through server async call. --- common/JackAPI.cpp | 121 ++--- common/JackChannel.h | 4 + common/JackClient.cpp | 37 ++ common/JackClient.h | 8 + common/JackEngine.cpp | 27 +- common/JackEngine.h | 2 + common/JackGenericClientChannel.cpp | 7 + common/JackGenericClientChannel.h | 2 + common/JackLibAPI.cpp | 126 +++++ common/JackLibClient.cpp | 10 + common/JackLibClient.h | 2 + common/JackLibGlobals.h | 5 + common/JackLockedEngine.h | 9 + common/JackMetadata.cpp | 730 ++++++++++++++++++++++++++++ common/JackMetadata.h | 61 ++- common/JackNotification.h | 1 + common/JackRequest.h | 44 +- common/JackRequestDecoder.cpp | 8 + common/wscript | 3 +- wscript | 5 + 20 files changed, 1131 insertions(+), 81 deletions(-) create mode 100644 common/JackMetadata.cpp diff --git a/common/JackAPI.cpp b/common/JackAPI.cpp index c2ea6400..b4933508 100644 --- a/common/JackAPI.cpp +++ b/common/JackAPI.cpp @@ -26,7 +26,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "JackGlobals.h" #include "JackTime.h" #include "JackPortType.h" -#include "JackMetadata.h" #include using namespace Jack; @@ -39,13 +38,6 @@ extern "C" typedef void (*print_function)(const char*); typedef void *(*thread_routine)(void*); - LIB_EXPORT const char* JACK_METADATA_PRETTY_NAME = "http://jackaudio.org/metadata/pretty-name"; - LIB_EXPORT const char* JACK_METADATA_HARDWARE = "http://jackaudio.org/metadata/hardware"; - LIB_EXPORT const char* JACK_METADATA_CONNECTED = "http://jackaudio.org/metadata/connected"; - LIB_EXPORT const char* JACK_METADATA_PORT_GROUP = "http://jackaudio.org/metadata/port-group"; - LIB_EXPORT const char* JACK_METADATA_ICON_SMALL = "http://jackaudio.org/metadata/icon-small"; - LIB_EXPORT const char* JACK_METADATA_ICON_LARGE = "http://jackaudio.org/metadata/icon-large"; - LIB_EXPORT void jack_get_version( @@ -273,16 +265,6 @@ extern "C" LIB_EXPORT void jack_session_commands_free(jack_session_command_t *cmds); LIB_EXPORT int jack_client_has_session_callback(jack_client_t *client, const char* client_name); - LIB_EXPORT int jack_set_property(jack_client_t*, jack_uuid_t subject, const char* key, const char* value, const char* type); - LIB_EXPORT int jack_get_property(jack_uuid_t subject, const char* key, char** value, char** type); - LIB_EXPORT void jack_free_description(jack_description_t* desc, int free_description_itself); - LIB_EXPORT int jack_get_properties(jack_uuid_t subject, jack_description_t* desc); - LIB_EXPORT int jack_get_all_properties(jack_description_t** descs); - LIB_EXPORT int jack_remove_property(jack_client_t* client, jack_uuid_t subject, const char* key); - LIB_EXPORT int jack_remove_properties(jack_client_t* client, jack_uuid_t subject); - LIB_EXPORT int jack_remove_all_properties(jack_client_t* client); - LIB_EXPORT int jack_set_property_change_callback(jack_client_t* client, JackPropertyChangeCallback callback, void* arg); - LIB_EXPORT jack_uuid_t jack_client_uuid_generate(); LIB_EXPORT jack_uuid_t jack_port_uuid_generate(uint32_t port_id); LIB_EXPORT uint32_t jack_uuid_to_index(jack_uuid_t); @@ -375,9 +357,18 @@ LIB_EXPORT void* jack_port_get_buffer(jack_port_t* port, jack_nframes_t frames) } } -LIB_EXPORT jack_uuid_t jack_port_uuid(const jack_port_t*) +LIB_EXPORT jack_uuid_t jack_port_uuid(const jack_port_t* port) { - return 0; + JackGlobals::CheckContext("jack_port_uuid"); + + uintptr_t port_aux = (uintptr_t)port; + jack_port_id_t myport = (jack_port_id_t)port_aux; + if (!CheckPort(myport)) { + jack_error("jack_port_uuid called with an incorrect port %ld", myport); + return 0; + } else { + return jack_port_uuid_generate(myport); + } } LIB_EXPORT const char* jack_port_name(const jack_port_t* port) @@ -2080,88 +2071,70 @@ LIB_EXPORT int jack_client_has_session_callback(jack_client_t* ext_client, const } } -LIB_EXPORT int jack_set_property(jack_client_t*, jack_uuid_t, const char*, const char*, const char*) -{ - return -1; -} - -LIB_EXPORT int jack_get_property(jack_uuid_t, const char*, char**, char**) -{ - return -1; -} - -LIB_EXPORT void jack_free_description(jack_description_t*, int) -{ -} - -LIB_EXPORT int jack_get_properties(jack_uuid_t, jack_description_t*) -{ - return -1; -} - -LIB_EXPORT int jack_get_all_properties(jack_description_t**) +LIB_EXPORT jack_uuid_t jack_client_uuid_generate() { - return -1; + static uint32_t uuid_cnt = 0; + jack_uuid_t uuid = 0x2; /* JackUUIDClient */; + uuid = (uuid << 32) | ++uuid_cnt; + return uuid; } -LIB_EXPORT int jack_remove_property(jack_client_t*, jack_uuid_t, const char*) +LIB_EXPORT jack_uuid_t jack_port_uuid_generate(uint32_t port_id) { - return -1; + jack_uuid_t uuid = 0x1; /* JackUUIDPort */ + uuid = (uuid << 32) | (port_id + 1); + return uuid; } -LIB_EXPORT int jack_remove_properties(jack_client_t*, jack_uuid_t) +LIB_EXPORT uint32_t jack_uuid_to_index(jack_uuid_t u) { - return -1; + return (u & 0xffff) - 1; } -LIB_EXPORT int jack_remove_all_properties(jack_client_t*) +LIB_EXPORT int jack_uuid_compare(jack_uuid_t a, jack_uuid_t b) { - return -1; -} + if (a == b) { + return 0; + } -LIB_EXPORT int jack_set_property_change_callback(jack_client_t*, JackPropertyChangeCallback, void*) -{ - return -1; -} + if (a < b) { + return -1; + } -LIB_EXPORT jack_uuid_t jack_client_uuid_generate() -{ - return 0; + return 1; } -LIB_EXPORT jack_uuid_t jack_port_uuid_generate(uint32_t) +LIB_EXPORT void jack_uuid_copy(jack_uuid_t* dst, jack_uuid_t src) { - return 0; + *dst = src; } -LIB_EXPORT uint32_t jack_uuid_to_index(jack_uuid_t) +LIB_EXPORT void jack_uuid_clear(jack_uuid_t* u) { - return 0; + *u = 0; } -LIB_EXPORT int jack_uuid_compare(jack_uuid_t, jack_uuid_t) +LIB_EXPORT int jack_uuid_parse(const char* b, jack_uuid_t* u) { - return 0; -} + if (sscanf (b, "%" PRIu64, u) == 1) { -LIB_EXPORT void jack_uuid_copy(jack_uuid_t*, jack_uuid_t) -{ -} + if (*u < (0x1LL << 32)) { + /* has not type bits set - not legal */ + return -1; + } -LIB_EXPORT void jack_uuid_clear(jack_uuid_t*) -{ -} + return 0; + } -LIB_EXPORT int jack_uuid_parse(const char*, jack_uuid_t*) -{ - return 0; + return -1; } -LIB_EXPORT void jack_uuid_unparse(jack_uuid_t, char buf[JACK_UUID_STRING_SIZE]) +LIB_EXPORT void jack_uuid_unparse(jack_uuid_t u, char b[JACK_UUID_STRING_SIZE]) { + snprintf (b, JACK_UUID_STRING_SIZE, "%" PRIu64, u); } -LIB_EXPORT int jack_uuid_empty(jack_uuid_t) +LIB_EXPORT int jack_uuid_empty(jack_uuid_t u) { - return 0; + return u == 0; } diff --git a/common/JackChannel.h b/common/JackChannel.h index 40f23b4f..6b0bca79 100644 --- a/common/JackChannel.h +++ b/common/JackChannel.h @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "types.h" #include "JackSession.h" +#include "JackMetadata.h" namespace Jack { @@ -186,6 +187,9 @@ class JackClientChannelInterface virtual void ClientHasSessionCallback(const char* client_name, int* result) {} + virtual void PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change, int* result) + {} + virtual bool IsChannelThread() { return false; diff --git a/common/JackClient.cpp b/common/JackClient.cpp index d1ef7d65..5befdfbd 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -59,6 +59,7 @@ JackClient::JackClient(JackSynchro* table):fThread(this) fThreadFun = NULL; fSession = NULL; fLatency = NULL; + fPropertyChange = NULL; fProcessArg = NULL; fGraphOrderArg = NULL; @@ -77,6 +78,7 @@ JackClient::JackClient(JackSynchro* table):fThread(this) fThreadFunArg = NULL; fSessionArg = NULL; fLatencyArg = NULL; + fPropertyChangeArg = NULL; fSessionReply = kPendingSessionReply; } @@ -319,6 +321,17 @@ int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, case kLatencyCallback: res = HandleLatencyCallback(value1); break; + + case kPropertyChangeCallback: { + jack_uuid_t subject; + jack_uuid_parse(name, &subject); + const char* key = message; + jack_property_change_t change = (jack_property_change_t)value1; + jack_log("JackClient::kPropertyChangeCallback subject = %x key = %s change = %x", subject, key, change); + if (fPropertyChange) + fPropertyChange(subject, key, change, fPropertyChangeArg); + break; + } } } @@ -1186,6 +1199,18 @@ int JackClient::SetLatencyCallback(JackLatencyCallback callback, void *arg) } } +int JackClient::SetPropertyChangeCallback(JackPropertyChangeCallback callback, void *arg) +{ + if (IsActive()) { + jack_error("You cannot set callbacks on an active client"); + return -1; + } else { + fPropertyChangeArg = arg; + fPropertyChange = callback; + return 0; + } +} + //------------------ // Internal clients //------------------ @@ -1309,5 +1334,17 @@ int JackClient::ClientHasSessionCallback(const char* client_name) return result; } +//------------------ +// Metadata API +//------------------ + +int JackClient::PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change) +{ + int result = -1; + fChannel->PropertyChangeNotify(subject, key, change, &result); + return result; +} + + } // end of namespace diff --git a/common/JackClient.h b/common/JackClient.h index b6e78ef3..67cd611d 100644 --- a/common/JackClient.h +++ b/common/JackClient.h @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "JackPlatformPlug.h" #include "JackChannel.h" #include "JackRequest.h" +#include "JackMetadata.h" #include "varargs.h" #include @@ -68,6 +69,7 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable JackThreadCallback fThreadFun; JackSessionCallback fSession; JackLatencyCallback fLatency; + JackPropertyChangeCallback fPropertyChange; void* fProcessArg; void* fGraphOrderArg; @@ -87,6 +89,8 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable void* fThreadFunArg; void* fSessionArg; void* fLatencyArg; + void* fPropertyChangeArg; + char fServerName[JACK_SERVER_NAME_SIZE+1]; JackThread fThread; /*! Thread to execute the Process function */ @@ -185,6 +189,7 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable virtual int SetPortRenameCallback(JackPortRenameCallback callback, void *arg); virtual int SetSessionCallback(JackSessionCallback callback, void *arg); virtual int SetLatencyCallback(JackLatencyCallback callback, void *arg); + virtual int SetPropertyChangeCallback(JackPropertyChangeCallback callback, void* arg); // Internal clients virtual char* GetInternalClientName(int ref); @@ -205,6 +210,9 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable virtual int ReserveClientName(const char* client_name, const char* uuid); virtual int ClientHasSessionCallback(const char* client_name); + // Metadata API + virtual int PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change); + // JackRunnableInterface interface bool Init(); bool Execute(); diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp index 15076599..37d1022d 100644 --- a/common/JackEngine.cpp +++ b/common/JackEngine.cpp @@ -102,6 +102,7 @@ void JackEngine::NotifyQuit() fChannel.NotifyQuit(); } + //----------------------------- // Client ressource management //----------------------------- @@ -239,15 +240,15 @@ int JackEngine::ComputeTotalLatencies() fGraphManager->TopologicalSort(sorted); /* iterate over all clients in graph order, and emit - * capture latency callback. - */ + * capture latency callback. + */ for (it = sorted.begin(); it != sorted.end(); it++) { NotifyClient(*it, kLatencyCallback, true, "", 0, 0); } /* now issue playback latency callbacks in reverse graph order. - */ + */ for (rit = sorted.rbegin(); rit != sorted.rend(); rit++) { NotifyClient(*rit, kLatencyCallback, true, "", 1, 0); } @@ -255,6 +256,26 @@ int JackEngine::ComputeTotalLatencies() return 0; } +//-------------- +// Metadata API +//-------------- + +int JackEngine::PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change) +{ + jack_log("JackEngine::PropertyChangeNotify: subject = %x key = %s change = %x", subject, key, change); + + for (int i = 0; i < CLIENT_NUM; i++) { + JackClientInterface* client = fClientTable[i]; + if (client) { + char buf[JACK_UUID_STRING_SIZE]; + jack_uuid_unparse(subject, buf); + client->ClientNotify(i, buf, kPropertyChangeCallback, false, key, change, 0); + } + } + + return 0; +} + //--------------- // Notifications //--------------- diff --git a/common/JackEngine.h b/common/JackEngine.h index e3a471b5..5ef115bd 100644 --- a/common/JackEngine.h +++ b/common/JackEngine.h @@ -141,6 +141,8 @@ class SERVER_EXPORT JackEngine : public JackLockAble int ComputeTotalLatencies(); + int PropertyChangeNotify(jack_uuid_t subject, const char* key,jack_property_change_t change); + // Graph bool Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end); diff --git a/common/JackGenericClientChannel.cpp b/common/JackGenericClientChannel.cpp index 46e71619..047d7bfa 100644 --- a/common/JackGenericClientChannel.cpp +++ b/common/JackGenericClientChannel.cpp @@ -302,6 +302,13 @@ void JackGenericClientChannel::InternalClientUnload(int refnum, int int_ref, int *status = res.fStatus; } +void JackGenericClientChannel::PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change, int* result) +{ + JackPropertyChangeNotifyRequest req(subject, key, change); + JackResult res; + ServerAsyncCall(&req, &res, result); +} + } // end of namespace diff --git a/common/JackGenericClientChannel.h b/common/JackGenericClientChannel.h index fb125915..04195a2a 100644 --- a/common/JackGenericClientChannel.h +++ b/common/JackGenericClientChannel.h @@ -93,6 +93,8 @@ class JackGenericClientChannel : public detail::JackClientChannelInterface void GetClientNameForUUID(int refnum, const char* uuid, char* name_res, int* result); void ReserveClientName(int refnum, const char* client_name, const char *uuid, int* result); void ClientHasSessionCallback(const char* client_name, int* result); + + void PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change, int* result); }; } // end of namespace diff --git a/common/JackLibAPI.cpp b/common/JackLibAPI.cpp index 690f62bf..27dc5efe 100644 --- a/common/JackLibAPI.cpp +++ b/common/JackLibAPI.cpp @@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "JackTools.h" #include "JackSystemDeps.h" #include "JackServerLaunch.h" +#include "JackMetadata.h" #include using namespace Jack; @@ -46,6 +47,23 @@ extern "C" LIB_EXPORT int jack_client_close (jack_client_t *client); LIB_EXPORT int jack_get_client_pid (const char *name); + // Metadata API + LIB_EXPORT const char* JACK_METADATA_PRETTY_NAME = "http://jackaudio.org/metadata/pretty-name"; + LIB_EXPORT const char* JACK_METADATA_HARDWARE = "http://jackaudio.org/metadata/hardware"; + LIB_EXPORT const char* JACK_METADATA_CONNECTED = "http://jackaudio.org/metadata/connected"; + LIB_EXPORT const char* JACK_METADATA_PORT_GROUP = "http://jackaudio.org/metadata/port-group"; + LIB_EXPORT const char* JACK_METADATA_ICON_SMALL = "http://jackaudio.org/metadata/icon-small"; + LIB_EXPORT const char* JACK_METADATA_ICON_LARGE = "http://jackaudio.org/metadata/icon-large"; + + LIB_EXPORT int jack_set_property(jack_client_t*, jack_uuid_t subject, const char* key, const char* value, const char* type); + LIB_EXPORT int jack_get_property(jack_uuid_t subject, const char* key, char** value, char** type); + LIB_EXPORT void jack_free_description(jack_description_t* desc, int free_description_itself); + LIB_EXPORT int jack_get_properties(jack_uuid_t subject, jack_description_t* desc); + LIB_EXPORT int jack_get_all_properties(jack_description_t** descs); + LIB_EXPORT int jack_remove_property(jack_client_t* client, jack_uuid_t subject, const char* key); + LIB_EXPORT int jack_remove_properties(jack_client_t* client, jack_uuid_t subject); + LIB_EXPORT int jack_remove_all_properties(jack_client_t* client); + LIB_EXPORT int jack_set_property_change_callback(jack_client_t* client, JackPropertyChangeCallback callback, void* arg); #ifdef __cplusplus } @@ -213,3 +231,111 @@ LIB_EXPORT int jack_get_client_pid(const char *name) return 0; } +// Metadata API + +LIB_EXPORT int jack_set_property(jack_client_t* ext_client, jack_uuid_t subject, const char* key, const char* value, const char* type) +{ + JackGlobals::CheckContext("jack_set_property"); + + JackClient* client = (JackClient*)ext_client; + jack_log("jack_set_property ext_client %x client %x ", ext_client, client); + if (client == NULL) { + jack_error("jack_set_property called with a NULL client"); + return -1; + } else { + JackMetadata* metadata = GetMetadata(); + return (metadata ? metadata->SetProperty(client, subject, key, value, type) : -1); + } +} + +LIB_EXPORT int jack_get_property(jack_uuid_t subject, const char* key, char** value, char** type) +{ + JackGlobals::CheckContext("jack_get_property"); + + JackMetadata* metadata = GetMetadata(); + return (metadata ? metadata->GetProperty(subject, key, value, type) : -1); +} + +LIB_EXPORT void jack_free_description(jack_description_t* desc, int free_actual_description_too) +{ + JackGlobals::CheckContext("jack_free_description"); + + JackMetadata* metadata = GetMetadata(); + if (metadata) + metadata->FreeDescription(desc, free_actual_description_too); +} + +LIB_EXPORT int jack_get_properties(jack_uuid_t subject, jack_description_t* desc) +{ + JackGlobals::CheckContext("jack_get_properties"); + + JackMetadata* metadata = GetMetadata(); + return (metadata ? metadata->GetProperties(subject, desc) : -1); +} + +LIB_EXPORT int jack_get_all_properties(jack_description_t** descriptions) +{ + JackGlobals::CheckContext("jack_get_all_properties"); + + JackMetadata* metadata = GetMetadata(); + return (metadata ? metadata->GetAllProperties(descriptions) : -1); +} + +LIB_EXPORT int jack_remove_property(jack_client_t* ext_client, jack_uuid_t subject, const char* key) +{ + JackGlobals::CheckContext("jack_remove_property"); + + JackClient* client = (JackClient*)ext_client; + jack_log("jack_remove_property ext_client %x client %x ", ext_client, client); + if (client == NULL) { + jack_error("jack_remove_property called with a NULL client"); + return -1; + } else { + JackMetadata* metadata = GetMetadata(); + return (metadata ? metadata->RemoveProperty(client, subject, key) : -1); + } +} + +LIB_EXPORT int jack_remove_properties(jack_client_t* ext_client, jack_uuid_t subject) +{ + JackGlobals::CheckContext("jack_remove_properties"); + + JackClient* client = (JackClient*)ext_client; + jack_log("jack_remove_properties ext_client %x client %x ", ext_client, client); + if (client == NULL) { + jack_error("jack_remove_properties called with a NULL client"); + return -1; + } else { + JackMetadata* metadata = GetMetadata(); + return (metadata ? metadata->RemoveProperties(client, subject) : -1); + } +} + +LIB_EXPORT int jack_remove_all_properties(jack_client_t* ext_client) +{ + JackGlobals::CheckContext("jack_remove_all_properties"); + + JackClient* client = (JackClient*)ext_client; + jack_log("jack_remove_all_properties ext_client %x client %x ", ext_client, client); + if (client == NULL) { + jack_error("jack_remove_all_properties called with a NULL client"); + return -1; + } else { + JackMetadata* metadata = GetMetadata(); + return (metadata ? metadata->RemoveAllProperties(client) : -1); + } +} + +LIB_EXPORT int jack_set_property_change_callback(jack_client_t* ext_client, JackPropertyChangeCallback callback, void* arg) +{ + JackGlobals::CheckContext("jack_set_property_change_callback"); + + JackClient* client = (JackClient*)ext_client; + jack_log("jack_set_property_change_callback ext_client %x client %x ", ext_client, client); + if (client == NULL) { + jack_error("jack_set_property_change_callback called with a NULL client"); + return -1; + } else { + return client->SetPropertyChangeCallback(callback, arg); + } +} diff --git a/common/JackLibClient.cpp b/common/JackLibClient.cpp index 9eb7e350..515a5f02 100644 --- a/common/JackLibClient.cpp +++ b/common/JackLibClient.cpp @@ -51,6 +51,16 @@ JackSynchro* GetSynchroTable() return (JackLibGlobals::fGlobals ? JackLibGlobals::fGlobals->fSynchroTable : 0); } +// Used for client-side Metadata API (JackLibAPI.cpp) +JackMetadata* GetMetadata() +{ + if (JackLibGlobals::fGlobals) { + return JackLibGlobals::fGlobals->fMetadata; + } else { + return NULL; + } +} + //------------------- // Client management //------------------- diff --git a/common/JackLibClient.h b/common/JackLibClient.h index 00f7bf92..6077ebe8 100644 --- a/common/JackLibClient.h +++ b/common/JackLibClient.h @@ -54,6 +54,8 @@ class JackLibClient : public JackClient JackClientControl* GetClientControl() const; }; +// Used for client-side Metadata API (JackLibAPI.cpp) +JackMetadata* GetMetadata(); } // end of namespace diff --git a/common/JackLibGlobals.h b/common/JackLibGlobals.h index cc7ee040..16d93fde 100644 --- a/common/JackLibGlobals.h +++ b/common/JackLibGlobals.h @@ -55,6 +55,7 @@ struct JackLibGlobals JackShmReadWritePtr fGraphManager; /*! Shared memory Port manager */ JackShmReadWritePtr fEngineControl; /*! Shared engine control */ // transport engine has to be writable JackSynchro fSynchroTable[CLIENT_NUM]; /*! Shared synchro table */ + JackMetadata *fMetadata; /*! Shared metadata base */ sigset_t fProcessSignals; static int fClientCount; @@ -69,6 +70,8 @@ struct JackLibGlobals fGraphManager = -1; fEngineControl = -1; + fMetadata = new JackMetadata(NULL); + // Filter SIGPIPE to avoid having client get a SIGPIPE when trying to access a died server. #ifdef WIN32 // TODO @@ -88,6 +91,8 @@ struct JackLibGlobals } JackMessageBuffer::Destroy(); + delete fMetadata; + // Restore old signal mask #ifdef WIN32 // TODO diff --git a/common/JackLockedEngine.h b/common/JackLockedEngine.h index b7fc3890..7cc0f401 100644 --- a/common/JackLockedEngine.h +++ b/common/JackLockedEngine.h @@ -361,6 +361,7 @@ class SERVER_EXPORT JackLockedEngine return fEngine.GetUUIDForClientName(client_name, uuid_res); CATCH_EXCEPTION_RETURN } + int GetClientNameForUUID(const char *uuid, char *name_res) { TRY_CALL @@ -383,6 +384,14 @@ class SERVER_EXPORT JackLockedEngine return fEngine.ClientHasSessionCallback(name); CATCH_EXCEPTION_RETURN } + + int PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change) + { + TRY_CALL + JackLock lock(&fEngine); + return fEngine.PropertyChangeNotify(subject, key, change); + CATCH_EXCEPTION_RETURN + } }; } // end of namespace diff --git a/common/JackMetadata.cpp b/common/JackMetadata.cpp new file mode 100644 index 00000000..4d7df2f0 --- /dev/null +++ b/common/JackMetadata.cpp @@ -0,0 +1,730 @@ +/* + Copyright (C) 2011 David Robillard + Copyright (C) 2013 Paul Davis + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + 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 Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "JackMetadata.h" + +#include "JackClient.h" + +#include +#include + + +namespace Jack +{ + +JackMetadata::JackMetadata(const char* server_name) +#if HAVE_DB + : fDB(NULL), fDBenv(NULL) +#endif +{ + PropertyInit(server_name); +} + +JackMetadata::~JackMetadata() +{ +#if HAVE_DB + if (fDB) { + fDB->close (fDB, 0); + fDB = NULL; + } + if (fDBenv) { + fDBenv->close (fDBenv, 0); + fDBenv = 0; + } +#endif +} + +int JackMetadata::PropertyInit(const char* server_name) +{ +#if HAVE_DB + + int ret; + char dbpath[PATH_MAX + 1]; + + /* idempotent */ + + if (fDBenv) { + return 0; + } + + if ((ret = db_env_create (&fDBenv, 0)) != 0) { + jack_error ("cannot initialize DB environment: %s\n", db_strerror (ret)); + return -1; + } + + if ((ret = fDBenv->open (fDBenv, jack_server_dir /*FIXME:(server_name, server_dir)*/, DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD, 0)) != 0) { + jack_error ("cannot open DB environment: %s", db_strerror (ret)); + return -1; + } + + if ((ret = db_create (&fDB, fDBenv, 0)) != 0) { + jack_error ("Cannot initialize metadata DB (%s)", db_strerror (ret)); + return -1; + } + + snprintf (dbpath, sizeof(dbpath), "%s/%s", jack_server_dir /*FIXME:(server_name, server_dir)*/, "metadata.db"); + + if ((ret = fDB->open (fDB, NULL, dbpath, NULL, DB_HASH, DB_CREATE | DB_THREAD, 0666)) != 0) { + jack_error ("Cannot open metadata DB at %s: %s", dbpath, db_strerror (ret)); + fDB->close (fDB, 0); + fDB = NULL; + return -1; + } + + return 0; + +#else // !HAVE_DB + return -1; +#endif +} + +int JackMetadata::PropertyChangeNotify(JackClient* client, jack_uuid_t subject, const char* key, jack_property_change_t change) +{ + /* the engine passes in a NULL client when it removes metadata during port or client removal + */ + + if (client == NULL) { + return 0; + } + + return client->PropertyChangeNotify(subject, key, change); +} + +#if HAVE_DB +void JackMetadata::MakeKeyDbt(DBT* dbt, jack_uuid_t subject, const char* key) +{ + char ustr[JACK_UUID_STRING_SIZE]; + size_t len1, len2; + + memset (dbt, 0, sizeof(DBT)); + memset (ustr, 0, JACK_UUID_STRING_SIZE); + jack_uuid_unparse (subject, ustr); + len1 = JACK_UUID_STRING_SIZE; + len2 = strlen (key) + 1; + dbt->size = len1 + len2; + dbt->data = malloc (dbt->size); + memcpy (dbt->data, ustr, len1); // copy subject+null + memcpy ((char *)dbt->data + len1, key, len2); // copy key+null +} +#endif + +int JackMetadata::SetProperty(JackClient* client, jack_uuid_t subject, const char* key, const char* value, const char* type) +{ +#if HAVE_DB + + DBT d_key; + DBT data; + int ret; + size_t len1, len2; + jack_property_change_t change; + + if (!key || key[0] == '\0') { + jack_error ("empty key string for metadata not allowed"); + return -1; + } + + if (!value || value[0] == '\0') { + jack_error ("empty value string for metadata not allowed"); + return -1; + } + + if (PropertyInit(NULL)) { + return -1; + } + + /* build a key */ + + MakeKeyDbt(&d_key, subject, key); + + /* build data */ + + memset (&data, 0, sizeof(data)); + + len1 = strlen (value) + 1; + if (type && type[0] != '\0') { + len2 = strlen (type) + 1; + } else { + len2 = 0; + } + + data.size = len1 + len2; + data.data = malloc (data.size); + memcpy (data.data, value, len1); + + if (len2) { + memcpy ((char *)data.data + len1, type, len2); + } + + if (fDB->exists (fDB, NULL, &d_key, 0) == DB_NOTFOUND) { + change = PropertyCreated; + } else { + change = PropertyChanged; + } + + if ((ret = fDB->put (fDB, NULL, &d_key, &data, 0)) != 0) { + char ustr[JACK_UUID_STRING_SIZE]; + jack_uuid_unparse (subject, ustr); + jack_error ("Cannot store metadata for %s/%s (%s)", ustr, key, db_strerror (ret)); + if (d_key.size > 0) { + free (d_key.data); + } + if (data.size > 0) { + free (data.data); + } + return -1; + } + + PropertyChangeNotify(client, subject, key, change); + + if (d_key.size > 0) { + free (d_key.data); + } + if (data.size > 0) { + free (data.data); + } + + return 0; + +#else // !HAVE_DB + return -1; +#endif +} + +int JackMetadata::GetProperty(jack_uuid_t subject, const char* key, char** value, char** type) +{ +#if HAVE_DB + + DBT d_key; + DBT data; + int ret; + size_t len1, len2; + + if (key == NULL || key[0] == '\0') { + return -1; + } + + if (PropertyInit(NULL)) { + return -1; + } + + /* build a key */ + + MakeKeyDbt(&d_key, subject, key); + + /* setup data DBT */ + + memset (&data, 0, sizeof(data)); + data.flags = DB_DBT_MALLOC; + + if ((ret = fDB->get (fDB, NULL, &d_key, &data, 0)) != 0) { + if (ret != DB_NOTFOUND) { + char ustr[JACK_UUID_STRING_SIZE]; + jack_uuid_unparse (subject, ustr); + jack_error ("Cannot retrieve metadata for %s/%s (%s)", ustr, key, db_strerror (ret)); + } + if (d_key.size > 0) { + free (d_key.data); + } + if (data.size > 0) { + free (data.data); + } + return -1; + } + + /* result must have at least 2 chars plus 2 nulls to be valid + */ + + if (data.size < 4) { + if (d_key.size > 0) { + free (d_key.data); + } + if (data.size > 0) { + free (data.data); + } + return -1; + } + + len1 = strlen ((const char*)data.data) + 1; + (*value) = (char*)malloc (len1); + memcpy (*value, data.data, len1); + + if (len1 < data.size) { + len2 = strlen ((const char*)data.data + len1) + 1; + + (*type) = (char*)malloc (len2); + memcpy (*type, (const char *)data.data + len1, len2); + } else { + /* no type specified, assume default */ + *type = NULL; + } + + if (d_key.size > 0) { + free (d_key.data); + } + if (data.size > 0) { + free (data.data); + } + + return 0; + +#else // !HAVE_DB + return -1; +#endif +} + +int JackMetadata::GetProperties(jack_uuid_t subject, jack_description_t* desc) +{ +#if HAVE_DB + + DBT key; + DBT data; + DBC* cursor; + int ret; + size_t len1, len2; + size_t cnt = 0; + char ustr[JACK_UUID_STRING_SIZE]; + size_t props_size = 0; + jack_property_t* prop; + + desc->properties = NULL; + desc->property_cnt = 0; + + memset (ustr, 0, JACK_UUID_STRING_SIZE); + jack_uuid_unparse (subject, ustr); + + if (PropertyInit(NULL)) { + return -1; + } + + + if ((ret = fDB->cursor (fDB, NULL, &cursor, 0)) != 0) { + jack_error ("Cannot create cursor for metadata search (%s)", db_strerror (ret)); + return -1; + } + + memset (&key, 0, sizeof(key)); + memset (&data, 0, sizeof(data)); + data.flags = DB_DBT_MALLOC; + + while ((ret = cursor->get (cursor, &key, &data, DB_NEXT)) == 0) { + + /* require 2 extra chars (data+null) for key, + which is composed of UUID str plus a key name + */ + + if (key.size < JACK_UUID_STRING_SIZE + 2) { + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + continue; + } + + if (memcmp (ustr, key.data, JACK_UUID_STRING_SIZE) != 0) { + /* not relevant */ + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + continue; + } + + /* result must have at least 2 chars plus 2 nulls to be valid + */ + + if (data.size < 4) { + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + continue; + } + + /* realloc array if necessary */ + + if (cnt == props_size) { + if (props_size == 0) { + props_size = 8; /* a rough guess at a likely upper bound for the number of properties */ + } else { + props_size *= 2; + } + + desc->properties = (jack_property_t*)realloc (desc->properties, sizeof(jack_property_t) * props_size); + } + + prop = &desc->properties[cnt]; + + /* store UUID/subject */ + + jack_uuid_copy (&desc->subject, subject); + + /* copy key (without leading UUID as subject */ + + len1 = key.size - JACK_UUID_STRING_SIZE; + prop->key = (char*)malloc (len1); + memcpy ((char*)prop->key, (const char *)key.data + JACK_UUID_STRING_SIZE, len1); + + /* copy data (which contains 1 or 2 null terminated strings, the value + and optionally a MIME type. + */ + + len1 = strlen ((const char*)data.data) + 1; + prop->data = (char*)malloc (len1); + memcpy ((char*)prop->data, data.data, len1); + + if (len1 < data.size) { + len2 = strlen ((const char *)data.data + len1) + 1; + + prop->type = (char*)malloc (len2); + memcpy ((char*)prop->type, (const char *)data.data + len1, len2); + } else { + /* no type specified, assume default */ + prop->type = NULL; + } + + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + + ++cnt; + } + + cursor->close (cursor); + desc->property_cnt = cnt; + + return cnt; + +#else // !HAVE_DB + return -1; +#endif +} + +int JackMetadata::GetAllProperties(jack_description_t** descriptions) +{ +#if HAVE_DB + + DBT key; + DBT data; + DBC* cursor; + int ret; + size_t dcnt = 0; + size_t dsize = 0; + size_t n = 0; + jack_description_t* desc = NULL; + jack_uuid_t uuid = JACK_UUID_EMPTY_INITIALIZER; + jack_description_t* current_desc = NULL; + jack_property_t* current_prop = NULL; + size_t len1, len2; + + if (PropertyInit(NULL)) { + return -1; + } + + if ((ret = fDB->cursor (fDB, NULL, &cursor, 0)) != 0) { + jack_error ("Cannot create cursor for metadata search (%s)", db_strerror (ret)); + return -1; + } + + memset (&key, 0, sizeof(key)); + memset (&data, 0, sizeof(data)); + data.flags = DB_DBT_MALLOC; + + dsize = 8; /* initial guess at number of descriptions we need */ + dcnt = 0; + desc = (jack_description_t*)malloc (dsize * sizeof(jack_description_t)); + + while ((ret = cursor->get (cursor, &key, &data, DB_NEXT)) == 0) { + + /* require 2 extra chars (data+null) for key, + which is composed of UUID str plus a key name + */ + + if (key.size < JACK_UUID_STRING_SIZE + 2) { + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + continue; + } + + if (jack_uuid_parse ((const char *)key.data, &uuid) != 0) { + continue; + } + + /* do we have an existing description for this UUID */ + + for (n = 0; n < dcnt; ++n) { + if (jack_uuid_compare (uuid, desc[n].subject) == 0) { + break; + } + } + + if (n == dcnt) { + /* we do not have an existing description, so grow the array */ + + if (dcnt == dsize) { + dsize *= 2; + desc = (jack_description_t*)realloc (desc, sizeof(jack_description_t) * dsize); + } + + /* initialize */ + + desc[n].property_size = 0; + desc[n].property_cnt = 0; + desc[n].properties = NULL; + + /* set up UUID */ + + jack_uuid_copy (&desc[n].subject, uuid); + dcnt++; + } + + current_desc = &desc[n]; + + /* see if there is room for the new property or if we need to realloc + */ + + if (current_desc->property_cnt == current_desc->property_size) { + if (current_desc->property_size == 0) { + current_desc->property_size = 8; + } else { + current_desc->property_size *= 2; + } + + current_desc->properties = (jack_property_t*)realloc (current_desc->properties, sizeof(jack_property_t) * current_desc->property_size); + } + + current_prop = ¤t_desc->properties[current_desc->property_cnt++]; + + /* copy key (without leading UUID) */ + + len1 = key.size - JACK_UUID_STRING_SIZE; + current_prop->key = (char*)malloc (len1); + memcpy ((char*)current_prop->key, (const char *)key.data + JACK_UUID_STRING_SIZE, len1); + + /* copy data (which contains 1 or 2 null terminated strings, the value + and optionally a MIME type. + */ + + len1 = strlen ((const char *)data.data) + 1; + current_prop->data = (char*)malloc (len1); + memcpy ((char*)current_prop->data, data.data, len1); + + if (len1 < data.size) { + len2 = strlen ((const char *)data.data + len1) + 1; + + current_prop->type = (char*)malloc (len2); + memcpy ((char*)current_prop->type, (const char *)data.data + len1, len2); + } else { + /* no type specified, assume default */ + current_prop->type = NULL; + } + + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + } + + cursor->close (cursor); + + (*descriptions) = desc; + + return dcnt; + +#else // !HAVE_DB + return -1; +#endif +} + +int JackMetadata::GetDescription(jack_uuid_t subject, jack_description_t* desc) +{ + return 0; +} + +int JackMetadata::GetAllDescriptions(jack_description_t** descs) +{ + return 0; +} + +void JackMetadata::FreeDescription(jack_description_t* desc, int free_actual_description_too) +{ + uint32_t n; + + for (n = 0; n < desc->property_cnt; ++n) { + free ((char*)desc->properties[n].key); + free ((char*)desc->properties[n].data); + if (desc->properties[n].type) { + free ((char*)desc->properties[n].type); + } + } + + free (desc->properties); + + if (free_actual_description_too) { + free (desc); + } +} + +int JackMetadata::RemoveProperty(JackClient* client, jack_uuid_t subject, const char* key) +{ +#if HAVE_DB + + DBT d_key; + int ret; + + if (PropertyInit(NULL)) { + return -1; + } + + MakeKeyDbt(&d_key, subject, key); + if ((ret = fDB->del (fDB, NULL, &d_key, 0)) != 0) { + jack_error ("Cannot delete key %s (%s)", key, db_strerror (ret)); + if (d_key.size > 0) { + free (d_key.data); + } + return -1; + } + + PropertyChangeNotify(client, subject, key, PropertyDeleted); + + if (d_key.size > 0) { + free (d_key.data); + } + + return 0; + +#else // !HAVE_DB + return -1; +#endif +} + +int JackMetadata::RemoveProperties(JackClient* client, jack_uuid_t subject) +{ +#if HAVE_DB + + DBT key; + DBT data; + DBC* cursor; + int ret; + char ustr[JACK_UUID_STRING_SIZE]; + int retval = 0; + uint32_t cnt = 0; + + memset (ustr, 0, JACK_UUID_STRING_SIZE); + jack_uuid_unparse (subject, ustr); + + if (PropertyInit(NULL)) { + return -1; + } + + if ((ret = fDB->cursor (fDB, NULL, &cursor, 0)) != 0) { + jack_error ("Cannot create cursor for metadata search (%s)", db_strerror (ret)); + return -1; + } + + memset (&key, 0, sizeof(key)); + memset (&data, 0, sizeof(data)); + data.flags = DB_DBT_MALLOC; + + while ((ret = cursor->get (cursor, &key, &data, DB_NEXT)) == 0) { + + /* require 2 extra chars (data+null) for key, + which is composed of UUID str plus a key name + */ + + if (key.size < JACK_UUID_STRING_SIZE + 2) { + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + continue; + } + + if (memcmp (ustr, key.data, JACK_UUID_STRING_SIZE) != 0) { + /* not relevant */ + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + continue; + } + + if ((ret = cursor->del (cursor, 0)) != 0) { + jack_error ("cannot delete property (%s)", db_strerror (ret)); + /* don't return -1 here since this would leave things + even more inconsistent. wait till the cursor is finished + */ + retval = -1; + } + cnt++; + + /* if (key.size > 0) free(key.data); */ + if (data.size > 0) { + free (data.data); + } + } + + cursor->close (cursor); + + if (cnt) { + PropertyChangeNotify(client, subject, NULL, PropertyDeleted); + } + + if (retval) { + return -1; + } + + return cnt; + +#else // !HAVE_DB + return -1; +#endif +} + +int JackMetadata::RemoveAllProperties(JackClient* client) +{ +#if HAVE_DB + + int ret; + jack_uuid_t empty_uuid = JACK_UUID_EMPTY_INITIALIZER; + + if (PropertyInit(NULL)) { + return -1; + } + + if ((ret = fDB->truncate (fDB, NULL, NULL, 0)) != 0) { + jack_error ("Cannot clear properties (%s)", db_strerror (ret)); + return -1; + } + + PropertyChangeNotify(client, empty_uuid, NULL, PropertyDeleted); + + return 0; + +#else // !HAVE_DB + return -1; +#endif +} + +} // end of namespace + + + diff --git a/common/JackMetadata.h b/common/JackMetadata.h index 9e1a11ee..6968183d 100644 --- a/common/JackMetadata.h +++ b/common/JackMetadata.h @@ -17,11 +17,17 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifndef __jack_metadata_int_h__ -#define __jack_metadata_int_h__ +#ifndef __JackMetadata__ +#define __JackMetadata__ #include +#if HAVE_DB +#include +#endif + +#include + #ifdef __cplusplus extern "C" { #endif @@ -53,4 +59,55 @@ typedef void (*JackPropertyChangeCallback)(jack_uuid_t subject, #ifdef __cplusplus } #endif + + +namespace Jack +{ + +class JackClient; + +/*! +\brief Metadata base. +*/ + +class JackMetadata +{ + private: + + #if HAVE_DB + DB* fDB; + DB_ENV* fDBenv; + #endif + + int PropertyInit(const char* server_name); + int PropertyChangeNotify(JackClient* client, jack_uuid_t subject, const char* key, jack_property_change_t change); + + #if HAVE_DB + void MakeKeyDbt(DBT* dbt, jack_uuid_t subject, const char* key); + #endif + + public: + + JackMetadata(const char* server_name = NULL); + ~JackMetadata(); + + int GetProperty(jack_uuid_t subject, const char* key, char** value, char** type); + int GetProperties(jack_uuid_t subject, jack_description_t* desc); + int GetAllProperties(jack_description_t** descriptions); + + int GetDescription(jack_uuid_t subject, jack_description_t* desc); + int GetAllDescriptions(jack_description_t** descs); + void FreeDescription(jack_description_t* desc, int free_actual_description_too); + + int SetProperty(JackClient* client, jack_uuid_t subject, const char* key, const char* value, const char* type); + + int RemoveProperty(JackClient* client, jack_uuid_t subject, const char* key); + int RemoveProperties(JackClient* client, jack_uuid_t subject); + int RemoveAllProperties(JackClient* client); + +}; + + +} // end of namespace + #endif diff --git a/common/JackNotification.h b/common/JackNotification.h index 74b9df4b..830c3ca4 100644 --- a/common/JackNotification.h +++ b/common/JackNotification.h @@ -47,6 +47,7 @@ enum NotificationType { kQUIT = 16, kSessionCallback = 17, kLatencyCallback = 18, + kPropertyChangeCallback = 19, kMaxNotification = 64 // To keep some room in JackClientControl fCallback table }; diff --git a/common/JackRequest.h b/common/JackRequest.h index 0262acbc..af843191 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -89,7 +89,8 @@ struct JackRequest kReserveClientName = 36, kGetUUIDByClient = 37, kClientHasSessionCallback = 38, - kComputeTotalLatencies = 39 + kComputeTotalLatencies = 39, + kPropertyChangeNotify = 40 }; RequestType fType; @@ -1629,6 +1630,47 @@ struct JackClientHasSessionCallbackRequest : public JackRequest }; + +struct JackPropertyChangeNotifyRequest : public JackRequest +{ + jack_uuid_t fSubject; + char fKey[JACK_UUID_STRING_SIZE]; + jack_property_change_t fChange; + + JackPropertyChangeNotifyRequest() : fChange((jack_property_change_t)0) + { + jack_uuid_clear(&fSubject); + memset(fKey, 0, sizeof(fKey)); + } + JackPropertyChangeNotifyRequest(jack_uuid_t subject, const char* key, jack_property_change_t change) + : JackRequest(JackRequest::kPropertyChangeNotify), fChange(change) + { + jack_uuid_copy(&fSubject, subject); + memset(fKey, 0, sizeof(fKey)); + strncpy(fKey, key, sizeof(fKey)-1); + } + + int Read(detail::JackChannelTransactionInterface* trans) + { + CheckSize(); + CheckRes(trans->Read(&fSubject, sizeof(fSubject))); + CheckRes(trans->Read(&fKey, sizeof(fKey))); + CheckRes(trans->Read(&fChange, sizeof(fChange))); + return 0; + } + + int Write(detail::JackChannelTransactionInterface* trans) + { + CheckRes(JackRequest::Write(trans, Size())); + CheckRes(trans->Write(&fSubject, sizeof(fSubject))); + CheckRes(trans->Write(&fKey, sizeof(fKey))); + CheckRes(trans->Write(&fChange, sizeof(fChange))); + return 0; + } + + int Size() { return sizeof(fSubject) + sizeof(fKey) + sizeof(fChange); } +}; + /*! \brief ClientNotification. */ diff --git a/common/JackRequestDecoder.cpp b/common/JackRequestDecoder.cpp index e1245f3d..46bbd1f3 100644 --- a/common/JackRequestDecoder.cpp +++ b/common/JackRequestDecoder.cpp @@ -338,6 +338,14 @@ int JackRequestDecoder::HandleRequest(detail::JackChannelTransactionInterface* s break; } + case JackRequest::kPropertyChangeNotify: { + jack_log("JackRequest::PropertyChangeNotify"); + JackPropertyChangeNotifyRequest req; + CheckRead(req, socket); + fServer->GetEngine()->PropertyChangeNotify(req.fSubject, req.fKey, req.fChange); + break; + } + default: jack_error("Unknown request %ld", type); return -1; diff --git a/common/wscript b/common/wscript index 60874720..48c0ad9d 100644 --- a/common/wscript +++ b/common/wscript @@ -74,7 +74,7 @@ def build(bld): includes.append('..') else: includes.append('../..') - uselib = ['PTHREAD', 'CELT', 'OPUS'] + uselib = ['PTHREAD', 'CELT', 'OPUS', 'DB'] if bld.env['IS_LINUX']: common_libsources += [ @@ -156,6 +156,7 @@ def build(bld): clientlib.source += [ 'JackLibClient.cpp', 'JackLibAPI.cpp', + 'JackMetadata.cpp', ] if bld.env['IS_LINUX']: diff --git a/wscript b/wscript index 9df8855f..41f4c392 100644 --- a/wscript +++ b/wscript @@ -172,6 +172,11 @@ def options(opt): help='Use systemd notify') sd.check(header_name='systemd/sd-daemon.h') sd.check(lib='systemd') + db = opt.add_auto_option( + 'db', + help='Use Berkeley DB (metadata)') + db.check(header_name='db.h') + db.check(lib='db') # dbus options opt.recurse('dbus')