Add session, value as string and expiration
authorJose Bollo <jose.bollo@iot.bzh>
Wed, 19 Sep 2018 12:52:03 +0000 (14:52 +0200)
committerJose Bollo <jose.bollo@iot.bzh>
Thu, 20 Sep 2018 21:39:57 +0000 (23:39 +0200)
The DB now records sessions. It has expiration
management (currently coarse: ~16s, see db.c)
that is propagated to caches.

Values are now strings. Default values are
"yes" and "no" for allowed or denied permissions.

new program: cynadm

Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
20 files changed:
cynara.initial
src/.gitignore [deleted file]
src/CMakeLists.txt
src/Makefile [deleted file]
src/cache.c
src/cache.h
src/cyn.c
src/cyn.h
src/db.c
src/db.h
src/lib-compat.c
src/main-cynadm.c [new file with mode: 0644]
src/main-cynarad.c
src/main-test-old-cynara.c [moved from src/test-lib-compat.c with 100% similarity]
src/queue.c
src/queue.h
src/rcyn-client.c
src/rcyn-client.h
src/rcyn-protocol.txt
src/rcyn-server.c

index 96054d3..aa58c4b 100644 (file)
@@ -1,4 +1,4 @@
 # initial database for cynara
-System * * * 1
-User * * * 1
+System * * * yes always
+User * * * yes always
 
diff --git a/src/.gitignore b/src/.gitignore
deleted file mode 100644 (file)
index d551f05..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-cynarad
-test-cynara
-cynara.names
-cynara.rules
-.*.sw*
index 4363234..549d401 100644 (file)
@@ -72,11 +72,19 @@ INSTALL(TARGETS cynara LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR})
 INSTALL(FILES rcyn-client.h DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/cynara)
 
 ###########################################
-# build and install test-cynara
+# build and install cynadm
 ###########################################
-ADD_EXECUTABLE(test-cynara test-lib-compat.c)
-TARGET_LINK_LIBRARIES(test-cynara cynara)
-INSTALL(TARGETS test-cynara
+ADD_EXECUTABLE(cynadm main-cynadm.c)
+TARGET_LINK_LIBRARIES(cynadm cynara)
+INSTALL(TARGETS cynadm
+        RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
+
+###########################################
+# build and install test-old-cynara
+###########################################
+ADD_EXECUTABLE(test-old-cynara main-test-old-cynara.c)
+TARGET_LINK_LIBRARIES(test-old-cynara cynara)
+INSTALL(TARGETS test-old-cynara
         RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
 
 ###########################################
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644 (file)
index f5969c3..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-.PHONY: all clean
-
-srcssrv = db.c fbuf.c queue.c cyn.c prot.c rcyn-protocol.c socket.c
-
-srcscli = prot.c rcyn-client.c rcyn-protocol.c lib-compat.c cache.c socket.c
-
-incssrv = db.h fbuf.h cyn.h prot.h rcyn-protocol.h socket.h
-
-incscli = prot.h rcyn-client.h rcyn-protocol.h cache.h socket.h
-
-defs = -DRCYN_DEFAULT_CHECK_SOCKET_SPEC=\"tcp:localhost:5555\" \
-       -DRCYN_DEFAULT_ADMIN_SOCKET_SPEC=\"tcp:localhost:4444\"
-
-bins = cynarad test-cynara
-
-all: $(bins)
-
-clean:
-       rm cynara.names cynara.rules $(bins) 2>/dev/null || true
-
-cynarad: rcyn-server.c $(srcssrv) $(incssrv)
-       gcc -o cynarad -g -Wall rcyn-server.c $(srcssrv) $(defs)
-
-test-cynara: test-lib-compat.c  $(srcscli) $(incscli)
-       gcc -o test-cynara -I../include -g -Wall test-lib-compat.c $(srcscli) $(defs)
-
index 8d15728..9bbe667 100644 (file)
 
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdalign.h>
 #include <string.h>
 #include <errno.h>
+#include <time.h>
 #include <ctype.h>
 
 #include "cache.h"
 
 /**
- * The cache structure is a blob of memory ('content')
- * of 'count' bytes of only 'used' bytes.
- * That blob containts at sequence of records of variable length
- * Each records holds the following values in that given order:
- *  - length: 2 bytes unsigned integer LSB first, MSB second
- *  - hit count: 1 byte unsigned integer
- *  - value: 1 byte unsigned integer
+ * A cache item header
+ *
+ * Each item is followed by values in that given order:
  *  - client: zero  terminated string
  *  - session: zero  terminated string
  *  - user: zero  terminated string
  *  - permission: zero  terminated string
  *  - 
  */
+struct item
+{
+       /** expiration */
+       time_t expire;
+
+       /** length of the cache entry including this header */
+       uint16_t length;
+
+       /** hit indicator */
+       uint8_t hit;
+
+       /** value to store */
+       int8_t value;
+
+       /** fake ending character */
+       char strings;
+};
+typedef struct item item_t;
+
+/**
+ * The cache structure is a blob of memory ('content')
+ * of 'count' bytes of only 'used' bytes.
+ * That blob containts at sequence of records of variable length
+ */
 struct cache
 {
        uint32_t used;
@@ -46,12 +68,13 @@ struct cache
 };
 
 static
-uint32_t
-lenat(
+inline
+item_t *
+itemat(
        cache_t *cache,
        uint32_t pos
 ) {
-       return ((uint32_t)cache->content[pos]) |  (((uint32_t)cache->content[pos + 1]) << 8);
+       return (item_t*)(&cache->content[pos]);
 }
 
 static
@@ -62,7 +85,7 @@ drop_at(
 ) {
        uint32_t e, l;
 
-       l = lenat(cache, pos);
+       l = itemat(cache, pos)->length;
        e = pos + l;
        cache->used -= l;
        if (cache->used > e)
@@ -75,13 +98,15 @@ drop_lre(
        cache_t *cache
 ) {
        uint32_t found = 0, iter = 0;
-       uint8_t hmin = 255, hint;
+       uint8_t hmin = 255, hit;
+       item_t *item;
 
        while (iter < cache->used) {
-               hint = cache->content[iter + 2];
-               if (hint < hmin)
+               item = itemat(cache, iter);
+               hit = item->hit;
+               if (hit < hmin)
                        found = iter;
-               iter += lenat(cache, iter);
+               iter += item->length;
        }
        if (found < cache->used)
                drop_at(cache, found);
@@ -91,21 +116,23 @@ static
 void
 hit(
        cache_t *cache,
-       uint32_t pos
+       item_t *target
 ) {
        uint32_t iter = 0;
-       uint8_t hint;
+       uint8_t hit;
+       item_t *item;
 
        while (iter < cache->used) {
-               if (iter == pos)
-                       hint = 255;
+               item = itemat(cache, iter);
+               if (item == target)
+                       hit = 255;
                else {
-                       hint = cache->content[iter + 2];
-                       if (hint)
-                               hint--;
+                       hit = item->hit;
+                       if (hit)
+                               hit--;
                }
-               cache->content[iter + 2] = hint;
-               iter += lenat(cache, iter);
+               item->hit = hit;
+               iter += item->length;
        }
 }
 
@@ -135,7 +162,6 @@ cmp(
        return 0;
 }
 
-
 static
 int
 match(
@@ -156,7 +182,7 @@ match(
 }
 
 static
-uint32_t
+item_t*
 search(
        cache_t *cache,
        const char *client,
@@ -164,16 +190,24 @@ search(
        const char *user,
        const char *permission
 ) {
-       char *txt;
-       uint32_t iter = 0;
+       time_t now;
+       item_t *item, *found;
+       uint32_t iter;
 
+       found = NULL;
+       now = time(NULL);
+       iter = 0;
        while (iter < cache->used) {
-               txt = (char*)&cache->content[iter + 4];
-               if (match(txt, client, session, user, permission))
-                       return iter;
-               iter += lenat(cache, iter);
+               item = itemat(cache, iter);
+               if (item->expire && item->expire < now)
+                       drop_at(cache, iter);
+               else {
+                       if (match(&item->strings, client, session, user, permission))
+                               found = item;
+                       iter += item->length;
+               }
        }
-       return iter;
+       return found;
 }
 
 int
@@ -183,37 +217,40 @@ cache_put(
        const char *session,
        const char *user,
        const char *permission,
-       int value
+       int value,
+       time_t expire
 ) {
-       uint32_t pos;
-       size_t size, scli, sses, susr, sper;
+       uint16_t length;
+       item_t *item;
+       size_t size;
 
-       if (cache == NULL || value < 0 || value > 255)
+       if (cache == NULL || value < -128 || value > 127)
                return -EINVAL;
 
-       pos = search(cache, client, session, user, permission);
-       if (pos < cache->used)
-               cache->content[pos + 3] = (uint8_t)value;
-       else {
-               scli = strlen(client);
-               sses = strlen(session);
-               susr = strlen(user);
-               sper = strlen(permission);
-               size = scli + sses + susr + sper + 8;
+       item = search(cache, client, session, user, permission);
+       if (item == NULL) {
+               /* create an item */
+               size = (size_t)(&((item_t*)0)->strings)
+                       + strlen(client)
+                       + strlen(session)
+                       + strlen(user)
+                       + strlen(permission);
+               size = (size + alignof(item_t) - 1) & ~(alignof(item_t) - 1);
                if (size > 65535)
                        return -EINVAL;
-               if (size > cache->count)
+               length = (uint16_t)size;
+               if (length > cache->count)
                        return -ENOMEM;
-               while(cache->used + (uint32_t)size > cache->count)
+               while(cache->used + length > cache->count)
                        drop_lre(cache);
-               pos = cache->used;
-               cache->content[pos + 0] = (uint8_t)(size & 255);
-               cache->content[pos + 1] = (uint8_t)((size >> 8) & 255);
-               cache->content[pos + 2] = (uint8_t)255;
-               cache->content[pos + 3] = (uint8_t)value;
-               stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)&cache->content[pos + 4], client), session), user), permission);
+               item = itemat(cache, cache->used);
+               item->length = length;
+               stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy(&item->strings, client), session), user), permission);
                cache->used += (uint32_t)size;
        }
+       item->expire = expire;
+       item->hit = 255;
+       item->value = (int8_t)value;
        return 0;
 }
 
@@ -225,12 +262,12 @@ cache_search(
        const char *user,
        const char *permission
 ) {
-       uint32_t pos;
+       item_t *item;
 
-       pos = search(cache, client, session, user, permission);
-       if (pos < cache->used) {
-               hit(cache, pos);
-               return (int)cache->content[pos + 3];
+       item = search(cache, client, session, user, permission);
+       if (item) {
+               hit(cache, item);
+               return (int)item->value;
        }
        return -ENOENT;
 }
index 5cd827b..bd56601 100644 (file)
@@ -38,7 +38,8 @@ cache_put(
        const char *session,
        const char *user,
        const char *permission,
-       int value
+       int value,
+       time_t expire
 );
 
 extern
index 85deb4e..914bf52 100644 (file)
--- a/src/cyn.c
+++ b/src/cyn.c
@@ -88,6 +88,7 @@ changed(
        int rc;
        struct callback *c;
 
+       db_cleanup(0);
        rc = db_sync();
        for (c = observers; c ; c = c->next)
                c->callback(c->closure);
@@ -197,11 +198,12 @@ cyn_set(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 ) {
        if (!lock)
                return -EPERM;
-       return queue_set(client, session, user, permission, value);
+       return queue_set(client, session, user, permission, value, expire);
 }
 
 int
@@ -225,7 +227,8 @@ cyn_list(
                const char *session,
                const char *user,
                const char *permission,
-               uint32_t value),
+               const char *value,
+               time_t expire),
        const char *client,
        const char *session,
        const char *user,
@@ -240,11 +243,12 @@ cyn_test(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t *value
+       const char **value,
+       time_t *expire
 ) {
        int rc;
 
-       rc = db_test(client, session, user, permission, value);
+       rc = db_test(client, session, user, permission, value, expire);
        if (rc <= 0)
                *value = DEFAULT;
        else
@@ -254,24 +258,25 @@ cyn_test(
 
 int
 cyn_check_async(
-       void (*check_cb)(void *closure, uint32_t value),
+       void (*check_cb)(void *closure, const char *value, time_t expire),
        void *closure,
        const char *client,
        const char *session,
        const char *user,
        const char *permission
 ) {
-       uint32_t value;
+       const char *value;
+       time_t expire;
 
-       cyn_test(client, session, user, permission, &value);
-       if (value == ALLOW || value == DENY) {
-               check_cb(closure, value);
+       cyn_test(client, session, user, permission, &value, &expire);
+       if (!strcmp(value, ALLOW) || !strcmp(value, DENY)) {
+               check_cb(closure, value, expire);
                return 0;
        }
 
        /* TODO: try to resolve AGENT?? */
 
-       check_cb(closure, value);
+       check_cb(closure, value, expire);
        return 0;
 }
 
index df08a5a..379deef 100644 (file)
--- a/src/cyn.h
+++ b/src/cyn.h
@@ -18,9 +18,9 @@
 
 #pragma once
 
-#define DENY    0
-#define ALLOW   1
-#define ASK     2
+#define DENY    "no"
+#define ALLOW   "yes"
+#define ASK     "ask"
 #define DEFAULT DENY
 
 /** enter critical recoverable section */
@@ -45,7 +45,8 @@ cyn_set(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 );
 
 extern
@@ -64,7 +65,8 @@ cyn_test(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t *value
+       const char **value,
+       time_t *expire
 );
 
 extern
@@ -77,7 +79,8 @@ cyn_list(
                const char *session,
                const char *user,
                const char *permission,
-               uint32_t value),
+               const char *value,
+               time_t expire),
        const char *client,
        const char *session,
        const char *user,
@@ -87,7 +90,7 @@ cyn_list(
 extern
 int
 cyn_check_async(
-       void (*check_cb)(void *closure, uint32_t value),
+       void (*check_cb)(void *closure, const char *value, time_t expire),
        void *closure,
        const char *client,
        const char *session,
index f5f46da..f77b76f 100644 (file)
--- a/src/db.c
+++ b/src/db.c
 #include <stdalign.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 #include <errno.h>
 
 #include "fbuf.h"
 #include "db.h"
 
+#define NOEXPIRE 0
 #define NOIDX   0
 
 #define ANYIDX  40
 #define WIDEIDX 42
 #define WIDESTR "*"
 
-/**
- * A rule is a set of 4 integers
+/*
+ * 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
  */
-struct rule
-{
-       uint32_t client, user, permission, value;
-};
-typedef struct rule rule_t;
+#define exp2time(x)  (((time_t)(x)) << 4)
+#define time2expl(x) ((uint32_t)((x) >> 4))
+#define time2exph(x) time2expl((x) + 15)
 
 /**
- * Sessions
+ * A rule is a set of 32 bits integers
  */
-struct session
+struct rule
 {
-       struct session *next, *prev;
-       rule_t *rules;
-       const char *name;
-       uint32_t count;
+       union {
+               uint32_t ids[5];
+               struct {
+                       /** client string id */
+                       uint32_t client;
+
+                       /** session string id */
+                       uint32_t session;
+
+                       /** user string id */
+                       uint32_t user;
+
+                       /** permission string id */
+                       uint32_t permission;
+
+                       /** value string id */
+                       uint32_t value;
+               };
+       };
+
+       /**  expiration */
+       uint32_t expire;
 };
-typedef struct session session_t;
+typedef struct rule rule_t;
 
 /*
  * The cynara-agl database is made of 2 memory mapped files:
@@ -92,12 +113,11 @@ static uint32_t names_count;
 /** the name indexes sorted */
 static uint32_t *names_sorted;
 
-/** the sessions */
-static session_t sessions = {
-       .next = &sessions,
-       .prev = &sessions,
-       .name = WIDESTR
-};
+/** count of rules */
+static uint32_t rules_count;
+
+/** the rules */
+static rule_t *rules;
 
 /** return the name of 'index' */
 static
@@ -273,134 +293,77 @@ is_any_or_wide(
        return is_any(text) || 0 == strcmp(text, WIDESTR);
 }
 
-/** get in 'session' the session for 'name' and create it if 'needed' */
+/** set the 'value' to the rule at 'index' */
 static
-int
-get_session(
-       const char *name,
-       bool needed,
-       session_t **session
+void
+touch_at(
+       uint32_t index
 ) {
-       session_t *s;
-       size_t len;
-
-       /* start on ANY sessions */
-       s = &sessions;
-       if (is_any_or_wide(name))
-               goto found;
-
-       /* look to other sessions */
-       s = s->next;
-       while(s != &sessions) {
-               if (!strcmp(s->name, name))
-                       goto found;
-               s = s->next;
-       }
-
-       /* 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;
-       }
+       uint32_t pos;
 
-       /* create it */
-       s = malloc(sizeof * s + len + 1);
-       if (s == NULL)
-               return -1; /* out of memory */
-
-       /* init new session */
-       s->rules = NULL;
-       s->count = 0;
-       s->name = strcpy((char*)(s + 1), name);
-       s->next = &sessions;
-       s->prev = sessions.prev;
-       sessions.prev = s;
-       s->prev->next = s;
-found:
-       *session = s;
-       return 0;
+       pos = (uint32_t)(((void*)&rules[index]) - frules.buffer);
+       if (pos < frules.saved)
+               frules.saved = pos;
 }
 
-/** for 'session' set the value the rule at 'index' */
+/** set the 'value' to the rule at 'index' */
 static
 void
-session_set_at(
-       session_t *session,
+set_at(
        uint32_t index,
-       uint32_t value
+       uint32_t value,
+       uint32_t expire
 ) {
-       uint32_t pos;
-
-       assert(index < session->count);
-       session->rules[index].value = value;
-       if (session == &sessions) {
-               pos = (uint32_t)(((void*)&session->rules[index]) - frules.buffer);
-               if (pos < frules.saved)
-                       frules.saved = pos;
-       }
+       assert(index < rules_count);
+       rules[index].value = value;
+       rules[index].expire = expire;
+       touch_at(index);
 }
 
-/** drop of 'session' the rule at 'index' */
+/** drop the rule at 'index' */
 static
 void
-session_drop_at(
-       session_t *session,
+drop_at(
        uint32_t index
 ) {
        uint32_t pos;
 
-       assert(index < session->count);
-       if (index < --session->count)
-               session->rules[index] = session->rules[session->count];
-       if (session == &sessions) {
-               pos = (uint32_t)(((void*)&session->rules[index]) - frules.buffer);
-               if (pos < frules.saved)
-                       frules.saved = pos;
-               pos = (uint32_t)(((void*)&session->rules[session->count]) - frules.buffer);
-               frules.used = 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 to 'session' the rule 'client' x 'user' x 'permission' x 'value' */
+/** add the rule 'client' x 'session' x 'user' x 'permission' x 'value' */
 static
 int
-session_add(
-       session_t *session,
+add_rule(
        uint32_t client,
+       uint32_t session,
        uint32_t user,
        uint32_t permission,
-       uint32_t value
+       uint32_t value,
+       uint32_t expire
 ) {
        int rc;
        uint32_t c;
        rule_t *rule;
 
-       if (session == &sessions) {
-               c = frules.used + (uint32_t)sizeof *rule;
-               rc = fbuf_ensure_capacity(&frules, c);
-               if (rc)
-                       return rc;
-               frules.used = c;
-               session->rules = (rule_t*)(frules.buffer + uuidlen);
-       } else {
-               c = session->count + 32 - (session->count & 31);
-               rule = realloc(session->rules, c * sizeof *rule);
-               if (rule == NULL)
-                       return -ENOMEM;
-               session->rules = rule;
-       }
-       rule = &session->rules[session->count++];
+       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->client = client;
+       rule->session = session;
        rule->user = user;
        rule->permission = permission;
        rule->value = value;
+       rule->expire = expire;
+       frules.used = c;
        return 0;
 }
 
@@ -409,8 +372,8 @@ static
 void
 init_rules(
 ) {
-       sessions.rules = (rule_t*)(frules.buffer + uuidlen);
-       sessions.count = (frules.used - uuidlen) / sizeof *sessions.rules;
+       rules = (rule_t*)(frules.buffer + uuidlen);
+       rules_count = (frules.used - uuidlen) / sizeof *rules;
 }
 
 /** open a fbuf */
@@ -487,7 +450,7 @@ db_close(
 bool
 db_is_empty(
 ) {
-       return !sessions.count;
+       return !rules_count;
 }
 
 /** synchronize db on files */
@@ -554,46 +517,35 @@ db_for_all(
                const char *session,
                const char *user,
                const char *permission,
-               uint32_t value),
+               const char *value,
+               time_t expire),
        const char *client,
        const char *session,
        const char *user,
        const char *permission
 ) {
-       uint32_t ucli, uusr, i;
-       bool anyperm, anysession;
-       session_t *ses;
+       uint32_t ucli, uusr, uses, i;
+       bool anyperm;
 
        if (db_get_name_index(&ucli, client, false)
+        || db_get_name_index(&uses, session, false)
         || db_get_name_index(&uusr, user, false))
                return; /* nothing to do! */
 
        anyperm = is_any(permission);
-       anysession = is_any(session);
-       if (anysession)
-               ses = &sessions;
-       else {
-               if (get_session(session, false, &ses))
-                       return; /* ignore if no session */
-       }
-       for(;;) {
-               for (i = 0; i < ses->count; i++) {
-                       if ((ucli == ANYIDX || ucli == ses->rules[i].client)
-                        && (uusr == ANYIDX || uusr == ses->rules[i].user)
-                        && (anyperm || !strcasecmp(permission, name_at(ses->rules[i].permission)))) {
-                               callback(closure,
-                                       name_at(ses->rules[i].client),
-                                       ses->name,
-                                       name_at(ses->rules[i].user),
-                                       name_at(ses->rules[i].permission),
-                                       ses->rules[i].value);
-                       }
+       for (i = 0; i < rules_count; i++) {
+               if ((ucli == ANYIDX || ucli == rules[i].client)
+                && (uses == ANYIDX || uses == rules[i].session)
+                && (uusr == ANYIDX || uusr == rules[i].user)
+                && (anyperm || !strcasecmp(permission, name_at(rules[i].permission)))) {
+                       callback(closure,
+                               name_at(rules[i].client),
+                               name_at(rules[i].session),
+                               name_at(rules[i].user),
+                               name_at(rules[i].permission),
+                               name_at(rules[i].value),
+                               exp2time(rules[i].expire));
                }
-               if (!anysession)
-                       break;
-               ses = ses->next;
-               if (ses == &sessions)
-                       break;
        }
 }
 
@@ -605,37 +557,24 @@ db_drop(
        const char *user,
        const char *permission
 ) {
-       uint32_t ucli, uusr, i;
-       bool anyperm, anysession;
-       session_t *ses;
+       uint32_t ucli, uses, uusr, i;
+       bool anyperm;
 
        if (db_get_name_index(&ucli, client, false)
+        || db_get_name_index(&uses, session, false)
         || db_get_name_index(&uusr, user, false))
                return 0; /* nothing to do! */
 
        anyperm = is_any(permission);
-       anysession = is_any(session);
-       if (anysession)
-               ses = &sessions;
-       else {
-               if (get_session(session, false, &ses))
-                       return 0; /* ignore if no session */
-       }
-       for(;;) {
-               i = 0;
-               while (i < ses->count) {
-                       if ((ucli == ANYIDX || ucli == ses->rules[i].client)
-                        && (uusr == ANYIDX || uusr == ses->rules[i].user)
-                        && (anyperm || !strcasecmp(permission, name_at(ses->rules[i].permission))))
-                               session_drop_at(ses, i);
-                       else
-                               i++;
-               }
-               if (!anysession)
-                       break;
-               ses = ses->next;
-               if (ses == &sessions)
-                       break;
+       i = 0;
+       while (i < rules_count) {
+               if ((ucli == ANYIDX || ucli == rules[i].client)
+                && (uses == ANYIDX || uses == rules[i].session)
+                && (uusr == ANYIDX || uusr == rules[i].user)
+                && (anyperm || !strcasecmp(permission, name_at(rules[i].permission))))
+                       drop_at(i);
+               else
+                       i++;
        }
        return 0;
 }
@@ -647,11 +586,11 @@ db_set(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 ) {
        int rc;
-       uint32_t ucli, uusr, uperm, i;
-       session_t *ses;
+       uint32_t ucli, uses, uusr, uperm, uval, i;
 
        /* normalize */
        client = is_any_or_wide(client) ? WIDESTR : client;
@@ -659,26 +598,28 @@ db_set(
        user = is_any_or_wide(user) ? WIDESTR : user;
        permission = is_any_or_wide(permission) ? WIDESTR : permission;
 
-       /* get the session */
-       rc = get_session(session, true, &ses);
-       if (rc)
-               goto error;
-
        /* get/create strings */
        rc = db_get_name_index(&ucli, client, true);
+       if (rc)
+               goto error;
+       rc = db_get_name_index(&uses, session, true);
        if (rc)
                goto error;
        rc = db_get_name_index(&uusr, user, true);
+       if (rc)
+               goto error;
+       rc = db_get_name_index(&uval, value, true);
        if (rc)
                goto error;
 
        /* search the existing rule */
-       for (i = 0 ; i < ses->count ; i++) {
-               if (ucli == ses->rules[i].client
-                && uusr == ses->rules[i].user
-                && !strcasecmp(permission, name_at(ses->rules[i].permission))) {
+       for (i = 0; i < rules_count; i++) {
+               if (ucli == rules[i].client
+                && uses == rules[i].session
+                && uusr == rules[i].user
+                && !strcasecmp(permission, name_at(rules[i].permission))) {
                        /* found */
-                       session_set_at(ses, i, value);
+                       set_at(i, uval, time2exph(expire));
                        return 0;
                }
        }
@@ -688,7 +629,7 @@ db_set(
        if (rc)
                goto error;
 
-       rc = session_add(ses, ucli, uusr, uperm, value);
+       rc = add_rule(ucli, uses, uusr, uperm, uval, time2exph(expire));
 
        return 0;
 error:
@@ -702,11 +643,11 @@ db_test(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t *value
+       const char **value,
+       time_t *expire
 ) {
-       uint32_t ucli, uusr, i, val, score, sc;
-       session_t *ses;
-       rule_t *rule;
+       uint32_t ucli, uses, uusr, i, score, sc, now;
+       rule_t *rule, *found;
 
        /* check */
        client = is_any_or_wide(client) ? WIDESTR : client;
@@ -715,41 +656,203 @@ db_test(
        permission = is_any_or_wide(permission) ? WIDESTR : permission;
 
        /* search the items */
-       val = score = 0;
-#define NOIDX   0
        if (db_get_name_index(&ucli, client, false))
                ucli = NOIDX;
+       if (db_get_name_index(&uses, session, false))
+               uses = NOIDX;
        if (db_get_name_index(&uusr, user, false))
                uusr = NOIDX;
 
-       /* get the session */
-       if (get_session(session, false, &ses))
-               ses = &sessions;
-
-retry:
        /* search the existing rule */
-       for (i = 0 ; i < ses->count ; i++) {
-               rule = &ses->rules[i];
-               if ((ucli == rule->client || WIDEIDX == rule->client)
+       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->client || WIDEIDX == rule->client)
+                && (uses == rule->session || WIDEIDX == rule->session)
                 && (uusr == rule->user || WIDEIDX == rule->user)
                 && (WIDEIDX == rule->permission
                        || !strcasecmp(permission, name_at(rule->permission)))) {
                        /* found */
-                       sc = 1 + (rule->client != WIDEIDX)
+                       sc = 1 + (rule->client != WIDEIDX) + (rule->session != WIDEIDX)
                                + (rule->user != WIDEIDX) + (rule->permission != WIDEIDX);
                        if (sc > score) {
                                score = sc;
-                               val = rule->value;
+                               found = rule;
                        }
                }
        }
-       if (!score && ses != &sessions) {
-               ses = &sessions;
-               goto retry;
+       if (!found) {
+               *value = NULL;
+               *expire = 0;
+               return 0;
+       }
+       *value = name_at(found->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
+void
+gc_mark(
+       gc_t *gc,
+       uint32_t idx
+) {
+       uint32_t *p = bsearch(&idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs);
+       assert(p != NULL);
+       gc->afters[p - gc->befores] = 1;
+}
+
+static
+uint32_t
+gc_after(
+       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
+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(gc, ANYIDX);
+       gc_mark(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 idbef, idaft, i, j, 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 {
+                       for (j = 0 ; j < 5 ; j++)
+                               gc_mark(&gc, rule->ids[j]);
+                       i++;
+               }
+       }
+
+       /* pack the strings */
+       if (gc_pack(&gc)) {
+               /* replace the ids if changed */
+               i = 0;
+               while (i < rules_count) {
+                       rule = &rules[i];
+                       for (chg = j = 0 ; j < 5 ; j++) {
+                               idbef = rule->ids[j];
+                               idaft = gc_after(&gc, idbef);
+                               rule->ids[j] = idaft;
+                               chg += idbef != idaft;
+                       }
+                       if (chg)
+                               touch_at(i);
+                       i++;
+               }
        }
 
-       if (score)
-               *value = val;
-       return score > 0;
+       /* terminate */
+       gc_end(&gc);
+
+       return 0;
 }
 
index 0639833..192e215 100644 (file)
--- a/src/db.h
+++ b/src/db.h
@@ -89,7 +89,8 @@ db_for_all(
                const char *session,
                const char *user,
                const char *permission,
-               uint32_t value),
+               const char *value,
+               time_t expire),
        const char *client,
        const char *session,
        const char *user,
@@ -114,7 +115,8 @@ db_set(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 );
 
 /** check rules */
@@ -125,6 +127,12 @@ db_test(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t *value
+       const char **value,
+       time_t *expire
+);
+
+/** cleanup the base */
+int
+db_cleanup(
 );
 
index 794487d..b9934af 100644 (file)
@@ -77,26 +77,25 @@ static int from_check_status(int rc)
        return rc;
 }
 
-static int from_value(uint32_t value)
+static int from_value(const char *value)
 {
-       switch(value) {
-       case 0: return CYNARA_ADMIN_DENY;
-       case 1: return CYNARA_ADMIN_ALLOW;
-       case 2: return CYNARA_ADMIN_ASK;
-       }
-       return (int)value;
+       if (!strcmp(value, "yes"))
+               return CYNARA_ADMIN_ALLOW;
+       if (!strcmp(value, "ask"))
+               return CYNARA_ADMIN_ASK;
+       return CYNARA_ADMIN_DENY;
 }
 
-static uint32_t to_value(int value)
+static const char *to_value(int value)
 {
        switch(value) {
-       case CYNARA_ADMIN_DENY: return 0;
-       case CYNARA_ADMIN_NONE: return 0;
-       case CYNARA_ADMIN_BUCKET: return 0;
-       case CYNARA_ADMIN_ALLOW: return 1;
-       case CYNARA_ADMIN_ASK: return 2;
+       case CYNARA_ADMIN_DENY:
+       case CYNARA_ADMIN_NONE:
+       case CYNARA_ADMIN_BUCKET: return "no";
+       case CYNARA_ADMIN_ALLOW: return "yes";
+       case CYNARA_ADMIN_ASK: return "ask";
        }
-       return (uint32_t)value;
+       return "?";
 }
 
 /************************************ ERROR ****************************************/
@@ -173,7 +172,7 @@ int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin,
                                                p->client, "*", p->user, p->privilege);
                        else if (p->result != CYNARA_ADMIN_BUCKET && p->result != CYNARA_ADMIN_NONE)
                                rc = rcyn_set((rcyn_t*)p_cynara_admin,
-                                               p->client, "*", p->user, p->privilege, to_value(p->result));
+                                               p->client, "*", p->user, p->privilege, to_value(p->result), 0);
                        p = *++policies;
                }
                rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0);
@@ -189,7 +188,8 @@ static void check_cb(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 ) {
        *((int*)closure) = from_value(value);
 }
@@ -219,7 +219,8 @@ static void list_cb(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 ) {
        struct list_data *data = closure;
        struct cynara_admin_policy *pol;
diff --git a/src/main-cynadm.c b/src/main-cynadm.c
new file mode 100644 (file)
index 0000000..e5318a1
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+
+#include "rcyn-client.h"
+
+rcyn_t *rcyn;
+
+char buffer[4000];
+char *str[40];
+int nstr;
+
+static const int MIN = 60;
+static const int HOUR = 60*MIN;
+static const int DAY = 24*HOUR;
+static const int YEAR = 365*DAY;
+
+const char *client, *session, *user, *permission, *value;
+time_t expire;
+int txt2experr;
+
+time_t txt2exp(const char *txt)
+{
+       time_t r, x;
+
+       txt2experr = 0;
+       if (!strcmp(txt, "always"))
+               return 0;
+
+       r = time(NULL);
+       while(*txt) {
+               x = 0;
+               while('0' <= *txt && *txt <= '9')
+                       x = 10 * x + (time_t)(*txt++ - '0');
+               switch(*txt) {
+               case 'y': r += x * YEAR; txt++; break;
+               case 'd': r += x *= DAY; txt++; break;
+               case 'h': r += x *= HOUR; txt++; break;
+               case 'm': r += x *= MIN; txt++; break;
+               case 's': txt++; /*@fallthrough@*/
+               case 0: r += x; break;
+               default: txt2experr = 1; return -1;
+               }
+       }
+       return r;
+}
+
+const char *exp2txt(time_t expire)
+{
+       static char buffer[200];
+       int n;
+
+       if (!expire)
+               return "always";
+       expire -= time(NULL);
+       n = 0;
+       if (expire >= YEAR) {
+               n += snprintf(&buffer[n], sizeof buffer - n, "%lldy",
+                       (long long)(expire / YEAR));
+               expire = expire % YEAR;
+       }
+       if (expire >= DAY) {
+               n += snprintf(&buffer[n], sizeof buffer - n, "%lldd",
+                       (long long)(expire / DAY));
+               expire = expire % DAY;
+       }
+       if (expire >= HOUR) {
+               n += snprintf(&buffer[n], sizeof buffer - n, "%lldh",
+                       (long long)(expire / HOUR));
+               expire = expire % HOUR;
+       }
+       if (expire >= MIN) {
+               n += snprintf(&buffer[n], sizeof buffer - n, "%lldm",
+                       (long long)(expire / MIN));
+               expire = expire % MIN;
+       }
+       if (expire) {
+               n += snprintf(&buffer[n], sizeof buffer - n, "%llds",
+                       (long long)expire);
+       }
+       return buffer;
+}
+
+int plink(int ac, char **av, int *used, int maxi)
+{
+       int r = 0;
+
+       if (maxi < ac)
+               ac = maxi;
+       while (r < ac && strcmp(av[r], ";"))
+               r++;
+
+       *used = r + (r < ac);
+       return r;
+}
+
+int get_csupve(int ac, char **av, int *used, const char *def)
+{
+       int n = plink(ac, av, used, 7);
+
+       client = n > 1 ? av[1] : def;
+       session = n > 2 ? av[2] : def;
+       user = n > 3 ? av[3] : def;
+       permission = n > 4 ? av[4] : def;
+       value = n > 5 ? av[5] : "no";
+       expire = n > 6 ? txt2exp(av[6]) : 0;
+
+       return client && session && user && permission && value && !txt2experr ? 0 : -EINVAL;
+}
+
+int get_csup(int ac, char **av, int *used, const char *def)
+{
+       int n = plink(ac, av, used, 5);
+
+       client = n > 1 ? av[1] : def;
+       session = n > 2 ? av[2] : def;
+       user = n > 3 ? av[3] : def;
+       permission = n > 4 ? av[4] : def;
+
+       return client && session && user && permission ? 0 : -EINVAL;
+}
+
+void listcb(void *closure, const char *client, const char *session,
+               const char *user, const char *permission,
+               const char *value, time_t expire)
+{
+       int *p = closure;
+       const char *e = exp2txt(expire);
+       fprintf(stdout, "%s\t%s\t%s\t%s\t%s\t%s\n", client, session, user, permission, value, e);
+       (*p)++;
+}
+
+int do_list(int ac, char **av)
+{
+       int count, uc, rc;
+
+       rc = get_csup(ac, av, &uc, "#");
+       if (rc == 0) {
+               count = 0;
+               rc = rcyn_get(rcyn, client, session, user, permission, listcb, &count);
+               if (rc < 0)
+                       fprintf(stderr, "error %s\n", strerror(-rc));
+               else
+                       fprintf(stdout, "%d entries found\n", count);
+       }
+       return uc;
+}
+
+int do_set(int ac, char **av)
+{
+       int uc, rc;
+
+       rc = get_csupve(ac, av, &uc, "*");
+       if (rc == 0)
+               rc = rcyn_enter(rcyn);
+       if (rc == 0) {
+               rc = rcyn_set(rcyn, client, session, user, permission, value, expire);
+               rcyn_leave(rcyn, !rc);
+       }
+       if (rc < 0)
+               fprintf(stderr, "error %s\n", strerror(-rc));
+       return uc;
+}
+
+int do_drop(int ac, char **av)
+{
+       int uc, rc;
+
+       rc = get_csup(ac, av, &uc, "#");
+       if (rc == 0)
+               rc = rcyn_enter(rcyn);
+       if (rc == 0) {
+               rc = rcyn_drop(rcyn, client, session, user, permission);
+               rcyn_leave(rcyn, !rc);
+       }
+       if (rc < 0)
+               fprintf(stderr, "error %s\n", strerror(-rc));
+       return uc;
+}
+
+int do_check(int ac, char **av, int (*f)(rcyn_t*,const char*,const char*,const char*,const char*))
+{
+       int uc, rc;
+
+       rc = get_csup(ac, av, &uc, NULL);
+       if (rc == 0) {
+               rc = f(rcyn, client, session, user, permission);
+               if (rc > 0)
+                       fprintf(stdout, "allowed\n");
+               else if (rc == 0)
+                       fprintf(stdout, "denied\n");
+               else
+                       fprintf(stderr, "error %s\n", strerror(-rc));
+       }
+       return uc;
+}
+
+int do_any(int ac, char **av)
+{
+       if (!ac)
+               return 0;
+
+       if (!strcmp(av[0], "list"))
+               return do_list(ac, av);
+
+       if (!strcmp(av[0], "set"))
+               return do_set(ac, av);
+
+       if (!strcmp(av[0], "drop"))
+               return do_drop(ac, av);
+
+       if (!strcmp(av[0], "check"))
+               return do_check(ac, av, rcyn_check);
+
+       if (!strcmp(av[0], "test"))
+               return do_check(ac, av, rcyn_test);
+
+       if (!strcmp(av[0], "cache"))
+               return do_check(ac, av, rcyn_cache_check);
+
+       if (!strcmp(av[0], "clear")) {
+               rcyn_cache_clear(rcyn);
+               return 1;
+       }
+
+       return 0;
+}
+
+void do_all(int ac, char **av)
+{
+       int rc;
+
+       while(ac) {
+               rc = do_any(ac, av);
+               if (rc <= 0)
+                       exit(1);
+               ac -= rc;
+               av += rc;
+       }
+}
+
+int main(int ac, char **av)
+{
+       int rc;
+
+       signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */
+       rc = rcyn_open(&rcyn, rcyn_Admin, 5000);
+       if (rc < 0) {
+               fprintf(stderr, "initialisation failed: %s\n", strerror(-rc));
+               return 1;
+       }
+       if (ac > 1) {
+               do_all(ac - 1, av + 1);
+               return 0;
+       }
+
+       for(;;) {
+               if (!fgets(buffer, sizeof buffer, stdin))
+                       break;
+
+               str[nstr = 0] = strtok(buffer, " \t\n");
+               while(str[nstr])
+                       str[++nstr] = strtok(NULL, " \t\n");
+
+               ac = 0;
+               while(ac < nstr) {
+                       rc = do_any(nstr - ac, &str[ac]);
+                       if (rc <= 0)
+                               exit(1);
+                       ac += rc;
+               }
+       }
+       return 0;
+}
+
index ba7d71c..5744672 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <limits.h>
 #include <string.h>
+#include <time.h>
 #include <getopt.h>
 #include <unistd.h>
 #include <pwd.h>
@@ -106,24 +107,24 @@ helptxt[] =
        "\n"
        "otpions:\n"
 #if defined(WITH_SYSTEMD_ACTIVATION)
-       "       -s, --systemd         socket activation by systemd\n"
+       "       -s, --systemd         socket activation by systemd\n"
 #endif
        "       -u, --user xxx        set the user\n"
        "       -g, --group xxx       set the group\n"
-       "       -i, --init xxx        initialize if needed the database with content of file xxx\n"
-       "\n"
+       "       -i, --init xxx        initialize if needed the database with file xxx\n"
+       "                               (default: "DEFAULT_INIT_FILE"\n"
        "       -b, --dbdir xxx       set the directory of database\n"
-       "                              (default: "DEFAULT_DB_DIR")\n"
+       "                               (default: "DEFAULT_DB_DIR")\n"
        "       -m, --make-db-dir     make the database directory\n"
        "       -o, --own-db-dir      set user and group on database directory\n"
        "\n"
        "       -S, --socketdir xxx   set the base xxx for sockets\n"
-       "                              (default: "DEFAULT_SOCKET_DIR")\n"
+       "                               (default: "DEFAULT_SOCKET_DIR")\n"
        "       -M, --make-socket-dir make the socket directory\n"
        "       -O, --own-socket-dir  set user and group on socket directory\n"
        "\n"
-       "       -h, --help            print this help and exit\n"
-       "       -v, --version         print the version and exit\n"
+       "       -h, --help            print this help and exit\n"
+       "       -v, --version         print the version and exit\n"
        "\n"
 ;
 
@@ -435,12 +436,42 @@ static void ensure_directory(const char *path, int uid, int gid)
        ensuredir(p, (int)l, uid, gid);
 }
 
+time_t txt2exp(const char *txt)
+{
+       static const int MIN = 60;
+       static const int HOUR = 60*MIN;
+       static const int DAY = 24*HOUR;
+       static const int YEAR = 365*DAY;
+
+       time_t r, x;
+
+       if (!strcmp(txt, "always"))
+               return 0;
+       r = time(NULL);
+       while(*txt) {
+               x = 0;
+               while('0' <= *txt && *txt <= '9')
+                       x = 10 * x + (time_t)(*txt++ - '0');
+               switch(*txt) {
+               case 'y': r += x * YEAR; txt++; break;
+               case 'd': r += x *= DAY; txt++; break;
+               case 'h': r += x *= HOUR; txt++; break;
+               case 'm': r += x *= MIN; txt++; break;
+               case 's': txt++; /*@fallthrough@*/
+               case 0: r += x; break;
+               default: return -1;
+               }
+       }
+       return r;
+}
+
 /** initialize the database from file of 'path' */
 static void initdb(const char *path)
 {
-       int rc, lino, x;
+       int rc, lino;
        char *item[10];
        char buffer[2048];
+       time_t expire;
        FILE *f;
 
        f = fopen(path, "r");
@@ -459,20 +490,22 @@ static void initdb(const char *path)
                        item[3] = strtok(NULL, " \t\n\r");
                        item[4] = strtok(NULL, " \t\n\r");
                        item[5] = strtok(NULL, " \t\n\r");
+                       item[6] = strtok(NULL, " \t\n\r");
                        if (item[1] == NULL || item[2] == NULL
-                         || item[3] == NULL || item[4] == NULL) {
+                         || item[3] == NULL || item[4] == NULL
+                         || item[5] == NULL) {
                                fprintf(stderr, "field missing (%s:%d)\n", path, lino);
                                exit(1);
-                       } else if (item[5] != NULL && item[5][0] != '#') {
+                       } else if (item[6] != NULL && item[6][0] != '#') {
                                fprintf(stderr, "extra field (%s:%d)\n", path, lino);
                                exit(1);
                        }
-                       x = isid(item[4]);
-                       if (x < 0) {
-                               fprintf(stderr, "bad value (%s:%d)\n", path, lino);
+                       expire = txt2exp(item[5]);
+                       if (expire < 0) {
+                               fprintf(stderr, "bad expiration %s (%s:%d)\n", item[5], path, lino);
                                exit(1);
                        }
-                       rc = db_set(item[0], item[1], item[2], item[3], x);
+                       rc = db_set(item[0], item[1], item[2], item[3], item[4], expire);
                        if (rc < 0) {
                                fprintf(stderr, "can't set (%s:%d)\n", path, lino);
                                exit(1);
index e2fb84c..0699bb1 100644 (file)
@@ -55,16 +55,8 @@ qread(
 
 static
 bool
-qget_char(
-       char *value
-) {
-       return qread(value, sizeof *value);
-}
-
-static
-bool
-qget_uint32(
-       uint32_t *value
+qget_time(
+       time_t *value
 ) {
        return qread(value, sizeof *value);
 }
@@ -109,16 +101,8 @@ qwrite(
 
 static
 bool
-qput_char(
-       char value
-) {
-       return qwrite(&value, sizeof value);
-}
-
-static
-bool
-qput_uint32(
-       uint32_t value
+qput_time(
+       time_t value
 ) {
        return qwrite(&value, sizeof value);
 }
@@ -144,11 +128,11 @@ queue_drop(
        const char *user,
        const char *permission
 ) {
-       return qput_char(DROP)
-               && qput_string(client)
+       return qput_string(client)
                && qput_string(session)
                && qput_string(user)
                && qput_string(permission)
+               && qput_string(0)
                        ? 0 : -(errno = ENOMEM);
 }
 
@@ -158,14 +142,15 @@ queue_set(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 ) {
-       return qput_char(SET)
-               && qput_string(client)
+       return qput_string(client)
                && qput_string(session)
                && qput_string(user)
                && qput_string(permission)
-               && qput_uint32(value)
+               && qput_string(value)
+               && qput_time(expire)
                        ? 0 : -(errno = ENOMEM);
 }
 
@@ -180,26 +165,28 @@ int
 queue_play(
 ) {
        int rc, rc2;
-       char op;
        const char *client;
        const char *session;
        const char *user;
        const char *permission;
-       uint32_t value;
+       const char *value;
+       time_t expire;
 
        rc = 0;
        queue.read = 0;
        while (queue.read < queue.write) {
                rc2 = -EINVAL;
-               if (qget_char(&op)
-                && qget_string(&client)
+               if (qget_string(&client)
                 && qget_string(&session)
                 && qget_string(&user)
-                && qget_string(&permission)) {
-                       if (op == DROP)
+                && qget_string(&permission)
+                && qget_string(&value)) {
+                       if (!value[0])
                                rc2 = db_drop(client, session, user, permission);
-                       else if (qget_uint32(&value))
-                               rc2 = db_set(client, session, user, permission, value);
+                       else {
+                               if (qget_time(&expire))
+                                       rc2 = db_set(client, session, user, permission, value, expire);
+                       }
                }
                if (rc2 != 0 && rc == 0)
                        rc = rc2;
index 1937ee1..a9fc454 100644 (file)
@@ -32,7 +32,8 @@ queue_set(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 );
 
 extern
index e1f7841..47a5798 100644 (file)
@@ -261,11 +261,29 @@ status_done(
 static
 int
 status_check(
-       rcyn_t *rcyn
+       rcyn_t *rcyn,
+       time_t *expire
 ) {
-       return !strcmp(rcyn->reply.fields[0], _yes_) ? 1
-               : !strcmp(rcyn->reply.fields[0], _no_) ? 0
-               : -EEXIST;
+       int rc, exp;
+
+       if (!strcmp(rcyn->reply.fields[0], _yes_)) {
+               rc = 1;
+               exp = 1;
+       } else if (!strcmp(rcyn->reply.fields[0], _no_)) {
+               rc = 0;
+               exp = 1;
+       } else if (!strcmp(rcyn->reply.fields[0], _done_)) {
+               rc = -EEXIST;
+               exp = 2;
+       } else {
+               rc = -EPROTO;
+               exp = rcyn->reply.count;
+       }
+       if (exp < rcyn->reply.count)
+               *expire = strtoll(rcyn->reply.fields[exp], NULL, 10);
+       else
+               *expire = 0;
+       return rc;
 }
 
 static
@@ -366,6 +384,8 @@ int
 ensure_opened(
        rcyn_t *rcyn
 ) {
+       if (rcyn->fd >= 0 && write(rcyn->fd, NULL, 0) < 0)
+               disconnection(rcyn);
        return rcyn->fd < 0 ? connection(rcyn) : 0;
 }
 
@@ -475,6 +495,7 @@ check_or_test(
        const char *action
 ) {
        int rc;
+       time_t expire;
 
        if (rcyn->async.requests != NULL)
                return -EINPROGRESS;
@@ -496,9 +517,9 @@ check_or_test(
                /* get the response */
                rc = wait_pending_reply(rcyn);
                if (rc >= 0) {
-                       rc = status_check(rcyn);
+                       rc = status_check(rcyn, &expire);
                        if (rc >= 0)
-                               cache_put(rcyn->cache, client, session, user, permission, rc);
+                               cache_put(rcyn->cache, client, session, user, permission, rc, expire);
                }
        }
        return rc;
@@ -523,7 +544,7 @@ rcyn_test(
        const char *user,
        const char *permission
 ) {
-       return check_or_test(rcyn, client, session, user, permission, _check_);
+       return check_or_test(rcyn, client, session, user, permission, _test_);
 }
 
 int
@@ -533,9 +554,11 @@ rcyn_set(
        const char *session,
        const char *user,
        const char *permission,
-       int value
+       const char *value,
+       time_t expire
 ) {
-       char val[30];
+       char text[30];
+       const char *exp;
        int rc;
 
        if (rcyn->type != rcyn_Admin)
@@ -546,8 +569,13 @@ rcyn_set(
        if (rc < 0)
                return rc;
 
-       snprintf(val, sizeof val, "%u", (unsigned)value);
-       rc = putx(rcyn, _set_, client, session, user, permission, val, NULL);
+       if (!expire)
+               exp = NULL;
+       else {
+               snprintf(text, sizeof text, "%lld", (long long)expire);
+               exp = text;
+       }
+       rc = putx(rcyn, _set_, client, session, user, permission, value, exp, NULL);
        if (rc >= 0)
                rc = wait_done(rcyn);
        return rc;
@@ -566,7 +594,8 @@ rcyn_get(
                const char *session,
                const char *user,
                const char *permission,
-               uint32_t value
+               const char *value,
+               time_t expire
        ),
        void *closure
 ) {
@@ -583,13 +612,14 @@ rcyn_get(
        rc = putx(rcyn, _get_, client, session, user, permission, NULL);
        if (rc >= 0) {
                rc = wait_reply(rcyn, true);
-               while (rc == 6 && !strcmp(rcyn->reply.fields[0], _item_)) {
+               while ((rc == 6 || rc == 7) && !strcmp(rcyn->reply.fields[0], _item_)) {
                        callback(closure,
                                rcyn->reply.fields[1],
                                rcyn->reply.fields[2],
                                rcyn->reply.fields[3],
                                rcyn->reply.fields[4],
-                               (uint32_t)atoi(rcyn->reply.fields[5]));
+                               rcyn->reply.fields[5],
+                               rc == 6 ? 0 : (time_t)strtoll(rcyn->reply.fields[6], NULL, 10));
                        rc = wait_reply(rcyn, true);
                }
                rc = status_done(rcyn);
@@ -683,6 +713,7 @@ rcyn_async_process(
        const char *first;
        asreq_t *ar;
        const char *client, *session, *user, *permission;
+       time_t expire;
 
        for (;;) {
                /* non blocking wait for a reply */
@@ -707,13 +738,13 @@ rcyn_async_process(
 
                /* emit the asynchronous answer */
                rcyn->async.requests = ar->next;
-               rc = status_check(rcyn);
+               rc = status_check(rcyn, &expire);
                if (rc >= 0) {
                        client = (const char*)(ar + 1);
                        session = &client[1 + strlen(client)];
                        user = &session[1 + strlen(session)];
                        permission = &user[1 + strlen(user)];
-                       cache_put(rcyn->cache, client, session, user, permission, rc);
+                       cache_put(rcyn->cache, client, session, user, permission, rc, expire);
                }
                ar->callback(ar->closure, rc);
                free(ar);
index a4104ee..611f843 100644 (file)
@@ -81,7 +81,8 @@ rcyn_set(
        const char *session,
        const char *user,
        const char *permission,
-       int value
+       const char *value,
+       time_t expire
 );
 
 extern
@@ -98,7 +99,8 @@ rcyn_get(
                const char *session,
                const char *user,
                const char *permission,
-               uint32_t value
+               const char *value,
+               time_t expire
        ),
        void *closure
 );
index 134d05e..7a35ab9 100644 (file)
@@ -13,12 +13,12 @@ invalidate cache:
 test a permission:
 
   c->s test CLIENT SESSION USER PERMISSION
-  s->c yes|no|VALUE [CACHING]
+  s->c [yes|no|done VALUE] [EXPIRE]
 
 check a permission:
 
   c->s check CLIENT SESSION USER PERMISSION
-  s->c yes|no|VALUE [CACHING]
+  s->c [yes|no|done VALUE] [EXPIRE]
 
 erase (admin):
 
@@ -27,13 +27,13 @@ erase (admin):
 
 set (admin):
 
-  c->s set CLIENT SESSION USER PERMISSION VALUE
+  c->s set CLIENT SESSION USER PERMISSION VALUE [EXPIRE]
   s->c done|error ...
 
 list permissions (admin):
 
   c->s get CLIENT SESSION USER PERMISSION
-  s->c item CLIENT SESSION USER PERMISSION VALUE
+  s->c item CLIENT SESSION USER PERMISSION VALUE [EXPIRE]
   s->c ...
   s->c done
 
@@ -65,15 +65,14 @@ asking (agent ask CLIENT SESSION USER PERMISSION):
   c->s g(et) CLIENT SESSION USER PERMISSION
   c->s l(eave) [commit|rollback]
   c->s r(cyn)
-  c->s s(et) CLIENT SESSION USER PERMISSION VALUE
+  c->s s(et) CLIENT SESSION USER PERMISSION VALUE EXPIRE
   c->s t(est) CLIENT SESSION USER PERMISSION
 
   s->c clear
   s->c done
   s->c done [CLIENT SESSION USER PERMISSION VALUE]
   s->c done|error ...
-  s->c item CLIENT SESSION USER PERMISSION VALUE
-  s->c yes|no
-  s->c yes|no|VALUE [CACHING]
+  s->c item CLIENT SESSION USER PERMISSION VALUE EXPIRE
+  s->c done VALUE EXPIRE
 
 
index 2fff5db..13f3a2e 100644 (file)
@@ -277,26 +277,37 @@ entercb(
        send_done(cli);
 }
 
+/** translate optional expire value */
+static
+const char *
+exp2txt(
+       time_t expire,
+       char *buffer,
+       size_t bufsz
+) {
+       if (!expire)
+               return NULL;
+
+       /* TODO: check size */
+       snprintf(buffer, bufsz, "%lld", (long long)expire);
+       return buffer;
+}
+
 /** callback of checking */
 static
 void
 checkcb(
        void *closure,
-       uint32_t value
+       const char *value,
+       time_t expire
 ) {
        client_t *cli = closure;
-       const char *a;
-       char val[30];
-
-       if (value == DENY)
-               a = _no_;
-       else if (value == ALLOW)
-               a = _yes_;
-       else {
-               snprintf(val, sizeof val, "%d", value);
-               a = val;
-       }
-       putx(cli, a, NULL);
+       char text[30];
+
+       if (strcmp(value, ALLOW) && strcmp(value, DENY))
+               putx(cli, _done_, value, exp2txt(expire, text, sizeof text), NULL);
+       else
+               putx(cli, value, exp2txt(expire, text, sizeof text), NULL);
        flushw(cli);
 }
 
@@ -309,12 +320,14 @@ getcb(
        const char *session,
        const char *user,
        const char *permission,
-       uint32_t value
+       const char *value,
+       time_t expire
 ) {
        client_t *cli = closure;
-       char val[30];
-       snprintf(val, sizeof val, "%d", value);
-       putx(cli, _item_, client, session, user, permission, val, NULL);
+       char text[30];
+
+       putx(cli, _item_, client, session, user, permission,
+               value, exp2txt(expire, text, sizeof text), NULL);
 }
 
 /** handle a request */
@@ -326,7 +339,8 @@ onrequest(
        const char *args[]
 ) {
        int rc;
-       uint32_t value;
+       const char *value;
+       time_t expire;
 
        /* just ignore empty lines */
        if (count == 0)
@@ -397,21 +411,25 @@ onrequest(
                }
                break;
        case 's': /* set */
-               if (ckarg(args[0], _set_, 1) && count == 6) {
+               if (ckarg(args[0], _set_, 1) && (count == 6 || count == 7)) {
                        if (cli->type != rcyn_Admin)
                                break;
                        if (!cli->entered)
                                break;
-                       value = (uint32_t)atol(args[5]);
-                       rc = cyn_set(args[1], args[2], args[3], args[4], value);
+                       if (count == 6)
+                               expire = 0;
+                       else
+                               expire = strtoll(args[6], NULL, 10);
+                       rc = cyn_set(args[1], args[2], args[3], args[4], args[5], expire);
                        send_done_or_error(cli, rc);
                        return;
                }
                break;
        case 't': /* test */
                if (ckarg(args[0], _test_, 1) && count == 5) {
-                       cyn_test(args[1], args[2], args[3], args[4], &value);
-                       checkcb(cli, value);
+                       cli->checked = 1;
+                       cyn_test(args[1], args[2], args[3], args[4], &value, &expire);
+                       checkcb(cli, value, expire);
                        return;
                }
                break;
@@ -740,44 +758,3 @@ rcyn_server_serve(
        return server->stopped == INT_MIN ? 0 : server->stopped;
 }
 
-#if 0
-#if defined(WITH_SYSTEMD_ACTIVATION)
-#include <systemd/sd-daemon.h>
-#endif
-
-
-int main(int ac, char **av)
-{
-       int rc;
-       const char *check_spec = rcyn_default_check_socket_spec;
-       const char *admin_spec = rcyn_default_admin_socket_spec;
-       rcyn_server_t *server;
-
-       /* connection to the database */
-       rc = cyn_init(
-               "/var/lib/cynara/cynara.names",
-               "/var/lib/cynara/cynara.rules",
-               (const char**)((const char *[]){ "System", "*", "*", "*", "1", NULL })
-       );
-       if (rc < 0) {
-               fprintf(stderr, "can't initialise database: %m\n");
-               return 1;
-       }
-
-       /* create the server */
-       rc = rcyn_server_create(&server, admin_spec, check_spec);
-       if (rc < 0) {
-               fprintf(stderr, "can't initialise server: %m\n");
-               return 1;
-       }
-
-       /* ready ! */
-#if defined(WITH_SYSTEMD_ACTIVATION)
-       sd_notify(0, "READY=1");
-#endif
-
-       /* process inputs */
-       rcyn_server_serve(server);
-}
-#endif
-