* [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.tags/v1.9.13
| @@ -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 <math.h> | |||
| 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; | |||
| } | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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 <list> | |||
| @@ -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(); | |||
| @@ -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 | |||
| //--------------- | |||
| @@ -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); | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 <assert.h> | |||
| 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); | |||
| } | |||
| } | |||
| @@ -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 | |||
| //------------------- | |||
| @@ -54,6 +54,8 @@ class JackLibClient : public JackClient | |||
| JackClientControl* GetClientControl() const; | |||
| }; | |||
| // Used for client-side Metadata API (JackLibAPI.cpp) | |||
| JackMetadata* GetMetadata(); | |||
| } // end of namespace | |||
| @@ -55,6 +55,7 @@ struct JackLibGlobals | |||
| JackShmReadWritePtr<JackGraphManager> fGraphManager; /*! Shared memory Port manager */ | |||
| JackShmReadWritePtr<JackEngineControl> 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 | |||
| @@ -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 | |||
| @@ -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 <string.h> | |||
| #include <limits.h> | |||
| 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 | |||
| @@ -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 <stdint.h> | |||
| #if HAVE_DB | |||
| #include <db.h> | |||
| #endif | |||
| #include <jack/uuid.h> | |||
| #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 | |||
| @@ -47,6 +47,7 @@ enum NotificationType { | |||
| kQUIT = 16, | |||
| kSessionCallback = 17, | |||
| kLatencyCallback = 18, | |||
| kPropertyChangeCallback = 19, | |||
| kMaxNotification = 64 // To keep some room in JackClientControl fCallback table | |||
| }; | |||
| @@ -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. | |||
| */ | |||
| @@ -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; | |||
| @@ -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']: | |||
| @@ -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') | |||