* [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') | |||