|
|
|
@@ -0,0 +1,477 @@ |
|
|
|
/* |
|
|
|
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 <string.h> |
|
|
|
#include <db.h> |
|
|
|
#include <limits.h> |
|
|
|
|
|
|
|
#include <jack/metadata.h> |
|
|
|
#include <jack/uuid.h> |
|
|
|
|
|
|
|
#include "internal.h" |
|
|
|
|
|
|
|
static DB* db = NULL; |
|
|
|
|
|
|
|
static int |
|
|
|
jack_property_init (const char* server_name) |
|
|
|
{ |
|
|
|
int ret; |
|
|
|
char dbpath[PATH_MAX+1]; |
|
|
|
char server_dir[PATH_MAX+1]; |
|
|
|
|
|
|
|
/* idempotent */ |
|
|
|
|
|
|
|
if (db) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
if ((ret = db_create (&db, NULL, 0)) != 0) { |
|
|
|
jack_error ("Cannot initialize metadata DB (%s)", db_strerror (ret)); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
snprintf (dbpath, sizeof (dbpath), "%s/%s", jack_server_dir (server_name, server_dir), "metadata.db"); |
|
|
|
|
|
|
|
if ((ret = db->open (db, NULL, dbpath, NULL, DB_HASH, DB_CREATE|DB_THREAD, 0666)) != 0) { |
|
|
|
jack_error ("Cannot open metadata DB at %s: %s", dbpath, db_strerror (ret)); |
|
|
|
db->close (db, 0); |
|
|
|
db = NULL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void jack_properties_uninit () __attribute__ ((destructor)); |
|
|
|
|
|
|
|
void |
|
|
|
jack_properties_uninit () |
|
|
|
{ |
|
|
|
if (db) { |
|
|
|
db->close (db, 0); |
|
|
|
db = NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
jack_free_description(jack_description_t* desc) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
make_key_dbt (DBT* dbt, jack_uuid_t subject, const char* key) |
|
|
|
{ |
|
|
|
char ustr[JACK_UUID_STRING_SIZE]; |
|
|
|
size_t len1, len2; |
|
|
|
|
|
|
|
memset(dbt, 0, sizeof(DBT)); |
|
|
|
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 (dbt->data + len1, key, len2); // copy key+null |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
|
jack_set_property (jack_uuid_t subject, |
|
|
|
const char* key, |
|
|
|
const char* value, |
|
|
|
const char* type) |
|
|
|
{ |
|
|
|
DBT d_key; |
|
|
|
DBT data; |
|
|
|
int ret; |
|
|
|
size_t len1, len2; |
|
|
|
|
|
|
|
if (jack_property_init (NULL)) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/* build a key */ |
|
|
|
|
|
|
|
make_key_dbt (&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 (data.data + len1, type, len2); |
|
|
|
} |
|
|
|
|
|
|
|
if ((ret = db->put (db, 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)); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
jack_get_property (jack_uuid_t subject, |
|
|
|
const char* key, |
|
|
|
char** value, |
|
|
|
char** type) |
|
|
|
{ |
|
|
|
DBT d_key; |
|
|
|
DBT data; |
|
|
|
int ret; |
|
|
|
size_t len1, len2; |
|
|
|
|
|
|
|
if (key == NULL || key[0] == '\0') { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
if (jack_property_init (NULL)) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/* build a key */ |
|
|
|
|
|
|
|
make_key_dbt (&d_key, subject, key); |
|
|
|
|
|
|
|
/* setup data DBT */ |
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data)); |
|
|
|
data.flags = DB_DBT_MALLOC; |
|
|
|
|
|
|
|
if ((ret = db->get (db, NULL, &d_key, &data, 0)) != 0) { |
|
|
|
if (ret != DB_NOTFOUND) { |
|
|
|
char ustr[JACK_UUID_STRING_SIZE]; |
|
|
|
jack_uuid_unparse (subject, ustr); |
|
|
|
jack_error ("Cannot metadata for %s/%s (%s)", ustr, key, db_strerror (ret)); |
|
|
|
} |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/* result must have at least 2 chars plus 2 nulls to be valid |
|
|
|
*/ |
|
|
|
|
|
|
|
if (data.size < 4) { |
|
|
|
if (data.size > 0) { |
|
|
|
free (data.data); |
|
|
|
} |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
len1 = strlen (data.data) + 1; |
|
|
|
(*value) = (char *) malloc (len1); |
|
|
|
memcpy (*value, data.data, len1); |
|
|
|
|
|
|
|
if (len1 < data.size) { |
|
|
|
len2 = strlen (data.data+len1) + 1; |
|
|
|
|
|
|
|
(*type) = (char *) malloc (len2); |
|
|
|
memcpy (*type, data.data+len1, len2); |
|
|
|
} else { |
|
|
|
/* no type specified, assume default */ |
|
|
|
*type = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
if (data.size) { |
|
|
|
free (data.data); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
jack_get_properties (jack_uuid_t subject, |
|
|
|
jack_description_t* desc) |
|
|
|
{ |
|
|
|
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; |
|
|
|
|
|
|
|
jack_uuid_unparse (subject, ustr); |
|
|
|
|
|
|
|
if (jack_property_init (NULL)) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((ret = db->cursor (db, 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 (data.size > 0) { |
|
|
|
free (data.data); |
|
|
|
} |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (memcmp (ustr, key.data, JACK_UUID_STRING_SIZE) != 0) { |
|
|
|
/* not relevant */ |
|
|
|
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 (data.size > 0) { |
|
|
|
free (data.data); |
|
|
|
} |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
/* realloc array if necessary */ |
|
|
|
|
|
|
|
if (cnt == props_size) { |
|
|
|
if (props_size == 0) { |
|
|
|
props_size = 8; |
|
|
|
} 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 = malloc (key.size); |
|
|
|
memcpy ((char*) prop->key, key.data + JACK_UUID_STRING_SIZE, len1); |
|
|
|
|
|
|
|
/* copy data */ |
|
|
|
|
|
|
|
len1 = strlen (data.data) + 1; |
|
|
|
prop->data = (char *) malloc (len1); |
|
|
|
memcpy ((char*) prop->data, data.data, len1); |
|
|
|
|
|
|
|
if (len1 < data.size) { |
|
|
|
len2 = strlen (data.data+len1) + 1; |
|
|
|
|
|
|
|
prop->type= (char *) malloc (len2); |
|
|
|
memcpy ((char*) prop->type, data.data+len1, len2); |
|
|
|
} else { |
|
|
|
/* no type specified, assume default */ |
|
|
|
prop->type = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
if (data.size) { |
|
|
|
free (data.data); |
|
|
|
} |
|
|
|
|
|
|
|
++cnt; |
|
|
|
} |
|
|
|
|
|
|
|
cursor->close (cursor); |
|
|
|
|
|
|
|
return cnt; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
jack_get_all_properties (jack_description_t** desc) |
|
|
|
{ |
|
|
|
DBT key; |
|
|
|
DBT data; |
|
|
|
DBC* cursor; |
|
|
|
int ret; |
|
|
|
size_t cnt = 0; |
|
|
|
|
|
|
|
if (jack_property_init (NULL)) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((ret = db->cursor (db, 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) { |
|
|
|
|
|
|
|
if (data.size) { |
|
|
|
free (data.data); |
|
|
|
} |
|
|
|
|
|
|
|
++cnt; |
|
|
|
} |
|
|
|
|
|
|
|
cursor->close (cursor); |
|
|
|
|
|
|
|
return cnt; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
|
jack_get_description (jack_uuid_t subject, |
|
|
|
jack_description_t* desc) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
jack_get_all_descriptions (jack_description_t** descs) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int jack_set_property_change_callback (jack_client_t* client, |
|
|
|
JackPropertyChangeCallback callback, |
|
|
|
void *arg) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
jack_remove_property (jack_uuid_t subject, const char* key) |
|
|
|
{ |
|
|
|
DBT d_key; |
|
|
|
int ret; |
|
|
|
|
|
|
|
if (jack_property_init (NULL)) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
make_key_dbt (&d_key, subject, key); |
|
|
|
if ((ret = db->del (db, NULL, &d_key, 0)) != 0) { |
|
|
|
jack_error ("Cannot delete key %s (%s)", key, db_strerror (ret)); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
jack_remove_properties (jack_uuid_t subject) |
|
|
|
{ |
|
|
|
DBT key; |
|
|
|
DBT data; |
|
|
|
DBC* cursor; |
|
|
|
int ret; |
|
|
|
char ustr[JACK_UUID_STRING_SIZE]; |
|
|
|
int retval = 0; |
|
|
|
|
|
|
|
jack_uuid_unparse (subject, ustr); |
|
|
|
|
|
|
|
if (jack_property_init (NULL)) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((ret = db->cursor (db, 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 (data.size > 0) { |
|
|
|
free (data.data); |
|
|
|
} |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (memcmp (ustr, key.data, JACK_UUID_STRING_SIZE) != 0) { |
|
|
|
/* not relevant */ |
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
cursor->close (cursor); |
|
|
|
|
|
|
|
return retval; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
jack_remove_all_properties () |
|
|
|
{ |
|
|
|
int ret; |
|
|
|
|
|
|
|
if (jack_property_init (NULL)) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
if ((ret = db->truncate (db, NULL, NULL, 0)) != 0) { |
|
|
|
jack_error ("Cannot clear properties (%s)", db_strerror (ret)); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |