* [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 "JackGlobals.h" | ||||
#include "JackTime.h" | #include "JackTime.h" | ||||
#include "JackPortType.h" | #include "JackPortType.h" | ||||
#include "JackMetadata.h" | |||||
#include <math.h> | #include <math.h> | ||||
using namespace Jack; | using namespace Jack; | ||||
@@ -39,13 +38,6 @@ extern "C" | |||||
typedef void (*print_function)(const char*); | typedef void (*print_function)(const char*); | ||||
typedef void *(*thread_routine)(void*); | 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 | LIB_EXPORT | ||||
void | void | ||||
jack_get_version( | jack_get_version( | ||||
@@ -273,16 +265,6 @@ extern "C" | |||||
LIB_EXPORT void jack_session_commands_free(jack_session_command_t *cmds); | 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_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_client_uuid_generate(); | ||||
LIB_EXPORT jack_uuid_t jack_port_uuid_generate(uint32_t port_id); | LIB_EXPORT jack_uuid_t jack_port_uuid_generate(uint32_t port_id); | ||||
LIB_EXPORT uint32_t jack_uuid_to_index(jack_uuid_t); | 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) | 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 "types.h" | ||||
#include "JackSession.h" | #include "JackSession.h" | ||||
#include "JackMetadata.h" | |||||
namespace Jack | namespace Jack | ||||
{ | { | ||||
@@ -186,6 +187,9 @@ class JackClientChannelInterface | |||||
virtual void ClientHasSessionCallback(const char* client_name, int* result) | 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() | virtual bool IsChannelThread() | ||||
{ | { | ||||
return false; | return false; | ||||
@@ -59,6 +59,7 @@ JackClient::JackClient(JackSynchro* table):fThread(this) | |||||
fThreadFun = NULL; | fThreadFun = NULL; | ||||
fSession = NULL; | fSession = NULL; | ||||
fLatency = NULL; | fLatency = NULL; | ||||
fPropertyChange = NULL; | |||||
fProcessArg = NULL; | fProcessArg = NULL; | ||||
fGraphOrderArg = NULL; | fGraphOrderArg = NULL; | ||||
@@ -77,6 +78,7 @@ JackClient::JackClient(JackSynchro* table):fThread(this) | |||||
fThreadFunArg = NULL; | fThreadFunArg = NULL; | ||||
fSessionArg = NULL; | fSessionArg = NULL; | ||||
fLatencyArg = NULL; | fLatencyArg = NULL; | ||||
fPropertyChangeArg = NULL; | |||||
fSessionReply = kPendingSessionReply; | fSessionReply = kPendingSessionReply; | ||||
} | } | ||||
@@ -319,6 +321,17 @@ int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, | |||||
case kLatencyCallback: | case kLatencyCallback: | ||||
res = HandleLatencyCallback(value1); | res = HandleLatencyCallback(value1); | ||||
break; | 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 | // Internal clients | ||||
//------------------ | //------------------ | ||||
@@ -1309,5 +1334,17 @@ int JackClient::ClientHasSessionCallback(const char* client_name) | |||||
return result; | 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 | } // end of namespace | ||||
@@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
#include "JackPlatformPlug.h" | #include "JackPlatformPlug.h" | ||||
#include "JackChannel.h" | #include "JackChannel.h" | ||||
#include "JackRequest.h" | #include "JackRequest.h" | ||||
#include "JackMetadata.h" | |||||
#include "varargs.h" | #include "varargs.h" | ||||
#include <list> | #include <list> | ||||
@@ -68,6 +69,7 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable | |||||
JackThreadCallback fThreadFun; | JackThreadCallback fThreadFun; | ||||
JackSessionCallback fSession; | JackSessionCallback fSession; | ||||
JackLatencyCallback fLatency; | JackLatencyCallback fLatency; | ||||
JackPropertyChangeCallback fPropertyChange; | |||||
void* fProcessArg; | void* fProcessArg; | ||||
void* fGraphOrderArg; | void* fGraphOrderArg; | ||||
@@ -87,6 +89,8 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable | |||||
void* fThreadFunArg; | void* fThreadFunArg; | ||||
void* fSessionArg; | void* fSessionArg; | ||||
void* fLatencyArg; | void* fLatencyArg; | ||||
void* fPropertyChangeArg; | |||||
char fServerName[JACK_SERVER_NAME_SIZE+1]; | char fServerName[JACK_SERVER_NAME_SIZE+1]; | ||||
JackThread fThread; /*! Thread to execute the Process function */ | 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 SetPortRenameCallback(JackPortRenameCallback callback, void *arg); | ||||
virtual int SetSessionCallback(JackSessionCallback callback, void *arg); | virtual int SetSessionCallback(JackSessionCallback callback, void *arg); | ||||
virtual int SetLatencyCallback(JackLatencyCallback callback, void *arg); | virtual int SetLatencyCallback(JackLatencyCallback callback, void *arg); | ||||
virtual int SetPropertyChangeCallback(JackPropertyChangeCallback callback, void* arg); | |||||
// Internal clients | // Internal clients | ||||
virtual char* GetInternalClientName(int ref); | 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 ReserveClientName(const char* client_name, const char* uuid); | ||||
virtual int ClientHasSessionCallback(const char* client_name); | 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 | // JackRunnableInterface interface | ||||
bool Init(); | bool Init(); | ||||
bool Execute(); | bool Execute(); | ||||
@@ -102,6 +102,7 @@ void JackEngine::NotifyQuit() | |||||
fChannel.NotifyQuit(); | fChannel.NotifyQuit(); | ||||
} | } | ||||
//----------------------------- | //----------------------------- | ||||
// Client ressource management | // Client ressource management | ||||
//----------------------------- | //----------------------------- | ||||
@@ -239,15 +240,15 @@ int JackEngine::ComputeTotalLatencies() | |||||
fGraphManager->TopologicalSort(sorted); | fGraphManager->TopologicalSort(sorted); | ||||
/* iterate over all clients in graph order, and emit | /* iterate over all clients in graph order, and emit | ||||
* capture latency callback. | |||||
*/ | |||||
* capture latency callback. | |||||
*/ | |||||
for (it = sorted.begin(); it != sorted.end(); it++) { | for (it = sorted.begin(); it != sorted.end(); it++) { | ||||
NotifyClient(*it, kLatencyCallback, true, "", 0, 0); | NotifyClient(*it, kLatencyCallback, true, "", 0, 0); | ||||
} | } | ||||
/* now issue playback latency callbacks in reverse graph order. | /* now issue playback latency callbacks in reverse graph order. | ||||
*/ | |||||
*/ | |||||
for (rit = sorted.rbegin(); rit != sorted.rend(); rit++) { | for (rit = sorted.rbegin(); rit != sorted.rend(); rit++) { | ||||
NotifyClient(*rit, kLatencyCallback, true, "", 1, 0); | NotifyClient(*rit, kLatencyCallback, true, "", 1, 0); | ||||
} | } | ||||
@@ -255,6 +256,26 @@ int JackEngine::ComputeTotalLatencies() | |||||
return 0; | 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 | // Notifications | ||||
//--------------- | //--------------- | ||||
@@ -141,6 +141,8 @@ class SERVER_EXPORT JackEngine : public JackLockAble | |||||
int ComputeTotalLatencies(); | int ComputeTotalLatencies(); | ||||
int PropertyChangeNotify(jack_uuid_t subject, const char* key,jack_property_change_t change); | |||||
// Graph | // Graph | ||||
bool Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end); | 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; | *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 | } // 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 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 ReserveClientName(int refnum, const char* client_name, const char *uuid, int* result); | ||||
void ClientHasSessionCallback(const char* client_name, 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 | } // end of namespace | ||||
@@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
#include "JackTools.h" | #include "JackTools.h" | ||||
#include "JackSystemDeps.h" | #include "JackSystemDeps.h" | ||||
#include "JackServerLaunch.h" | #include "JackServerLaunch.h" | ||||
#include "JackMetadata.h" | |||||
#include <assert.h> | #include <assert.h> | ||||
using namespace Jack; | using namespace Jack; | ||||
@@ -46,6 +47,23 @@ extern "C" | |||||
LIB_EXPORT int jack_client_close (jack_client_t *client); | LIB_EXPORT int jack_client_close (jack_client_t *client); | ||||
LIB_EXPORT int jack_get_client_pid (const char *name); | 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 | #ifdef __cplusplus | ||||
} | } | ||||
@@ -213,3 +231,111 @@ LIB_EXPORT int jack_get_client_pid(const char *name) | |||||
return 0; | 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); | 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 | // Client management | ||||
//------------------- | //------------------- | ||||
@@ -54,6 +54,8 @@ class JackLibClient : public JackClient | |||||
JackClientControl* GetClientControl() const; | JackClientControl* GetClientControl() const; | ||||
}; | }; | ||||
// Used for client-side Metadata API (JackLibAPI.cpp) | |||||
JackMetadata* GetMetadata(); | |||||
} // end of namespace | } // end of namespace | ||||
@@ -55,6 +55,7 @@ struct JackLibGlobals | |||||
JackShmReadWritePtr<JackGraphManager> fGraphManager; /*! Shared memory Port manager */ | JackShmReadWritePtr<JackGraphManager> fGraphManager; /*! Shared memory Port manager */ | ||||
JackShmReadWritePtr<JackEngineControl> fEngineControl; /*! Shared engine control */ // transport engine has to be writable | JackShmReadWritePtr<JackEngineControl> fEngineControl; /*! Shared engine control */ // transport engine has to be writable | ||||
JackSynchro fSynchroTable[CLIENT_NUM]; /*! Shared synchro table */ | JackSynchro fSynchroTable[CLIENT_NUM]; /*! Shared synchro table */ | ||||
JackMetadata *fMetadata; /*! Shared metadata base */ | |||||
sigset_t fProcessSignals; | sigset_t fProcessSignals; | ||||
static int fClientCount; | static int fClientCount; | ||||
@@ -69,6 +70,8 @@ struct JackLibGlobals | |||||
fGraphManager = -1; | fGraphManager = -1; | ||||
fEngineControl = -1; | fEngineControl = -1; | ||||
fMetadata = new JackMetadata(NULL); | |||||
// Filter SIGPIPE to avoid having client get a SIGPIPE when trying to access a died server. | // Filter SIGPIPE to avoid having client get a SIGPIPE when trying to access a died server. | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
// TODO | // TODO | ||||
@@ -88,6 +91,8 @@ struct JackLibGlobals | |||||
} | } | ||||
JackMessageBuffer::Destroy(); | JackMessageBuffer::Destroy(); | ||||
delete fMetadata; | |||||
// Restore old signal mask | // Restore old signal mask | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
// TODO | // TODO | ||||
@@ -361,6 +361,7 @@ class SERVER_EXPORT JackLockedEngine | |||||
return fEngine.GetUUIDForClientName(client_name, uuid_res); | return fEngine.GetUUIDForClientName(client_name, uuid_res); | ||||
CATCH_EXCEPTION_RETURN | CATCH_EXCEPTION_RETURN | ||||
} | } | ||||
int GetClientNameForUUID(const char *uuid, char *name_res) | int GetClientNameForUUID(const char *uuid, char *name_res) | ||||
{ | { | ||||
TRY_CALL | TRY_CALL | ||||
@@ -383,6 +384,14 @@ class SERVER_EXPORT JackLockedEngine | |||||
return fEngine.ClientHasSessionCallback(name); | return fEngine.ClientHasSessionCallback(name); | ||||
CATCH_EXCEPTION_RETURN | 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 | } // 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. | 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> | #include <stdint.h> | ||||
#if HAVE_DB | |||||
#include <db.h> | |||||
#endif | |||||
#include <jack/uuid.h> | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
extern "C" { | extern "C" { | ||||
#endif | #endif | ||||
@@ -53,4 +59,55 @@ typedef void (*JackPropertyChangeCallback)(jack_uuid_t subject, | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #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 | #endif |
@@ -47,6 +47,7 @@ enum NotificationType { | |||||
kQUIT = 16, | kQUIT = 16, | ||||
kSessionCallback = 17, | kSessionCallback = 17, | ||||
kLatencyCallback = 18, | kLatencyCallback = 18, | ||||
kPropertyChangeCallback = 19, | |||||
kMaxNotification = 64 // To keep some room in JackClientControl fCallback table | kMaxNotification = 64 // To keep some room in JackClientControl fCallback table | ||||
}; | }; | ||||
@@ -89,7 +89,8 @@ struct JackRequest | |||||
kReserveClientName = 36, | kReserveClientName = 36, | ||||
kGetUUIDByClient = 37, | kGetUUIDByClient = 37, | ||||
kClientHasSessionCallback = 38, | kClientHasSessionCallback = 38, | ||||
kComputeTotalLatencies = 39 | |||||
kComputeTotalLatencies = 39, | |||||
kPropertyChangeNotify = 40 | |||||
}; | }; | ||||
RequestType fType; | 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. | \brief ClientNotification. | ||||
*/ | */ | ||||
@@ -338,6 +338,14 @@ int JackRequestDecoder::HandleRequest(detail::JackChannelTransactionInterface* s | |||||
break; | break; | ||||
} | } | ||||
case JackRequest::kPropertyChangeNotify: { | |||||
jack_log("JackRequest::PropertyChangeNotify"); | |||||
JackPropertyChangeNotifyRequest req; | |||||
CheckRead(req, socket); | |||||
fServer->GetEngine()->PropertyChangeNotify(req.fSubject, req.fKey, req.fChange); | |||||
break; | |||||
} | |||||
default: | default: | ||||
jack_error("Unknown request %ld", type); | jack_error("Unknown request %ld", type); | ||||
return -1; | return -1; | ||||
@@ -74,7 +74,7 @@ def build(bld): | |||||
includes.append('..') | includes.append('..') | ||||
else: | else: | ||||
includes.append('../..') | includes.append('../..') | ||||
uselib = ['PTHREAD', 'CELT', 'OPUS'] | |||||
uselib = ['PTHREAD', 'CELT', 'OPUS', 'DB'] | |||||
if bld.env['IS_LINUX']: | if bld.env['IS_LINUX']: | ||||
common_libsources += [ | common_libsources += [ | ||||
@@ -156,6 +156,7 @@ def build(bld): | |||||
clientlib.source += [ | clientlib.source += [ | ||||
'JackLibClient.cpp', | 'JackLibClient.cpp', | ||||
'JackLibAPI.cpp', | 'JackLibAPI.cpp', | ||||
'JackMetadata.cpp', | |||||
] | ] | ||||
if bld.env['IS_LINUX']: | if bld.env['IS_LINUX']: | ||||
@@ -172,6 +172,11 @@ def options(opt): | |||||
help='Use systemd notify') | help='Use systemd notify') | ||||
sd.check(header_name='systemd/sd-daemon.h') | sd.check(header_name='systemd/sd-daemon.h') | ||||
sd.check(lib='systemd') | 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 | # dbus options | ||||
opt.recurse('dbus') | opt.recurse('dbus') | ||||