aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2019-10-18 16:22:49 +0200
committerJosé Bollo <jose.bollo@iot.bzh>2019-10-18 16:23:56 +0200
commit3da281f531e9db1433ae2b4792a1840ca55b06bf (patch)
treea21e806475789fa38d14f0c3274655c85a42a320
parent5cb824d4aca7fea9ed58325933e1eb4f1afcb9b3 (diff)
Enforce ID on check/test queries
Change-Id: Ibdb7454657bcdc0a0874f05e065551de80b9bd4f Signed-off-by: José Bollo <jose.bollo@iot.bzh>
-rw-r--r--src/CMakeLists.txt12
-rw-r--r--src/cyn-protocol.c1
-rw-r--r--src/cyn-protocol.h1
-rw-r--r--src/cyn-server.c89
-rw-r--r--src/cynagora-protocol.txt16
-rw-r--r--src/cynagora.c224
-rw-r--r--src/cynagora.h12
-rw-r--r--src/idgen.c88
-rw-r--r--src/idgen.h42
9 files changed, 351 insertions, 134 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a60f30c..cf98bbe 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
)
diff --git a/src/cyn-protocol.c b/src/cyn-protocol.c
index c443996..f5d4825 100644
--- a/src/cyn-protocol.c
+++ b/src/cyn-protocol.c
@@ -25,6 +25,7 @@
#include "cyn-protocol.h"
const char
+ _ack_[] = "ack",
_agent_[] = "agent",
_ask_[] = "ask",
_check_[] = "check",
diff --git a/src/cyn-protocol.h b/src/cyn-protocol.h
index e68d70c..2c55df9 100644
--- a/src/cyn-protocol.h
+++ b/src/cyn-protocol.h
@@ -23,6 +23,7 @@
/* predefined protocol strings */
extern const char
+ _ack_[],
_agent_[],
_ask_[],
_check_[],
diff --git a/src/cyn-server.c b/src/cyn-server.c
index b3aa3b3..6c0e373 100644
--- a/src/cyn-server.c
+++ b/src/cyn-server.c
@@ -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);
diff --git a/src/cynagora-protocol.txt b/src/cynagora-protocol.txt
index 2afd43b..ebefde8 100644
--- a/src/cynagora-protocol.txt
+++ b/src/cynagora-protocol.txt
@@ -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
-----
diff --git a/src/cynagora.c b/src/cynagora.c
index 2928779..aee6a4c 100644
--- a/src/cynagora.c
+++ b/src/cynagora.c
@@ -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;
}
diff --git a/src/cynagora.h b/src/cynagora.h
index d5dc80f..363e725 100644
--- a/src/cynagora.h
+++ b/src/cynagora.h
@@ -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
index 0000000..3e43310
--- /dev/null
+++ b/src/idgen.c
@@ -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
index 0000000..59d84c2
--- /dev/null
+++ b/src/idgen.h
@@ -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
+);