Enforce ID on check/test queries
authorJosé Bollo <jose.bollo@iot.bzh>
Fri, 18 Oct 2019 14:22:49 +0000 (16:22 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Fri, 18 Oct 2019 14:23:56 +0000 (16:23 +0200)
Change-Id: Ibdb7454657bcdc0a0874f05e065551de80b9bd4f
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
src/CMakeLists.txt
src/cyn-protocol.c
src/cyn-protocol.h
src/cyn-server.c
src/cynagora-protocol.txt
src/cynagora.c
src/cynagora.h
src/idgen.c [new file with mode: 0644]
src/idgen.h [new file with mode: 0644]

index a60f30c..cf98bbe 100644 (file)
@@ -31,19 +31,21 @@ set(LIBCORE_SOURCES
 
 set(SERVER_SOURCES
        agent-at.c
-       main-cynagorad.c
-       prot.c
        cyn-protocol.c
        cyn-server.c
+       idgen.c
+       main-cynagorad.c
+       prot.c
        socket.c
 )
 
 set(LIBCLI_SOURCES
-       expire.c
        cache.c
-       prot.c
-       cynagora.c
        cyn-protocol.c
+       cynagora.c
+       expire.c
+       idgen.c
+       prot.c
        socket.c
 )
 
index c443996..f5d4825 100644 (file)
@@ -25,6 +25,7 @@
 #include "cyn-protocol.h"
 
 const char
+       _ack_[] = "ack",
        _agent_[] = "agent",
        _ask_[] = "ask",
        _check_[] = "check",
index e68d70c..2c55df9 100644 (file)
@@ -23,6 +23,7 @@
 
 /* predefined protocol strings */
 extern const char
+       _ack_[],
        _agent_[],
        _ask_[],
        _check_[],
index b3aa3b3..6c0e373 100644 (file)
@@ -44,7 +44,7 @@
 #include "socket.h"
 #include "pollitem.h"
 #include "expire.h"
-#include "cynagora.h"
+#include "idgen.h"
 
 typedef struct client client_t;
 typedef struct agent agent_t;
@@ -97,11 +97,11 @@ struct client
        /** list of pending ask */
        ask_t *asks;
 
-       /** last askid */
-       uint32_t askid;
-
        /** list of pending checks */
        check_t *checks;
+
+       /** id generator */
+       idgen_t idgen;
 };
 
 /** structure for pending asks */
@@ -114,7 +114,7 @@ struct ask
        cynagora_query_t *query;
 
        /** id of the ask */
-       uint32_t id;
+       idgen_t id;
 };
 
 /** structure for pending checks */
@@ -128,6 +128,9 @@ struct check
 
        /** is check? otherwise it is test */
        bool ischeck;
+
+       /** id */
+       char id[];
 };
 
 /** structure for servers */
@@ -352,6 +355,7 @@ static
 void
 replycheck(
        client_t *cli,
+       const char *id,
        const data_value_t *value,
        bool ischeck
 ) {
@@ -367,12 +371,12 @@ replycheck(
                else if (!strcmp(value->value, DENY) || ischeck)
                        vtxt = _no_;
                else
-                       vtxt = _done_;
+                       vtxt = _ack_;
                if (value->expire >= 0)
                        cli->caching = 1;
                etxt = exp2check(value->expire, text, sizeof text);
        }
-       putx(cli, vtxt, etxt, NULL);
+       putx(cli, vtxt, id, etxt, NULL);
        flushw(cli);
 }
 
@@ -397,7 +401,7 @@ checkcb(
                                *pc = check->next;
                                break;
                        }
-               replycheck(cli, value, check->ischeck);
+               replycheck(cli, check->id, value, check->ischeck);
        }
        free(check);
 }
@@ -414,19 +418,20 @@ makecheck(
        data_key_t key;
        check_t *check;
 
-       check = malloc(sizeof *check);
+       check = malloc(sizeof *check + 1 + strlen(args[1]));
        if (!check)
-               replycheck(cli, NULL, ischeck);
+               replycheck(cli, args[1], NULL, ischeck);
        else {
+               strcpy(check->id, args[1]);
                check->ischeck = ischeck;
                check->client = cli;
                check->next = cli->checks;
                cli->checks = check;
 
-               key.client = args[1];
-               key.session = args[2];
-               key.user = args[3];
-               key.permission = args[4];
+               key.client = args[2];
+               key.session = args[3];
+               key.user = args[4];
+               key.permission = args[5];
                (ischeck ? cyn_check_async : cyn_test_async)(checkcb, check, &key);
        }
 }
@@ -451,13 +456,13 @@ static
 ask_t*
 searchask(
        client_t *cli,
-       uint32_t askid,
+       const char *askid,
        bool unlink
 ) {
        ask_t *r, **prv;
 
        prv = &cli->asks;
-       while ((r = *prv) && r->id != askid)
+       while ((r = *prv) && strcmp(r->id, askid))
                prv = &r->next;
        if (r && unlink)
                *prv = r->next;
@@ -475,26 +480,18 @@ agentcb(
        cynagora_query_t *query
 ) {
        client_t *cli = closure;
-       uint32_t askid;
        ask_t *ask;
-       char buffer[30];
-       int rc;
-
-       /* search a valid id */
-       do {
-               askid = ++cli->askid;
-       } while (!askid && searchask(cli, askid, false));
-       rc = snprintf(buffer, sizeof buffer, "%lu", (long unsigned)askid);
-       if (rc < 0)
-               return -errno;
-       if (rc >= (int)sizeof buffer)
-               return -ECANCELED;
 
        /* allocate the ask structure */
        ask = malloc(sizeof *ask);
        if (!ask)
                return -ENOMEM;
-       ask->id = askid;
+
+       /* search a valid id */
+       do {
+               idgen_next(cli->idgen);
+       } while (searchask(cli, cli->idgen, false));
+       memcpy(ask->id, cli->idgen, sizeof cli->idgen);
        ask->query = query;
 
        /* link the ask */
@@ -502,7 +499,7 @@ agentcb(
        cli->asks = ask;
 
        /* make the query */
-       putx(cli, _ask_, buffer, name, value,
+       putx(cli, _ask_, ask->id, name, value,
                        key->client, key->session, key->user, key->permission,
                        NULL);
        flushw(cli);
@@ -518,21 +515,17 @@ replycb(
        const char *expire
 ) {
        ask_t *ask;
-       unsigned long int ul;
        data_value_t value;
 
-       ul = strtoul(askid, NULL, 10);
-       if (ul <= UINT32_MAX) {
-               ask = searchask(cli, (uint32_t)ul, true);
-               if (ask) {
-                       value.value = yesno;
-                       if (!expire)
-                               value.expire = 0;
-                       else if (!txt2exp(expire, &value.expire, true))
-                               value.expire = -1;
-                       cyn_query_reply(ask->query, &value);
-                       free(ask);
-               }
+       ask = searchask(cli, askid, true);
+       if (ask) {
+               value.value = yesno;
+               if (!expire)
+                       value.expire = 0;
+               else if (!txt2exp(expire, &value.expire, true))
+                       value.expire = -1;
+               cyn_query_reply(ask->query, &value);
+               free(ask);
        }
 }
 
@@ -562,7 +555,7 @@ onrequest(
                if (ckarg(args[0], _cynagora_, 0)) {
                        if (count < 2 || !ckarg(args[1], "1", 0))
                                goto invalid;
-                       putx(cli, _yes_, "1", cyn_changeid_string(), NULL);
+                       putx(cli, _done_, "1", cyn_changeid_string(), NULL);
                        flushw(cli);
                        cli->version = 1;
                        return;
@@ -582,7 +575,7 @@ onrequest(
                }
                break;
        case 'c': /* check */
-               if (ckarg(args[0], _check_, 1) && count == 5) {
+               if (ckarg(args[0], _check_, 1) && count == 6) {
                        makecheck(cli, count, args, true);
                        return;
                }
@@ -685,7 +678,7 @@ onrequest(
                }
                break;
        case 't': /* test */
-               if (ckarg(args[0], _test_, 1) && count == 5) {
+               if (ckarg(args[0], _test_, 1) && count == 6) {
                        makecheck(cli, count, args, false);
                        return;
                }
@@ -838,8 +831,8 @@ create_client(
        cli->pollitem.closure = cli;
        cli->pollitem.fd = fd;
        cli->asks = NULL;
-       cli->askid = 0;
        cli->checks = NULL;
+       idgen_init(cli->idgen);
        return 0;
 error3:
        prot_destroy(cli->prot);
index 2afd43b..ebefde8 100644 (file)
@@ -11,7 +11,7 @@ Introduction
  - EXPIRE:  if missing, means forever
             if positive, a number of second since EPOCH, invalid after it
  - CACHEID: a 32 bits positive integer
- - ASKID:   a 32 bits positive integer
+ - ID:      a string
 
 Messages
 --------
@@ -21,7 +21,7 @@ Messages
 synopsis:
 
        c->s cynagora 1
-       s->c yes 1 CACHEID
+       s->c done 1 CACHEID
 
 The client present itself with the version of the protocol it expects to
 speak (today version 1 only). The server answer yes with the acknoledged
@@ -46,8 +46,8 @@ identifier is CACHEID
 
 synopsis:
 
-       c->s test CLIENT SESSION USER PERMISSION
-       s->c (done|yes|no) [EXPIRE]
+       c->s test ID CLIENT SESSION USER PERMISSION
+       s->c (ack|yes|no) ID [EXPIRE]
 
 
 
@@ -55,8 +55,8 @@ synopsis:
 
 synopsis:
 
-       c->s check CLIENT SESSION USER PERMISSION
-       s->c (yes|no) [EXPIRE]
+       c->s check ID CLIENT SESSION USER PERMISSION
+       s->c (yes|no) ID [EXPIRE]
 
 
 
@@ -122,8 +122,8 @@ synopsis:
 
 synopsis:
 
-       c->s sub ASKID (test|check) CLIENT SESSION USER PERMISSION
-       s->c reply ASKID (done|yes|no) [EXPIRE]
+       c->s sub ASKID (test|check) ID CLIENT SESSION USER PERMISSION
+       s->c (ack|yes|no) ID [EXPIRE]
 
 Notes
 -----
index 2928779..aee6a4c 100644 (file)
@@ -43,6 +43,7 @@
 #include "cache.h"
 #include "socket.h"
 #include "expire.h"
+#include "idgen.h"
 
 #define MIN_CACHE_SIZE 400
 #define CACHESIZE(x)  ((x) >= MIN_CACHE_SIZE ? (x) : (x) ? MIN_CACHE_SIZE : 0)
@@ -62,6 +63,9 @@ struct asreq
 
        /** closure of the callback */
        void *closure;
+
+       /** id of the request */
+       idgen_t id;
 };
 
 /** structure to handle agents */
@@ -91,6 +95,12 @@ struct cynagora
        /** count of pending requests */
        int pending;
 
+       /** synchronous lock */
+       bool synclock;
+
+       /** entered in critical section */
+       bool entered;
+
        /** type of link */
        cynagora_type_t type;
 
@@ -127,6 +137,9 @@ struct cynagora
        /** the pending queries */
        query_t *queries;
 
+       /** id generator */
+       idgen_t idgen;
+
        /** spec of the socket */
        char socketspec[];
 };
@@ -436,17 +449,17 @@ status_check(
                rc = 1;
        else if (!strcmp(cynagora->reply.fields[0], _no_))
                rc = 0;
-       else if (!strcmp(cynagora->reply.fields[0], _done_))
+       else if (!strcmp(cynagora->reply.fields[0], _ack_))
                rc = -EEXIST;
        else
                rc = -EPROTO;
 
-       if (cynagora->reply.count < 2)
+       if (cynagora->reply.count < 3)
                *expire = 0;
-       else if (cynagora->reply.fields[1][0] == '-')
+       else if (cynagora->reply.fields[2][0] == '-')
                *expire = -1;
        else
-               txt2exp(cynagora->reply.fields[1], expire, true);
+               txt2exp(cynagora->reply.fields[2], expire, true);
 
        return rc;
 }
@@ -570,7 +583,7 @@ connection(
                if (rc >= 0) {
                        rc = -EPROTO;
                        if (cynagora->reply.count >= 2
-                        && 0 == strcmp(cynagora->reply.fields[0], _yes_)
+                        && 0 == strcmp(cynagora->reply.fields[0], _done_)
                         && 0 == strcmp(cynagora->reply.fields[1], "1")) {
                                cache_clear(cynagora->cache,
                                        cynagora->reply.count > 2 ? (uint32_t)atol(cynagora->reply.fields[2]) : 0);
@@ -631,14 +644,14 @@ check_or_test(
        int rc;
        time_t expire;
 
-       /* forbids 2 queries interleaved */
-       if (cynagora->async.requests != NULL)
-               return -EINPROGRESS;
+       if (cynagora->synclock)
+               return -EBUSY;
+       cynagora->synclock = true;
 
        /* ensure opened */
        rc = ensure_opened(cynagora);
        if (rc < 0)
-               return rc;
+               goto end;
 
        /* ensure there is no clear cache pending */
        flushr(cynagora);
@@ -647,11 +660,11 @@ check_or_test(
        if (!force) {
                rc = cache_search(cynagora->cache, key);
                if (rc >= 0)
-                       return rc;
+                       goto end;
        }
 
        /* send the request */
-       rc = putxkv(cynagora, action, 0, key, 0);
+       rc = putxkv(cynagora, action, "{sync}", key, 0);
        if (rc >= 0) {
                /* get the response */
                rc = wait_pending_reply(cynagora);
@@ -661,9 +674,37 @@ check_or_test(
                                cache_put(cynagora->cache, key, rc, expire, true);
                }
        }
+end:
+       cynagora->synclock = false;
        return rc;
 }
 
+/**
+ * get the pending asynchrounous request
+ *
+ * @param cynagora the cynagora client
+ * @param id       id of the request to find
+ * @param unlink   if true, remove the request from the
+ *                 list of requests
+ * @return the found request of NULL
+ */
+static
+asreq_t *
+search_async_request(
+       cynagora_t *cynagora,
+       const char *id,
+       bool unlink
+) {
+       asreq_t *ar, **par;
+
+       par = &cynagora->async.requests;
+       while((ar = *par) && strcmp(ar->id, id))
+               par = &ar->next;
+       if (ar && unlink)
+               *par = ar->next;
+       return ar;
+}
+
 /******************************************************************************/
 /*** PUBLIC COMMON METHODS                                                  ***/
 /******************************************************************************/
@@ -712,11 +753,14 @@ cynagora_create(
 
        /* record type and weakly create cache */
        cache_create(&cynagora->cache, CACHESIZE(cache_size)); /* ignore errors */
+       cynagora->entered = false;
+       cynagora->synclock = false;
        cynagora->type = type;
        cynagora->async.controlcb = NULL;
        cynagora->async.closure = 0;
        cynagora->async.requests = NULL;
        cynagora->agents = NULL;
+       idgen_init(cynagora->idgen);
 
        /* lazy connection */
        cynagora->fd = -1;
@@ -785,6 +829,7 @@ cynagora_async_process(
 ) {
        int rc;
        const char *first;
+       const char *id;
        asreq_t *ar;
        time_t expire;
        cynagora_key_t key;
@@ -805,13 +850,15 @@ cynagora_async_process(
                 || !strcmp(first, _error_))
                        continue;
 
+               /* search the request */
+               id = cynagora->reply.count < 2 ? "" : cynagora->reply.fields[1];
+               ar = search_async_request(cynagora, id, true);
+
                /* ignore unexpected answers */
-               ar = cynagora->async.requests;
                if (ar == NULL)
                        continue;
 
                /* emit the asynchronous answer */
-               cynagora->async.requests = ar->next;
                rc = status_check(cynagora, &expire);
                if (rc >= 0) {
                        key.client = (const char*)(ar + 1);
@@ -882,7 +929,7 @@ cynagora_async_check(
        void *closure
 ) {
        int rc;
-       asreq_t **pr, *ar;
+       asreq_t *ar;
 
        /* ensure connection */
        rc = ensure_opened(cynagora);
@@ -907,13 +954,16 @@ cynagora_async_check(
                return -ENOMEM;
 
        /* init */
-       ar->next = NULL;
+       do {
+               idgen_next(cynagora->idgen);
+       } while (search_async_request(cynagora, cynagora->idgen, false));
+       strcpy(ar->id, cynagora->idgen);
        ar->callback = callback;
        ar->closure = closure;
        stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)(ar + 1), key->client), key->session), key->user), key->permission);
 
        /* send the request */
-       rc = putxkv(cynagora, simple ? _test_ : _check_, 0, key, 0);
+       rc = putxkv(cynagora, simple ? _test_ : _check_, ar->id, key, 0);
        if (rc >= 0)
                rc = flushw(cynagora);
        if (rc < 0) {
@@ -922,10 +972,8 @@ cynagora_async_check(
        }
 
        /* record the request */
-       pr = &cynagora->async.requests;
-       while(*pr != NULL)
-               pr = &(*pr)->next;
-       *pr = ar;
+       ar->next = cynagora->async.requests;
+       cynagora->async.requests = ar;
        return 0;
 }
 
@@ -947,30 +995,32 @@ cynagora_get(
 
        if (cynagora->type != cynagora_Admin)
                return -EPERM;
-       if (cynagora->async.requests != NULL)
-               return -EINPROGRESS;
-       rc = ensure_opened(cynagora);
-       if (rc < 0)
-               return rc;
+       if (cynagora->synclock)
+               return -EBUSY;
 
-       rc = putxkv(cynagora, _get_, 0, key, 0);
+       cynagora->synclock = true;
+       rc = ensure_opened(cynagora);
        if (rc >= 0) {
-               rc = wait_reply(cynagora, true);
-               while ((rc == 6 || rc == 7) && !strcmp(cynagora->reply.fields[0], _item_)) {
-                       k.client = cynagora->reply.fields[1];
-                       k.session = cynagora->reply.fields[2];
-                       k.user = cynagora->reply.fields[3];
-                       k.permission = cynagora->reply.fields[4];
-                       v.value = cynagora->reply.fields[5];
-                       if (rc == 6)
-                               v.expire = 0;
-                       else if (!txt2exp(cynagora->reply.fields[6], &v.expire, true))
-                               v.expire = -1;
-                       callback(closure, &k, &v);
+               rc = putxkv(cynagora, _get_, 0, key, 0);
+               if (rc >= 0) {
                        rc = wait_reply(cynagora, true);
+                       while ((rc == 6 || rc == 7) && !strcmp(cynagora->reply.fields[0], _item_)) {
+                               k.client = cynagora->reply.fields[1];
+                               k.session = cynagora->reply.fields[2];
+                               k.user = cynagora->reply.fields[3];
+                               k.permission = cynagora->reply.fields[4];
+                               v.value = cynagora->reply.fields[5];
+                               if (rc == 6)
+                                       v.expire = 0;
+                               else if (!txt2exp(cynagora->reply.fields[6], &v.expire, true))
+                                       v.expire = -1;
+                               callback(closure, &k, &v);
+                               rc = wait_reply(cynagora, true);
+                       }
+                       rc = status_done(cynagora);
                }
-               rc = status_done(cynagora);
        }
+       cynagora->synclock = false;
        return rc;
 }
 
@@ -985,16 +1035,20 @@ cynagora_log(
 
        if (cynagora->type != cynagora_Admin)
                return -EPERM;
-       if (cynagora->async.requests != NULL)
-               return -EINPROGRESS;
+       if (cynagora->synclock)
+               return -EBUSY;
 
+       cynagora->synclock = true;
        rc = ensure_opened(cynagora);
-       if (rc < 0)
-               return rc;
-
-       rc = putxkv(cynagora, _log_, off ? _off_ : on ? _on_ : 0, 0, 0);
-       if (rc >= 0)
-               rc = wait_done(cynagora);
+       if (rc >= 0) {
+               rc = putxkv(cynagora, _log_, off ? _off_ : on ? _on_ : 0, 0, 0);
+               if (rc >= 0) {
+                       rc = wait_done(cynagora);
+                       if (rc > 0)
+                               rc = cynagora->reply.count >= 2 && !strcmp(cynagora->reply.fields[1], _on_);
+               }
+       }
+       cynagora->synclock = false;
 
        return rc < 0 ? rc : cynagora->reply.count < 2 ? 0 : !strcmp(cynagora->reply.fields[1], _on_);
 }
@@ -1008,15 +1062,23 @@ cynagora_enter(
 
        if (cynagora->type != cynagora_Admin)
                return -EPERM;
-       if (cynagora->async.requests != NULL)
-               return -EINPROGRESS;
+       if (cynagora->entered)
+               return -ECANCELED;
+       if (cynagora->synclock)
+               return -EBUSY;
+
+       cynagora->synclock = true;
        rc = ensure_opened(cynagora);
-       if (rc < 0)
-               return rc;
+       if (rc >= 0) {
+               rc = putxkv(cynagora, _enter_, 0, 0, 0);
+               if (rc >= 0) {
+                       rc = wait_done(cynagora);
+                       if (rc >= 0)
+                               cynagora->entered = true;
+               }
+       }
+       cynagora->synclock = false;
 
-       rc = putxkv(cynagora, _enter_, 0, 0, 0);
-       if (rc >= 0)
-               rc = wait_done(cynagora);
        return rc;
 }
 
@@ -1030,15 +1092,23 @@ cynagora_leave(
 
        if (cynagora->type != cynagora_Admin)
                return -EPERM;
-       if (cynagora->async.requests != NULL)
+       if (!cynagora->entered)
                return -ECANCELED;
+       if (cynagora->synclock)
+               return -EBUSY;
+
+       cynagora->synclock = true;
        rc = ensure_opened(cynagora);
-       if (rc < 0)
-               return rc;
+       if (rc >= 0) {
+               rc = putxkv(cynagora, _leave_, commit ? _commit_ : 0/*default: rollback*/, 0, 0);
+               if (rc >= 0) {
+                       rc = wait_done(cynagora);
+                       if (rc >= 0)
+                               cynagora->entered = false;
+               }
+       }
+       cynagora->synclock = false;
 
-       rc = putxkv(cynagora, _leave_, commit ? _commit_ : 0/*default: rollback*/, 0, 0);
-       if (rc >= 0)
-               rc = wait_done(cynagora);
        return rc;
 }
 
@@ -1053,15 +1123,20 @@ cynagora_set(
 
        if (cynagora->type != cynagora_Admin)
                return -EPERM;
-       if (cynagora->async.requests != NULL)
+       if (!cynagora->entered)
                return -ECANCELED;
+       if (cynagora->synclock)
+               return -EBUSY;
+
+       cynagora->synclock = true;
        rc = ensure_opened(cynagora);
-       if (rc < 0)
-               return rc;
+       if (rc >= 0) {
+               rc = putxkv(cynagora, _set_, 0, key, value);
+               if (rc >= 0)
+                       rc = wait_done(cynagora);
+       }
+       cynagora->synclock = false;
 
-       rc = putxkv(cynagora, _set_, 0, key, value);
-       if (rc >= 0)
-               rc = wait_done(cynagora);
        return rc;
 }
 
@@ -1075,15 +1150,20 @@ cynagora_drop(
 
        if (cynagora->type != cynagora_Admin)
                return -EPERM;
-       if (cynagora->async.requests != NULL)
+       if (!cynagora->entered)
                return -ECANCELED;
+
+       if (cynagora->synclock)
+               return -EBUSY;
+       cynagora->synclock = true;
        rc = ensure_opened(cynagora);
-       if (rc < 0)
-               return rc;
+       if (rc >= 0) {
+               rc = putxkv(cynagora, _drop_, 0, key, 0);
+               if (rc >= 0)
+                       rc = wait_done(cynagora);
+       }
+       cynagora->synclock = false;
 
-       rc = putxkv(cynagora, _drop_, 0, key, 0);
-       if (rc >= 0)
-               rc = wait_done(cynagora);
        return rc;
 }
 
index d5dc80f..363e725 100644 (file)
@@ -222,6 +222,7 @@ cynagora_cache_check(
  *
  * @return 0 if permission forbidden, 1 if permission granted
  *         or if error a negative -errno value
+ *         -EBUSY if pending synchronous request
  *
  * @see cynagora_test, cynagora_cache_check
  */
@@ -243,6 +244,7 @@ cynagora_check(
  *
  * @return 0 if permission forbidden, 1 if permission granted
  *         or if error a negative -errno value
+ *         -EBUSY if pending synchronous request
  *
  * @see cynagora_check
  */
@@ -315,6 +317,8 @@ typedef void cynagora_get_cb_t(
  * @param closure  closure of the callback
  *
  * @return 0 in case of success or a negative -errno value
+ *         -EPERM if not a admin client
+ *         -EBUSY if pending synchronous request
  */
 extern
 int
@@ -333,6 +337,8 @@ cynagora_get(
  * @param off      should set off
  *
  * @return 0 if not logging, 1 if logging or a negative -errno value
+ *         -EPERM if not a admin client
+ *         -EBUSY if pending synchronous request
  */
 extern
 int
@@ -349,7 +355,8 @@ cynagora_log(
  *
  * @return 0 in case of success or a negative -errno value
  *         -EPERM if not a admin client
- *         -EINPROGRESS if already entered
+ *         -ECANCELED if already entered
+ *         -EBUSY if pending synchronous request
  *
  * @see cynagora_leave, cynagora_set, cynagora_drop
  */
@@ -369,6 +376,7 @@ cynagora_enter(
  * @return 0 in case of success or a negative -errno value
  *         -EPERM if not a admin client
  *         -ECANCELED if not entered
+ *         -EBUSY if pending synchronous request
  *
  * @see cynagora_enter, cynagora_set, cynagora_drop
  */
@@ -390,6 +398,7 @@ cynagora_leave(
  * @return 0 in case of success or a negative -errno value
  *         -EPERM if not a admin client
  *         -ECANCELED if not entered
+ *         -EBUSY if pending synchronous request
  *
  * @see cynagora_enter, cynagora_leave, cynagora_drop
  */
@@ -411,6 +420,7 @@ cynagora_set(
  * @return  0 in case of success or a negative -errno value
  *         -EPERM if not a admin client
  *         -ECANCELED if not entered
+ *         -EBUSY if pending synchronous request
  *
  * @see cynagora_enter, cynagora_leave, cynagora_set
  */
diff --git a/src/idgen.c b/src/idgen.c
new file mode 100644 (file)
index 0000000..3e43310
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+/******************************************************************************/
+/******************************************************************************/
+/* CONVERTION OF EXPIRATIONS TO AND FROM TEXT                                 */
+/******************************************************************************/
+/******************************************************************************/
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "idgen.h"
+
+static char i2c[] =
+       "0123456789"
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+       "abcdefghijklmnopqrstuvwxyz"
+       "-+*/<%$#@?!,.&~^>=|_"
+;
+static char nxt[96];
+
+#define ZERO i2c[0]
+#define ONE i2c[1]
+
+static char next(char c)
+{
+       unsigned i;
+       char r;
+
+       if ((signed char)c <= 32)
+               r = ONE;
+       else {
+               r = nxt[c - 32];
+               if (!r) {
+                       memset(nxt, ONE, sizeof(nxt));
+                       for (i = 0 ; (r = i2c[i]) ; i++)
+                               nxt[r - 32] = i2c[i + 1] ?: ZERO;
+                       r = nxt[c - 32];
+               }
+       }
+       return r;
+}
+
+void
+idgen_init(
+       idgen_t idgen
+) {
+       memset(idgen, 0, sizeof(idgen_t));
+       idgen[0] = ZERO;
+}
+
+void
+idgen_next(
+       idgen_t idgen
+) {
+       unsigned i;
+       char c;
+
+       i = 0;
+       do {
+               c = next(idgen[i]);
+               idgen[i++] = c;
+       } while (c == ZERO && i < sizeof(idgen_t) - 1);
+}
+
+bool
+idgen_is_valid(
+       idgen_t idgen
+) {
+       unsigned i = 0;
+       while (i < sizeof(idgen_t) && idgen[i] && strchr(i2c, idgen[i]))
+               i++;
+       return i && i < sizeof(idgen_t) && !idgen[i];
+}
diff --git a/src/idgen.h b/src/idgen.h
new file mode 100644 (file)
index 0000000..59d84c2
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#pragma once
+/******************************************************************************/
+/******************************************************************************/
+/* HANDLE STRING IDS                                                          */
+/******************************************************************************/
+/******************************************************************************/
+
+typedef char idgen_t[7];
+
+extern
+void
+idgen_init(
+       idgen_t idgen
+);
+
+extern
+void
+idgen_next(
+       idgen_t idgen
+);
+
+extern
+bool
+idgen_is_valid(
+       idgen_t idgen
+);