Browse Source

[metadata] Metadata properties implementation. (#383)

* [metadata] Metadata properties implementation.

* [metadata] Fixed for shared server metadata-base accessor; alsofixed coding/naming style.

* [metadata] Fixed a tab for space.

* [metadata] Use of Berkeley DB is now truly optional on configure time.

* [metadata] Fixed tabs for spaces, again.

* [metadata] Fixed for shared metadata-base initialization and external clients.

* [metadata] Blind-fix for windows codebase.

* [metadata] Metadata API moved into client-side library only.

* [metadata] Fixed jack_port_uuid() stubbiness, now returning a proper UUID from port index.

* [metadata] Uniform method names.

* [metadata] Fixed PropertyChangeNotify through server async call.
tags/v1.9.13
Rui Nuno Capela Filipe Coelho <falktx@falktx.com> 6 years ago
parent
commit
fed6f1e66d
20 changed files with 1131 additions and 81 deletions
  1. +47
    -74
      common/JackAPI.cpp
  2. +4
    -0
      common/JackChannel.h
  3. +37
    -0
      common/JackClient.cpp
  4. +8
    -0
      common/JackClient.h
  5. +24
    -3
      common/JackEngine.cpp
  6. +2
    -0
      common/JackEngine.h
  7. +7
    -0
      common/JackGenericClientChannel.cpp
  8. +2
    -0
      common/JackGenericClientChannel.h
  9. +126
    -0
      common/JackLibAPI.cpp
  10. +10
    -0
      common/JackLibClient.cpp
  11. +2
    -0
      common/JackLibClient.h
  12. +5
    -0
      common/JackLibGlobals.h
  13. +9
    -0
      common/JackLockedEngine.h
  14. +730
    -0
      common/JackMetadata.cpp
  15. +59
    -2
      common/JackMetadata.h
  16. +1
    -0
      common/JackNotification.h
  17. +43
    -1
      common/JackRequest.h
  18. +8
    -0
      common/JackRequestDecoder.cpp
  19. +2
    -1
      common/wscript
  20. +5
    -0
      wscript

+ 47
- 74
common/JackAPI.cpp View File

@@ -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;
}

+ 4
- 0
common/JackChannel.h View File

@@ -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;


+ 37
- 0
common/JackClient.cpp View File

@@ -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


+ 8
- 0
common/JackClient.h View File

@@ -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();


+ 24
- 3
common/JackEngine.cpp View File

@@ -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
//---------------


+ 2
- 0
common/JackEngine.h View File

@@ -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);



+ 7
- 0
common/JackGenericClientChannel.cpp View File

@@ -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




+ 2
- 0
common/JackGenericClientChannel.h View File

@@ -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


+ 126
- 0
common/JackLibAPI.cpp View File

@@ -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);
}
}

+ 10
- 0
common/JackLibClient.cpp View File

@@ -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
//-------------------


+ 2
- 0
common/JackLibClient.h View File

@@ -54,6 +54,8 @@ class JackLibClient : public JackClient
JackClientControl* GetClientControl() const;
};

// Used for client-side Metadata API (JackLibAPI.cpp)
JackMetadata* GetMetadata();

} // end of namespace



+ 5
- 0
common/JackLibGlobals.h View File

@@ -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


+ 9
- 0
common/JackLockedEngine.h View File

@@ -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


+ 730
- 0
common/JackMetadata.cpp View File

@@ -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 = &current_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




+ 59
- 2
common/JackMetadata.h View File

@@ -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

+ 1
- 0
common/JackNotification.h View File

@@ -47,6 +47,7 @@ enum NotificationType {
kQUIT = 16,
kSessionCallback = 17,
kLatencyCallback = 18,
kPropertyChangeCallback = 19,
kMaxNotification = 64 // To keep some room in JackClientControl fCallback table
};



+ 43
- 1
common/JackRequest.h View File

@@ -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.
*/


+ 8
- 0
common/JackRequestDecoder.cpp View File

@@ -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;


+ 2
- 1
common/wscript View File

@@ -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']:


+ 5
- 0
wscript View File

@@ -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')


Loading…
Cancel
Save