aboutsummaryrefslogtreecommitdiffstats
path: root/src/db.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/db.c')
-rw-r--r--src/db.c803
1 files changed, 41 insertions, 762 deletions
diff --git a/src/db.c b/src/db.c
index 0bd4a09..98c96e7 100644
--- a/src/db.c
+++ b/src/db.c
@@ -27,392 +27,22 @@
#include <errno.h>
#include "data.h"
-#include "fbuf.h"
-#include "db.h"
-#include "rcyn-client.h"
+#include "anydb.h"
+#include "fdb.h"
+#include "memdb.h"
-#define NOEXPIRE 0
-#define NOIDX 0
+static anydb_t *memdb;
-#define ANYIDX 40
-#define ANYSTR "#"
-
-#define WIDEIDX 42
-#define WIDESTR "*"
-
-/*
- * for the first version, save enougth time up to 4149
- * 4149 = 1970 + (4294967296 * 16) / (365 * 24 * 60 * 60)
- *
- * in the next version, time will be relative to a stored base
- */
-#define exp2time(x) (((time_t)(x)) << 4)
-#define time2expl(x) ((uint32_t)((x) >> 4))
-#define time2exph(x) time2expl((x) + 15)
-
-/**
- * A query is a set of 32 bits integers
- */
-struct key_ids {
- /** client string id */
- uint32_t client;
-
- /** user string id */
- uint32_t user;
-
- /** permission string id */
- uint32_t permission;
-};
-typedef struct key_ids key_ids_t;
-
-/**
- * A rule is a set of 32 bits integers
- */
-struct rule
-{
- /** key part */
- key_ids_t key;
-
- /** value string id */
- uint32_t value;
-
- /** expiration */
- uint32_t expire;
-};
-typedef struct rule rule_t;
-
-/*
- * The cynara-agl database is made of 2 memory mapped files:
- * - names: the zero terminated names
- * - rules: the rules based on name indexes as 32bits indexes
- * These files are normally in /var/lib/cynara
- */
-#if !defined(DEFAULT_DB_DIR)
-# define DEFAULT_DB_DIR "/var/lib/cynara"
-#endif
-static const char db_default_directory[] = DEFAULT_DB_DIR;
-
-/** the file for the names */
-static fbuf_t fnames;
-
-/** the file for the rules */
-static fbuf_t frules;
-
-/** identification of names version 1
- * $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:names:1
- * $> uuid -v 5 urn:AGL:cynara:db:names:1
- */
-static const char uuid_names_v1[] = "e9481f9e-b2f4-5716-90cf-c286d98d1868\n--\n";
-
-/** identification of rules version 1
- * $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:rules:1
- * $> uuid -v 5 ns:URL urn:AGL:cynara:db:rules:1
- */
-static const char uuid_rules_v1[] = "8f7a5b21-48b1-57af-96c9-d5d7192be370\n--\n";
-
-/** length of the identification */
-static const int uuidlen = 40;
-
-/** count of names */
-static uint32_t names_count;
-
-/** the name indexes sorted */
-static uint32_t *names_sorted;
-
-/** count of rules */
-static uint32_t rules_count;
-
-/** the rules */
-static rule_t *rules;
-
-/** return the name of 'index' */
-static
-const char*
-name_at(
- uint32_t index
-) {
- return (const char*)(fnames.buffer + index);
-}
-
-/** compare names. used by qsort and bsearch */
-static
-int
-cmpnames(
- const void *pa,
- const void *pb
-) {
- uint32_t a = *(const uint32_t*)pa;
- uint32_t b = *(const uint32_t*)pb;
- return strcmp(name_at(a), name_at(b));
-}
-
-/** search the index of 'name' and create it if 'needed' */
-int
-db_get_name_index(
- uint32_t *index,
- const char *name,
- bool needed
-) {
- uint32_t lo, up, m, i, *p;
- int c;
- const char *n;
- size_t len;
-
- /* special names */
- if (!name || !name[0])
- name = ANYSTR;
-
- /* dichotomic search */
- lo = 0;
- up = names_count;
- while(lo < up) {
- m = (lo + up) >> 1;
- i = names_sorted[m];
- n = name_at(i);
- c = strcmp(n, name);
-
- if (c == 0) {
- /* found */
- *index = i;
- return 0;
- }
-
- /* dichotomic iteration */
- if (c < 0)
- lo = m + 1;
- else
- up = m;
- }
-
- /* not found */
- if (!needed) {
- errno = ENOENT;
- return -1;
- }
-
- /* check length */
- len = strnlen(name, MAX_NAME_LENGTH + 1);
- if (len > MAX_NAME_LENGTH) {
- errno = EINVAL;
- return -1;
- }
-
- /* add the name in the file */
- i = fnames.used;
- c = fbuf_append(&fnames, name, 1 + (uint32_t)len);
- if (c < 0)
- return c;
-
- /* add the name in sorted array */
- up = names_count;
- if (!(up & 1023)) {
- p = realloc(names_sorted, (up + 1024) * sizeof *names_sorted);
- if (p == NULL) {
- fprintf(stderr, "out of memory");
- return -1;
- }
- names_sorted = p;
- }
- memmove(&names_sorted[lo + 1], &names_sorted[lo], (up - lo) * sizeof *names_sorted);
- names_count = up + 1;
- *index = names_sorted[lo] = i;
- return 0;
-}
-
-/** initialize names */
-static
-int
-init_names(
-) {
- int rc;
- uint32_t pos, len, *ns, *p, all, nc;
-
- all = 0;
- nc = 0;
- ns = NULL;
-
- /* iterate over names */
- pos = uuidlen;
- while (pos < fnames.used) {
- /* get name length */
- len = (uint32_t)strlen(name_at(pos));
- if (pos + len <= pos || pos + len > fnames.used) {
- free(ns);
- goto bad_file;
- }
- /* store the position */
- if (all <= nc) {
- all += 1024;
- p = realloc(ns, all * sizeof *ns);
- if (p == NULL) {
- free(ns);
- fprintf(stderr, "out of memory");
- goto error;
- }
- ns = p;
- }
- ns[nc++] = pos;
- /* next */
- pos += len + 1;
- }
-
- /* sort and record */
- qsort(ns, nc, sizeof *ns, cmpnames);
- names_sorted = ns;
- names_count = nc;
-
- /* predefined symbols */
- rc = db_get_name_index(&pos, ANYSTR, true);
- if (rc < 0)
- goto error;
- if (pos != ANYIDX)
- goto bad_file;
- rc = db_get_name_index(&pos, WIDESTR, true);
- if (rc < 0)
- goto error;
- if (pos != WIDEIDX)
- goto bad_file;
-
- return 0;
-bad_file:
- fprintf(stderr, "bad file %s", fnames.name);
- errno = ENOEXEC;
-error:
- return -1;
-}
-
-/** check whether the 'text' fit ANYSTR, NULL or "" */
-static
-bool
-is_any(
- const char *text
-) {
- return text == NULL || text[0] == 0 || 0 == strcmp(text, ANYSTR);
-}
-
-/** check whether the 'text' fit ANYSTR, WIDESTR, NULL or "" */
+/** check whether the 'text' fit String_Any, String_Wide, NULL or "" */
static
bool
is_any_or_wide(
const char *text
) {
- return is_any(text) || 0 == strcmp(text, WIDESTR);
-}
-
-/** set the 'value' to the rule at 'index' */
-static
-void
-touch_at(
- uint32_t index
-) {
- uint32_t pos;
-
- pos = (uint32_t)(((void*)&rules[index]) - frules.buffer);
- if (pos < frules.saved)
- frules.saved = pos;
+ return text == NULL || text[0] == 0
+ || (!text[1] && (text[0] == Data_Any_Char || text[0] == Data_Wide_Char));
}
-/** set the 'value' to the rule at 'index' */
-static
-void
-set_at(
- uint32_t index,
- uint32_t value,
- uint32_t expire
-) {
- assert(index < rules_count);
- rules[index].value = value;
- rules[index].expire = expire;
- touch_at(index);
-}
-
-/** drop the rule at 'index' */
-static
-void
-drop_at(
- uint32_t index
-) {
- uint32_t pos;
-
- assert(index < rules_count);
- if (index < --rules_count)
- rules[index] = rules[rules_count];
- pos = (uint32_t)(((void*)&rules[rules_count]) - frules.buffer);
- frules.used = pos;
- touch_at(index);
-}
-
-/** add the rule 'client' x 'user' x 'permission' x 'value' */
-static
-int
-add_rule(
- uint32_t client,
- uint32_t user,
- uint32_t permission,
- uint32_t value,
- uint32_t expire
-) {
- int rc;
- uint32_t c;
- rule_t *rule;
-
- c = frules.used + (uint32_t)sizeof *rule;
- rc = fbuf_ensure_capacity(&frules, c);
- if (rc)
- return rc;
- rules = (rule_t*)(frules.buffer + uuidlen);
- rule = &rules[rules_count++];
- rule->key.client = client;
- rule->key.user = user;
- rule->key.permission = permission;
- rule->value = value;
- rule->expire = expire;
- frules.used = c;
- return 0;
-}
-
-/** init the rules from the file */
-static
-void
-init_rules(
-) {
- rules = (rule_t*)(frules.buffer + uuidlen);
- rules_count = (frules.used - uuidlen) / sizeof *rules;
-}
-
-/** open a fbuf */
-static
-int
-open_identify(
- fbuf_t *fb,
- const char *directory,
- const char *name,
- const char *id,
- uint32_t idlen
-) {
- int rc;
- char *file, *backup;
- size_t sd, sn;
-
- sd = strlen(directory);
- sn = strlen(name);
- file = malloc(((sd + sn) << 1) + 5);
- if (!file)
- rc = -ENOMEM;
- else {
-
- memcpy(file, directory, sd);
- file[sd] = '/';
- memcpy(&file[sd + 1], name, sn + 1);
- backup = &file[sd + sn + 2];
- memcpy(backup, file, sd + sn + 1);
- backup[sd + sn + 1] = '~';
- backup[sd + sn + 2] = 0;
- rc = fbuf_open_identify(fb, file, backup, id, idlen);
- free(file);
- }
- return rc;
-}
/** open the database for files 'names' and 'rules' (can be NULL) */
int
@@ -421,115 +51,49 @@ db_open(
) {
int rc;
- /* provide default directory */
- if (directory == NULL)
- directory = db_default_directory;
-
- /* open the names */
- rc = open_identify(&fnames, directory, "cynara.names", uuid_names_v1, uuidlen);
- if (rc < 0)
- goto error;
-
- /* open the rules */
- rc = open_identify(&frules, directory, "cynara.rules", uuid_rules_v1, uuidlen);
- if (rc < 0)
- goto error;
-
- /* connect internals */
- rc = init_names();
- if (rc < 0)
- goto error;
-
- init_rules();
- return 0;
-error:
- return -1;
+ rc = memdb_create(&memdb);
+ if (!rc) {
+ rc = fdb_open(directory);
+ if (rc)
+ anydb_destroy(memdb);
+ }
+ return rc;
}
/** close the database */
void
db_close(
) {
- assert(fnames.name && frules.name);
- fbuf_close(&fnames);
- fbuf_close(&frules);
+ fdb_close();
+ anydb_destroy(memdb);
}
/** is the database empty */
bool
db_is_empty(
) {
- return !rules_count;
+ return fdb_is_empty();
}
/** synchronize db on files */
int
db_sync(
) {
- int rc;
-
- assert(fnames.name && frules.name);
- rc = fbuf_sync(&fnames);
- if (rc == 0)
- rc = fbuf_sync(&frules);
- return rc;
+ return fdb_sync();
}
/** make a backup of the database */
int
db_backup(
) {
- int rc;
-
- assert(fnames.name && frules.name);
- rc = fbuf_backup(&fnames);
- if (rc == 0)
- rc = fbuf_backup(&frules);
- return rc;
+ return fdb_backup();
}
/** recover the database from latest backup */
int
db_recover(
) {
- int rc;
-
- assert(fnames.name && frules.name);
-
- rc = fbuf_recover(&fnames);
- if (rc < 0)
- goto error;
-
- rc = fbuf_recover(&frules);
- if (rc < 0)
- goto error;
-
- rc = init_names();
- if (rc < 0)
- goto error;
-
- init_rules();
- return 0;
-error:
- fprintf(stderr, "db recovery impossible: %m");
- exit(5);
- return rc;
-}
-
-static int get_query_ids(
- const data_key_t *in,
- key_ids_t *out,
- bool create
-) {
- int rc;
-
- rc = db_get_name_index(&out->client, in->client, create);
- if (rc) goto end;
- rc = db_get_name_index(&out->user, in->user, create);
- if (rc) goto end;
- rc = db_get_name_index(&out->permission, in->permission, create);
-end:
- return rc;
+ return fdb_recover();
}
/** enumerate */
@@ -542,30 +106,8 @@ db_for_all(
const data_value_t *value),
const data_key_t *key
) {
- uint32_t ucli, uusr, i;
- int anyperm;
- data_key_t k;
- data_value_t v;
-
- if (!is_any_or_wide(key->session)
- || db_get_name_index(&ucli, key->client, false)
- || db_get_name_index(&uusr, key->user, false))
- return; /* nothing to do! */
-
- anyperm = is_any(key->permission);
- for (i = 0; i < rules_count; i++) {
- if ((ucli == ANYIDX || ucli == rules[i].key.client)
- && (uusr == ANYIDX || uusr == rules[i].key.user)
- && (anyperm || !strcasecmp(key->permission, name_at(rules[i].key.permission)))) {
- k.client = name_at(rules[i].key.client);
- k.session = WIDESTR;
- k.user = name_at(rules[i].key.user);
- k.permission = name_at(rules[i].key.permission);
- v.value = name_at(rules[i].value);
- v.expire = exp2time(rules[i].expire);
- callback(closure, &k, &v);
- }
- }
+ fdb_for_all(closure, callback, key);
+ anydb_for_all(memdb, closure, callback, key);
}
/** drop rules */
@@ -573,24 +115,8 @@ int
db_drop(
const data_key_t *key
) {
- uint32_t ucli, uusr, i;
- bool anyperm;
-
- if (!is_any_or_wide(key->session)
- || db_get_name_index(&ucli, key->client, false)
- || db_get_name_index(&uusr, key->user, false))
- return 0; /* nothing to do! */
-
- anyperm = is_any(key->permission);
- i = 0;
- while (i < rules_count) {
- if ((ucli == ANYIDX || ucli == rules[i].key.client)
- && (uusr == ANYIDX || uusr == rules[i].key.user)
- && (anyperm || !strcasecmp(key->permission, name_at(rules[i].key.permission))))
- drop_at(i);
- else
- i++;
- }
+ fdb_drop(key);
+ anydb_drop(memdb, key);
return 0;
}
@@ -600,52 +126,10 @@ db_set(
const data_key_t *key,
const data_value_t *value
) {
- int rc;
- uint32_t ucli, uusr, uperm, uval, i;
- const char *perm;
-
- /* check the session */
- if (!is_any_or_wide(key->session)) {
- errno = EINVAL;
- rc = -1;
- goto error;
- }
-
- /* normalise the perm */
- perm = is_any_or_wide(key->permission) ? WIDESTR : key->permission;
-
- /* get/create strings */
- rc = db_get_name_index(&ucli, is_any_or_wide(key->client) ? WIDESTR : key->client, true);
- if (rc)
- goto error;
- rc = db_get_name_index(&uusr, is_any_or_wide(key->user) ? WIDESTR : key->user, true);
- if (rc)
- goto error;
- rc = db_get_name_index(&uval, value->value, true);
- if (rc)
- goto error;
-
- /* search the existing rule */
- for (i = 0; i < rules_count; i++) {
- if (ucli == rules[i].key.client
- && uusr == rules[i].key.user
- && !strcasecmp(perm, name_at(rules[i].key.permission))) {
- /* found */
- set_at(i, uval, time2exph(value->expire));
- return 0;
- }
- }
-
- /* create the rule */
- rc = db_get_name_index(&uperm, perm, true);
- if (rc)
- goto error;
-
- rc = add_rule(ucli, uusr, uperm, uval, time2exph(value->expire));
-
- return 0;
-error:
- return rc;
+ if (is_any_or_wide(key->session))
+ return fdb_set(key, value);
+ else
+ return anydb_set(memdb, key, value);
}
/** check rules */
@@ -654,230 +138,25 @@ db_test(
const data_key_t *key,
data_value_t *value
) {
- const char *perm;
- uint32_t ucli, uusr, i, score, sc, now;
- rule_t *rule, *found;
-
- /* normalize the items */
- if (db_get_name_index(&ucli, is_any_or_wide(key->client) ? WIDESTR : key->client, false))
- ucli = NOIDX;
- if (db_get_name_index(&uusr, is_any_or_wide(key->user) ? WIDESTR : key->user, false))
- uusr = NOIDX;
- perm = is_any_or_wide(key->permission) ? WIDESTR : key->permission;
-
- /* search the existing rule */
- now = time2expl(time(NULL));
- found = NULL;
- score = 0;
- for (i = 0 ; i < rules_count ; i++) {
- rule = &rules[i];
- if ((!rule->expire || rule->expire >= now)
- && (ucli == rule->key.client || WIDEIDX == rule->key.client)
- && (uusr == rule->key.user || WIDEIDX == rule->key.user)
- && (WIDEIDX == rule->key.permission
- || !strcasecmp(perm, name_at(rule->key.permission)))) {
- /* found */
- sc = 1 + (rule->key.client != WIDEIDX)
- + (rule->key.user != WIDEIDX)
- + (rule->key.permission != WIDEIDX);
- if (sc > score) {
- score = sc;
- found = rule;
- }
- }
+ int s1, s2;
+ data_value_t v1, v2;
+
+ s1 = anydb_test(memdb, key, &v1);
+ s2 = fdb_test(key, &v2);
+ if (s2 > s1) {
+ *value = v2;
+ return s2;
+ } else {
+ *value = v1;
+ return s1;
}
- if (!found) {
- value->value = NULL;
- value->expire = 0;
- return 0;
- }
-
- value->value = name_at(found->value);
- value->expire = exp2time(found->expire);
-
- return 1;
-}
-
-typedef struct gc gc_t;
-struct gc
-{
- uint32_t *befores;
- uint32_t *afters;
-};
-
-/** compare indexes. used by qsort and bsearch */
-static
-int
-cmpidxs(
- const void *pa,
- const void *pb
-) {
- uint32_t a = *(const uint32_t*)pa;
- uint32_t b = *(const uint32_t*)pb;
- return a < b ? -1 : a != b;
-}
-
-static
-uint32_t*
-gc_after_ptr(
- gc_t *gc,
- uint32_t *idx
-) {
- uint32_t *p = bsearch(idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs);
- assert(p != NULL);
- return &gc->afters[p - gc->befores];
-}
-
-static
-void
-gc_mark(
- gc_t *gc,
- uint32_t *idx
-) {
- *gc_after_ptr(gc, idx) = 1;
-}
-
-static
-void
-gc_mark_id(
- gc_t *gc,
- uint32_t idx
-) {
- gc_mark(gc, &idx);
-}
-
-static
-int
-gc_after(
- gc_t *gc,
- uint32_t *idx
-) {
- uint32_t idbef, idaft;
-
- idbef = *idx;
- idaft = *gc_after_ptr(gc, idx);
- *idx = idaft;
- return (int)(idbef - idaft);
-}
-
-static
-int
-gc_init(
- gc_t *gc
-) {
- gc->befores = malloc((sizeof *gc->befores + sizeof *gc->afters) * names_count);
- if (gc->befores == NULL)
- return -ENOMEM;
-
- names_count = names_count;
- memcpy(gc->befores, names_sorted, names_count * sizeof *gc->befores);
- qsort(gc->befores, names_count, sizeof *gc->befores, cmpidxs);
- gc->afters = &gc->befores[names_count];
- memset(gc->afters, 0, names_count * sizeof *gc->afters);
-
- gc_mark_id(gc, ANYIDX);
- gc_mark_id(gc, WIDEIDX);
- return 0;
-}
-
-static
-void
-gc_end(
- gc_t *gc
-) {
- free(gc->befores);
-}
-
-static
-int
-gc_pack(
- gc_t *gc
-) {
- uint32_t i, j, n, next, prev;
- char *strings;
-
- /* skip the unchanged initial part */
- n = names_count;
- i = 0;
- while (i < n && gc->afters[i])
- i++;
-
- /* at end means no change */
- if (i == n)
- return 0;
-
- /* pack the strings */
- strings = fnames.buffer;
- j = i;
- memcpy(gc->afters, gc->befores, j * sizeof *gc->afters);
- next = gc->befores[i++];
- fnames.saved = next;
- while (i < n) {
- if (gc->afters[i]) {
- gc->befores[j] = prev = gc->befores[i];
- gc->afters[j++] = next;
- while ((strings[next++] = strings[prev++]));
- }
- i++;
- }
- fnames.used = next;
- names_count = j;
- memcpy(names_sorted, gc->afters, j * sizeof *gc->afters);
- qsort(names_sorted, j, sizeof *names_sorted, cmpnames);
-
- return 1;
}
int
db_cleanup(
) {
- int rc, chg;
- uint32_t i, now;
- gc_t gc;
- rule_t *rule;
-
- /* init garbage collector */
- rc= gc_init(&gc);
- if (rc < 0)
- return rc;
-
- /* default now */
- now = time2expl(time(NULL));
-
- /* remove expired entries and mark string ids of remaining ones */
- i = 0;
- while (i < rules_count) {
- rule = &rules[i];
- if (rule->expire && now >= rule->expire)
- drop_at(i);
- else {
- gc_mark(&gc, &rule->key.client);
- gc_mark(&gc, &rule->key.user);
- gc_mark(&gc, &rule->key.permission);
- gc_mark(&gc, &rule->value);
- i++;
- }
- }
-
- /* pack the strings */
- if (gc_pack(&gc)) {
- /* replace the ids if changed */
- i = 0;
- while (i < rules_count) {
- rule = &rules[i];
- chg = gc_after(&gc, &rule->key.client);
- chg |= gc_after(&gc, &rule->key.user);
- chg |= gc_after(&gc, &rule->key.permission);
- chg |= gc_after(&gc, &rule->value);
- if (chg)
- touch_at(i);
- i++;
- }
- }
-
- /* terminate */
- gc_end(&gc);
-
+ fdb_cleanup();
+ anydb_cleanup(memdb);
return 0;
}