#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;
/** 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 */
cynagora_query_t *query;
/** id of the ask */
- uint32_t id;
+ idgen_t id;
};
/** structure for pending checks */
/** is check? otherwise it is test */
bool ischeck;
+
+ /** id */
+ char id[];
};
/** structure for servers */
void
replycheck(
client_t *cli,
+ const char *id,
const data_value_t *value,
bool ischeck
) {
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);
}
*pc = check->next;
break;
}
- replycheck(cli, value, check->ischeck);
+ replycheck(cli, check->id, value, check->ischeck);
}
free(check);
}
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);
}
}
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;
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 */
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);
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);
}
}
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;
}
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;
}
}
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;
}
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);
#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)
/** closure of the callback */
void *closure;
+
+ /** id of the request */
+ idgen_t id;
};
/** structure to handle agents */
/** count of pending requests */
int pending;
+ /** synchronous lock */
+ bool synclock;
+
+ /** entered in critical section */
+ bool entered;
+
/** type of link */
cynagora_type_t type;
/** the pending queries */
query_t *queries;
+ /** id generator */
+ idgen_t idgen;
+
/** spec of the socket */
char socketspec[];
};
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;
}
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);
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);
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);
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 ***/
/******************************************************************************/
/* 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;
) {
int rc;
const char *first;
+ const char *id;
asreq_t *ar;
time_t expire;
cynagora_key_t key;
|| !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);
void *closure
) {
int rc;
- asreq_t **pr, *ar;
+ asreq_t *ar;
/* ensure connection */
rc = ensure_opened(cynagora);
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) {
}
/* 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;
}
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;
}
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_);
}
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;
}
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;
}
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;
}
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;
}
*
* @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
*/
*
* @return 0 if permission forbidden, 1 if permission granted
* or if error a negative -errno value
+ * -EBUSY if pending synchronous request
*
* @see cynagora_check
*/
* @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
* @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
*
* @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
*/
* @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
*/
* @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
*/
* @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
*/