821 lines
21 KiB
C++
821 lines
21 KiB
C++
/*
|
|
Copyright (C) 2011 David Robillard
|
|
Copyright (C) 2013 Paul Davis
|
|
Copyright (C) 2019 Filipe Coelho
|
|
|
|
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 "JackTools.h"
|
|
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <limits.h>
|
|
|
|
|
|
#define JACK_METADATA_PREFIX "http://jackaudio.org/metadata/"
|
|
LIB_EXPORT const char* JACK_METADATA_CONNECTED = JACK_METADATA_PREFIX "connected";
|
|
LIB_EXPORT const char* JACK_METADATA_EVENT_TYPES = JACK_METADATA_PREFIX "event-types";
|
|
LIB_EXPORT const char* JACK_METADATA_HARDWARE = JACK_METADATA_PREFIX "hardware";
|
|
LIB_EXPORT const char* JACK_METADATA_ICON_LARGE = JACK_METADATA_PREFIX "icon-large";
|
|
LIB_EXPORT const char* JACK_METADATA_ICON_NAME = JACK_METADATA_PREFIX "icon-name";
|
|
LIB_EXPORT const char* JACK_METADATA_ICON_SMALL = JACK_METADATA_PREFIX "icon-small";
|
|
LIB_EXPORT const char* JACK_METADATA_ORDER = JACK_METADATA_PREFIX "order";
|
|
LIB_EXPORT const char* JACK_METADATA_PORT_GROUP = JACK_METADATA_PREFIX "port-group";
|
|
LIB_EXPORT const char* JACK_METADATA_PRETTY_NAME = JACK_METADATA_PREFIX "pretty-name";
|
|
LIB_EXPORT const char* JACK_METADATA_SIGNAL_TYPE = JACK_METADATA_PREFIX "signal-type";
|
|
#undef JACK_METADATA_PREFIX
|
|
|
|
namespace Jack
|
|
{
|
|
|
|
JackMetadata::JackMetadata(bool isEngine)
|
|
#if HAVE_DB
|
|
: fDB(NULL), fDBenv(NULL), fIsEngine(isEngine)
|
|
#endif
|
|
{
|
|
PropertyInit();
|
|
}
|
|
|
|
JackMetadata::~JackMetadata()
|
|
{
|
|
#if HAVE_DB
|
|
char dbpath[PATH_MAX + 1];
|
|
|
|
if (fDB) {
|
|
fDB->close (fDB, 0);
|
|
fDB = NULL;
|
|
}
|
|
if (fDBenv) {
|
|
fDBenv->close (fDBenv, 0);
|
|
fDBenv = NULL;
|
|
}
|
|
|
|
if (fIsEngine)
|
|
{
|
|
// cleanup after libdb, nasty!
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/metadata.db", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.001", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.002", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.003", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
// remove our custom dir
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d", fDBFilesDir, JackTools::GetUID());
|
|
rmdir (dbpath);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int JackMetadata::PropertyInit()
|
|
{
|
|
#if HAVE_DB
|
|
|
|
int ret;
|
|
char dbpath[PATH_MAX + 1];
|
|
|
|
#ifdef WIN32
|
|
ret = GetTempPathA (PATH_MAX, fDBFilesDir);
|
|
if ((ret > PATH_MAX) || (ret == 0)) {
|
|
jack_error ("cannot get path for temp files");
|
|
return -1;
|
|
}
|
|
#else
|
|
strncpy (fDBFilesDir, jack_server_dir, PATH_MAX);
|
|
#endif
|
|
|
|
/* 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;
|
|
}
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d", fDBFilesDir, JackTools::GetUID());
|
|
#ifdef WIN32
|
|
mkdir (dbpath);
|
|
#else
|
|
mkdir (dbpath, S_IRWXU | S_IRWXG);
|
|
#endif
|
|
|
|
if ((ret = fDBenv->open (fDBenv, dbpath, DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD, 0)) != 0) {
|
|
#if defined(WIN32) || defined(__APPLE__)
|
|
// new versions of jack2 are built with HAVE_MIXED_SIZE_ADDRESSING, which induces this error, this is expected
|
|
if (ret == DB_VERSION_MISMATCH) {
|
|
jack_error ("Failed to open previous DB environment, trying again clean...");
|
|
|
|
// cleanup old stuff
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/metadata.db", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.001", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.002", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/__db.003", fDBFilesDir, JackTools::GetUID());
|
|
remove (dbpath);
|
|
|
|
// try again fresh
|
|
ret = fDBenv->open (fDBenv, dbpath, DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD, 0);
|
|
}
|
|
if (ret != 0)
|
|
#endif
|
|
{
|
|
jack_error ("Cannot open DB environment: %s", db_strerror (ret));
|
|
fDBenv = NULL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if ((ret = db_create (&fDB, fDBenv, 0)) != 0) {
|
|
jack_error ("Cannot initialize metadata DB (%s)", db_strerror (ret));
|
|
fDBenv->close (fDBenv, 0);
|
|
fDBenv = NULL;
|
|
return -1;
|
|
}
|
|
|
|
snprintf (dbpath, sizeof(dbpath), "%s/jack_db-%d/metadata.db", fDBFilesDir, JackTools::GetUID());
|
|
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;
|
|
fDBenv->close (fDBenv, 0);
|
|
fDBenv = 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()) {
|
|
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()) {
|
|
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 1 char plus 1 null to be valid
|
|
(old rule was:
|
|
result must have at least 2 chars plus 2 nulls to be valid
|
|
)
|
|
*/
|
|
|
|
if (data.size < 2) {
|
|
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()) {
|
|
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 1 char plus 1 null to be valid
|
|
(old rule was:
|
|
result must have at least 2 chars plus 2 nulls to be valid
|
|
)
|
|
*/
|
|
|
|
if (data.size < 2) {
|
|
/* 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()) {
|
|
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()) {
|
|
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() || fDB == 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()) {
|
|
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
|
|
|
|
|
|
|