From 3da281f531e9db1433ae2b4792a1840ca55b06bf Mon Sep 17 00:00:00 2001 From: José Bollo Date: Fri, 18 Oct 2019 16:22:49 +0200 Subject: Enforce ID on check/test queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ibdb7454657bcdc0a0874f05e065551de80b9bd4f Signed-off-by: José Bollo --- src/CMakeLists.txt | 12 +-- src/cyn-protocol.c | 1 + src/cyn-protocol.h | 1 + src/cyn-server.c | 89 +++++++++--------- src/cynagora-protocol.txt | 16 ++-- src/cynagora.c | 224 +++++++++++++++++++++++++++++++--------------- src/cynagora.h | 12 ++- src/idgen.c | 88 ++++++++++++++++++ src/idgen.h | 42 +++++++++ 9 files changed, 351 insertions(+), 134 deletions(-) create mode 100644 src/idgen.c create mode 100644 src/idgen.h 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 + * + * 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 +#include + +#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 + * + * 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 +); -- cgit 1.2.3-korg