aboutsummaryrefslogtreecommitdiffstats
path: root/src/anydb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/anydb.c')
-rw-r--r--src/anydb.c435
1 files changed, 288 insertions, 147 deletions
diff --git a/src/anydb.c b/src/anydb.c
index d522c96..9196e5c 100644
--- a/src/anydb.c
+++ b/src/anydb.c
@@ -14,6 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+/******************************************************************************/
+/******************************************************************************/
+/* HIGH LEVEL ABSTRACTION OF THE DATABASES */
+/******************************************************************************/
+/******************************************************************************/
#include <stdlib.h>
#include <stdint.h>
@@ -31,13 +36,38 @@
#define USER_MATCH_SCORE 1
#define PERMISSION_MATCH_SCORE 1
+/**
+ * helper for searching items
+ */
+union searchkey {
+ struct {
+ /** client id */
+ anydb_idx_t idxcli;
+
+ /** session id */
+ anydb_idx_t idxses;
+
+ /** user id */
+ anydb_idx_t idxusr;
+
+ /** permission string */
+ const char *strperm;
+ };
+ anydb_key_t key;
+};
+typedef union searchkey searchkey_t;
+
/******************************************************************************/
/******************************************************************************/
/*** UTILITIES ***/
/******************************************************************************/
/******************************************************************************/
-/** check whether the 'text' fit String_Any, NULL or "" */
+/**
+ * Check whether the 'text' fit String_Any, NULL or ""
+ * @param text the text to check
+ * @return true if text matches ANY possible values
+ */
static
bool
is_any(
@@ -46,7 +76,11 @@ is_any(
return text == NULL || text[0] == 0 || (!text[1] && text[0] == Data_Any_Char);
}
-/** check whether the 'text' fit String_Any, String_Wide, NULL or "" */
+/**
+ * check whether the 'text' fit String_Any, String_Wide, NULL or ""
+ * @param text the text to check
+ * @return true if text matches ANY or WIDE possible values
+ */
static
bool
is_any_or_wide(
@@ -56,7 +90,12 @@ is_any_or_wide(
|| (!text[1] && (text[0] == Data_Any_Char || text[0] == Data_Wide_Char));
}
-/** return the name of 'index' */
+/**
+ * Get the name stored for index
+ * @param db the anydb database to query
+ * @param idx the index of the string to get
+ * @return the name or NULL if doesn't exist
+ */
static
const char*
string(
@@ -70,7 +109,14 @@ string(
return db->itf.string(db->clodb, idx);
}
-/** search the index of 'name' and create it if 'create' */
+/**
+ * Search the index of 'name' and create it if 'create'
+ * @param db the anydb database to query
+ * @param idx where to store the result if needed
+ * @param name name to search and/or create
+ * @param create if not nul, the name is created if it doesn't exist
+ * @return 0 in case of success of -errno
+ */
static
int
idx(
@@ -79,27 +125,39 @@ idx(
const char *name,
bool create
) {
- /* special names */
+ /* handle special names */
if (!name || !name[0]) {
+ /* no name or empty name means ANY */
*idx = AnyIdx_Any;
return 0;
}
+ /* handle special names of one character */
if (!name[1]) {
if (name[0] == Data_Any_Char) {
+ /* Single char for ANY */
*idx = AnyIdx_Any;
return 0;
}
if (name[0] == Data_Wide_Char) {
+ /* Single char for WIDE */
*idx = AnyIdx_Wide;
return 0;
}
}
- /* other case */
+ /* other case: ask the database backend */
return db->itf.index(db->clodb, idx, name, create);
}
-/** search the index of 'name' and create it if 'create' */
+/**
+ * Search the index of 'name' and create it if 'create'
+ * Return the index for WIDE if name matches ANY or WIDE
+ * @param db the backend database
+ * @param idx where to store the found index
+ * @param name the name to search or create
+ * @param create not nul for authorizing creation of the index for name
+ * @return 0 on success
+ */
static
int
idx_but_any(
@@ -117,7 +175,14 @@ idx_but_any(
return db->itf.index(db->clodb, idx, name, create);
}
-/** search the index of 'name' and create it if 'create' */
+/**
+ * Return the index of 'name' in the database 'db'. In option 'create' it.
+ * If the name encode ANY or WIDE returns WIDE.
+ * @param db the backend database
+ * @param name the name to search or create
+ * @param create not nul for authorizing creation of the index for name
+ * @return the found index or AnyIdx_None if not found.
+ */
static
anydb_idx_t
idx_or_none_but_any(
@@ -134,11 +199,124 @@ idx_or_none_but_any(
/******************************************************************************/
/******************************************************************************/
+/*** SEARCH KEYS ***/
+/******************************************************************************/
+/******************************************************************************/
+
+static
+bool
+searchkey_prepare_match(anydb_t *db,
+ const data_key_t *key,
+ searchkey_t *skey,
+ bool create
+) {
+ if (idx(db, &skey->idxcli, key->client, create)
+ || idx(db, &skey->idxses, key->session, create)
+ || idx(db, &skey->idxusr, key->user, create))
+ return false; /* one of the idx doesn't exist */
+ skey->strperm = is_any(key->permission) ? NULL : key->permission;
+
+ return true;
+}
+
+static
+bool
+searchkey_match(
+ anydb_t *db,
+ const anydb_key_t *key,
+ const searchkey_t *skey
+) {
+ return (skey->idxcli == AnyIdx_Any || skey->idxcli == key->client)
+ && (skey->idxses == AnyIdx_Any || skey->idxses == key->session)
+ && (skey->idxusr == AnyIdx_Any || skey->idxusr == key->user)
+ && (!skey->strperm || !strcasecmp(skey->strperm, string(db, key->permission)));
+}
+
+static
+int
+searchkey_prepare_is(
+ anydb_t *db,
+ const data_key_t *key,
+ searchkey_t *skey,
+ bool create
+) {
+ int rc;
+
+ rc = idx_but_any(db, &skey->idxcli, key->client, create);
+ if (!rc) {
+ rc = idx_but_any(db, &skey->idxses, key->session, create);
+ if (!rc) {
+ rc = idx_but_any(db, &skey->idxusr, key->user, create);
+ if (!rc)
+ skey->strperm = key->permission;
+ }
+ }
+ return rc;
+}
+
+static
+bool
+searchkey_is(
+ anydb_t *db,
+ const anydb_key_t *key,
+ const searchkey_t *skey
+) {
+ return skey->idxcli == key->client
+ && skey->idxses == key->session
+ && skey->idxusr == key->user
+ && !strcasecmp(skey->strperm, string(db, key->permission));
+}
+
+static
+void
+searchkey_prepare_test(
+ anydb_t *db,
+ const data_key_t *key,
+ searchkey_t *skey,
+ bool create
+) {
+ skey->idxcli = idx_or_none_but_any(db, key->client, create);
+ skey->idxses = idx_or_none_but_any(db, key->session, create);
+ skey->idxusr = idx_or_none_but_any(db, key->user, create);
+ skey->strperm = key->permission;
+}
+
+static
+unsigned
+searchkey_test(
+ anydb_t *db,
+ const anydb_key_t *key,
+ const searchkey_t *skey
+) {
+ unsigned sc;
+
+ if ((key->client != AnyIdx_Wide && skey->idxcli != key->client)
+ || (key->session != AnyIdx_Wide && skey->idxses != key->session)
+ || (key->user != AnyIdx_Wide && skey->idxusr != key->user)
+ || (key->permission != AnyIdx_Wide
+ && strcasecmp(skey->strperm, string(db, key->permission)))) {
+ sc = 0;
+ } else {
+ sc = 1;
+ if (key->client != AnyIdx_Wide)
+ sc += CLIENT_MATCH_SCORE;
+ if (key->session != AnyIdx_Wide)
+ sc += SESSION_MATCH_SCORE;
+ if (key->user != AnyIdx_Wide)
+ sc += USER_MATCH_SCORE;
+ if (key->permission != AnyIdx_Wide)
+ sc += PERMISSION_MATCH_SCORE;
+ }
+ return sc;
+}
+
+/******************************************************************************/
+/******************************************************************************/
/*** FOR ALL ***/
/******************************************************************************/
/******************************************************************************/
-/** manage atomicity of operations */
+/* see anydb.h */
int
anydb_transaction(
anydb_t *db,
@@ -157,17 +335,14 @@ anydb_transaction(
struct for_all_s
{
- anydb_t *db;
+ anydb_t *db; /* targeted database */
+ time_t now; /* also drop expired items */
+ searchkey_t skey;
void *closure;
void (*callback)(
void *closure,
const data_key_t *key,
const data_value_t *value);
- anydb_idx_t idxcli;
- anydb_idx_t idxses;
- anydb_idx_t idxusr;
- const char *strperm;
- time_t now;
};
static
@@ -181,48 +356,41 @@ for_all_cb(
data_key_t k;
data_value_t v;
+ /* drop expired items */
if (value->expire && value->expire <= s->now)
return Anydb_Action_Remove_And_Continue;
- if ((s->idxcli == AnyIdx_Any || s->idxcli == key->client)
- && (s->idxses == AnyIdx_Any || s->idxses == key->session)
- && (s->idxusr == AnyIdx_Any || s->idxusr == key->user)) {
+ if (searchkey_match(s->db, key, &s->skey)) {
+ k.client = string(s->db, key->client);
+ k.session = string(s->db, key->session);
+ k.user = string(s->db, key->user);
k.permission = string(s->db, key->permission);
- if (!s->strperm || !strcasecmp(s->strperm, k.permission)) {
- k.client = string(s->db, key->client);
- k.session = string(s->db, key->session);
- k.user = string(s->db, key->user);
- v.value = string(s->db, value->value);
- v.expire = value->expire;
- s->callback(s->closure, &k, &v);
- }
+ v.value = string(s->db, value->value);
+ v.expire = value->expire;
+ s->callback(s->closure, &k, &v);
}
return Anydb_Action_Continue;
}
-/** enumerate */
+/* see anydb.h */
void
anydb_for_all(
anydb_t *db,
- void *closure,
void (*callback)(
void *closure,
const data_key_t *key,
const data_value_t *value),
+ void *closure,
const data_key_t *key
) {
struct for_all_s s;
+ if (!searchkey_prepare_match(db, key, &s.skey, false))
+ return; /* nothing to do! because one of the idx doesn't exist */
+
s.db = db;
s.closure = closure;
s.callback = callback;
-
- if (idx(db, &s.idxcli, key->client, false)
- || idx(db, &s.idxses, key->session, false)
- || idx(db, &s.idxusr, key->user, false))
- return; /* nothing to do! because one of the idx doesn't exist */
- s.strperm = is_any(key->permission) ? NULL : key->permission;
-
s.now = time(NULL);
db->itf.apply(db->clodb, for_all_cb, &s);
}
@@ -233,16 +401,15 @@ anydb_for_all(
/******************************************************************************/
/******************************************************************************/
+/* structure for dropping items */
struct drop_s
{
- anydb_t *db;
- anydb_idx_t idxcli;
- anydb_idx_t idxses;
- anydb_idx_t idxusr;
- const char *strperm;
- time_t now;
+ anydb_t *db; /* targeted database */
+ time_t now; /* also drop expired items */
+ searchkey_t skey; /* the search key */
};
+/* callback for dropping items */
static
anydb_action_t
drop_cb(
@@ -252,57 +419,50 @@ drop_cb(
) {
struct drop_s *s = closure;
+ /* drop expired items */
if (value->expire && value->expire <= s->now)
return Anydb_Action_Remove_And_Continue;
- if ((s->idxcli == AnyIdx_Any || s->idxcli == key->client)
- && (s->idxses == AnyIdx_Any || s->idxses == key->session)
- && (s->idxusr == AnyIdx_Any || s->idxusr == key->user)
- && (!s->strperm || !strcasecmp(s->strperm, string(s->db, key->permission))))
+ /* remove if matches the key */
+ if (searchkey_match(s->db, key, &s->skey))
return Anydb_Action_Remove_And_Continue;
+ /* continue to next */
return Anydb_Action_Continue;
}
-/** drop rules */
-int
+/* see anydb.h */
+void
anydb_drop(
anydb_t *db,
const data_key_t *key
) {
struct drop_s s;
- s.db = db;
-
- if (idx(db, &s.idxcli, key->client, false)
- || idx(db, &s.idxses, key->session, false)
- || idx(db, &s.idxusr, key->user, false))
- return 0; /* nothing to do! because one of the idx doesn't exist */
- s.strperm = is_any(key->permission) ? NULL : key->permission;
+ if (!searchkey_prepare_match(db, key, &s.skey, false))
+ return; /* nothing to do! because one of the idx doesn't exist */
+ s.db = db;
s.now = time(NULL);
db->itf.apply(db->clodb, drop_cb, &s);
- return 0;
}
/******************************************************************************/
/******************************************************************************/
-/*** ADD ***/
+/*** SET ***/
/******************************************************************************/
/******************************************************************************/
+/* structure for setting values */
struct set_s
{
- anydb_t *db;
- anydb_idx_t idxcli;
- anydb_idx_t idxses;
- anydb_idx_t idxusr;
- anydb_idx_t idxval;
- time_t expire;
- const char *strperm;
- time_t now;
+ anydb_t *db; /* targeted database */
+ time_t now; /* also drop expired items */
+ searchkey_t skey; /* searching key */
+ anydb_value_t value; /* value to set */
};
+/* callback for setting values */
static
anydb_action_t
set_cb(
@@ -312,22 +472,21 @@ set_cb(
) {
struct set_s *s = closure;
+ /* drop expired items */
if (value->expire && value->expire <= s->now)
return Anydb_Action_Remove_And_Continue;
- if (s->idxcli == key->client
- && s->idxses == key->session
- && s->idxusr == key->user
- && !strcasecmp(s->strperm, string(s->db, key->permission))) {
- value->value = s->idxval;
- value->expire = s->expire;
- s->db = NULL;
+ if (searchkey_is(s->db, key, &s->skey)) {
+ value->value = s->value.value;
+ value->expire = s->value.expire;
+ s->db = NULL; /* indicates that is found */
return Anydb_Action_Update_And_Stop;
}
return Anydb_Action_Continue;
}
+/* see anydb.h */
int
anydb_set(
anydb_t *db,
@@ -336,41 +495,25 @@ anydb_set(
) {
int rc;
struct set_s s;
- anydb_key_t k;
- anydb_value_t v;
-
- s.db = db;
- s.strperm = key->permission;
- s.expire = value->expire;
- rc = idx_but_any(db, &s.idxcli, key->client, true);
- if (rc)
- goto error;
- rc = idx_but_any(db, &s.idxses, key->session, true);
- if (rc)
- goto error;
- rc = idx_but_any(db, &s.idxusr, key->user, true);
+ rc = searchkey_prepare_is(db, key, &s.skey, true);
if (rc)
goto error;
- rc = idx(db, &s.idxval, value->value, true);
+
+ rc = idx(db, &s.value.value, value->value, true);
if (rc)
goto error;
+ s.db = db;
+ s.value.expire = value->expire;
s.now = time(NULL);
db->itf.apply(db->clodb, set_cb, &s);
if (s.db) {
- if (idx(db, &k.permission, s.strperm, true))
- goto error;
- k.client = s.idxcli;
- k.user = s.idxusr;
- k.session = s.idxses;
- v.value = s.idxval;
- v.expire = s.expire;
- rc = db->itf.add(db->clodb, &k, &v);
- if (rc)
- goto error;
+ /* no item to alter so must be added */
+ rc = idx(db, &s.skey.key.permission, key->permission, true);
+ if (!rc)
+ rc = db->itf.add(db->clodb, &s.skey.key, &s.value);
}
- return 0;
error:
return rc;
}
@@ -381,19 +524,17 @@ error:
/******************************************************************************/
/******************************************************************************/
+/* structure for testing rule */
struct test_s
{
- anydb_t *db;
- anydb_idx_t idxcli;
- anydb_idx_t idxses;
- anydb_idx_t idxusr;
- const char *strperm;
- int score;
- anydb_idx_t idxval;
- time_t expire;
+ anydb_t *db; /* targeted database */
time_t now;
+ unsigned score;
+ searchkey_t skey;
+ anydb_value_t value;
};
+/* callback for testing rule */
static
anydb_action_t
test_cb(
@@ -402,35 +543,22 @@ test_cb(
anydb_value_t *value
) {
struct test_s *s = closure;
- int sc;
+ unsigned sc;
+ /* drop expired items */
if (value->expire && value->expire <= s->now)
return Anydb_Action_Remove_And_Continue;
- if ((s->idxcli == key->client || key->client == AnyIdx_Wide)
- && (s->idxses == key->session || key->session == AnyIdx_Wide)
- && (s->idxusr == key->user || key->user == AnyIdx_Wide)
- && (AnyIdx_Wide == key->permission
- || !strcasecmp(s->strperm, string(s->db, key->permission)))) {
- sc = 1;
- if (key->client != AnyIdx_Wide)
- sc += CLIENT_MATCH_SCORE;
- if (key->session != AnyIdx_Wide)
- sc += SESSION_MATCH_SCORE;
- if (key->user != AnyIdx_Wide)
- sc += USER_MATCH_SCORE;
- if (key->permission != AnyIdx_Wide)
- sc += PERMISSION_MATCH_SCORE;
- if (sc > s->score) {
- s->score = sc;
- s->idxval = value->value;
- s->expire = value->expire;
- }
+ sc = searchkey_test(s->db, key, &s->skey);
+ if (sc > s->score) {
+ s->score = sc;
+ s->value = *value;
}
return Anydb_Action_Continue;
}
-int
+/* see anydb.h */
+unsigned
anydb_test(
anydb_t *db,
const data_key_t *key,
@@ -438,34 +566,27 @@ anydb_test(
) {
struct test_s s;
+ searchkey_prepare_test(db, key, &s.skey, true);
+
s.db = db;
s.now = time(NULL);
- s.strperm = key->permission;
- s.expire = value->expire;
-
- s.idxcli = idx_or_none_but_any(db, key->client, true);
- s.idxses = idx_or_none_but_any(db, key->session, true);
- s.idxusr = idx_or_none_but_any(db, key->user, true);
-
- s.expire = -1;
- s.idxval = AnyIdx_Invalid;
s.score = 0;
-
db->itf.apply(db->clodb, test_cb, &s);
-
if (s.score) {
- value->value = string(db, s.idxval);
- value->expire = s.expire;
+ value->value = string(db, s.value.value);
+ value->expire = s.value.expire;
}
return s.score;
}
+
/******************************************************************************/
/******************************************************************************/
/*** IS EMPTY ***/
/******************************************************************************/
/******************************************************************************/
+/* callback for computing if empty */
static
anydb_action_t
is_empty_cb(
@@ -473,18 +594,23 @@ is_empty_cb(
const anydb_key_t *key,
anydb_value_t *value
) {
- bool *result = closure;
- *result = false;
+ time_t *t = closure;
+ if (value->expire && value->expire <= *t)
+ return Anydb_Action_Remove_And_Continue;
+ *t = 0;
return Anydb_Action_Stop;
}
+/* see anydb.h */
bool
anydb_is_empty(
anydb_t *db
) {
- bool result = true;
- db->itf.apply(db->clodb, is_empty_cb, &result);
- return result;
+ time_t t;
+
+ t = time(NULL);
+ db->itf.apply(db->clodb, is_empty_cb, &t);
+ return !t;
}
/******************************************************************************/
@@ -493,6 +619,7 @@ anydb_is_empty(
/******************************************************************************/
/******************************************************************************/
+/* cleanup callback */
static
anydb_action_t
cleanup_cb(
@@ -500,12 +627,11 @@ cleanup_cb(
const anydb_key_t *key,
anydb_value_t *value
) {
- if (value->expire && value->expire <= *(time_t*)closure)
- return Anydb_Action_Remove_And_Continue;
-
- return Anydb_Action_Continue;
+ return value->expire && value->expire <= *(time_t*)closure
+ ? Anydb_Action_Remove_And_Continue : Anydb_Action_Continue;
}
+/* see anydb.h */
void
anydb_cleanup(
anydb_t *db
@@ -519,10 +645,25 @@ anydb_cleanup(
/******************************************************************************/
/******************************************************************************/
+/*** SYNCHRONIZE ***/
+/******************************************************************************/
+/******************************************************************************/
+
+/* see anydb.h */
+int
+anydb_sync(
+ anydb_t *db
+) {
+ return db->itf.sync ? db->itf.sync(db->clodb) : 0;
+}
+
+/******************************************************************************/
+/******************************************************************************/
/*** DESTROY ***/
/******************************************************************************/
/******************************************************************************/
+/* see anydb.h */
void
anydb_destroy(
anydb_t *db