Refactor agent mechanism
authorJose Bollo <jose.bollo@iot.bzh>
Wed, 15 May 2019 07:08:08 +0000 (09:08 +0200)
committerJose Bollo <jose.bollo@iot.bzh>
Wed, 22 May 2019 13:57:34 +0000 (15:57 +0200)
Agent are now named and called when the
value returned is prefixed by name+colon.

For example, the agent 'me' receives the requests
'request' and the asked key for the rule:

 * * * * me:request forever

Add the always available AGENT-AT implementation.
The AGENT-AT handles the value prefix @: to re-ask
the database with a query derived from the value.

Example: the rule

 * * 1001 * @:%c:%s:OWNER:%p forever

if selected for the query key

  {client=C, session=S, user=1001, permission=P}

will produce the evaluation of the key

  {client=C, session=S, user=OWNER, permission=P}

The values @: are structured as 4 field separated
by colons (:). The sequences %c, %s, %u, %p, %%
and %: are substituted by client, session, user,
permission, % and :, with values coming from the
original request.

Change-Id: I7043845292f13f9c269a71cfabc4715330eaff34
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
src/CMakeLists.txt
src/agent-at.c [new file with mode: 0644]
src/agent-at.h [new file with mode: 0644]
src/anydb.c
src/cyn.c
src/cyn.h
src/main-cynadm.c
src/main-cynarad.c
src/prot.c
src/rcyn-server.c

index e8071e8..9b09a89 100644 (file)
@@ -17,6 +17,7 @@
 ###########################################################################
 
 set(SERVER_SOURCES
+       agent-at.c
        anydb.c
        cyn.c
        db.c
diff --git a/src/agent-at.c b/src/agent-at.c
new file mode 100644 (file)
index 0000000..f671fc7
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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 <stdlib.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+
+#include "data.h"
+#include "cyn.h"
+
+static
+size_t
+parse(
+       const char *spec,
+       const data_key_t *rkey,
+       data_key_t *key,
+       char *buffer,
+       size_t szbuf
+) {
+       size_t iout, ikey, inf;
+       const char *val;
+
+       iout = 0;
+       for (ikey = 0 ; ikey < 4 ; ikey++) {
+               inf = iout;
+               while(*spec) {
+                       if (*spec == ':' && ikey < 3) {
+                               spec++;
+                               break;
+                       }
+                       if (*spec == '%' && spec[1]) {
+                               switch(spec[1]) {
+                               case 'c':
+                                       val = rkey->client;
+                                       break;
+                               case 's':
+                                       val = rkey->session;
+                                       break;
+                               case 'u':
+                                       val = rkey->user;
+                                       break;
+                               case 'p':
+                                       val = rkey->permission;
+                                       break;
+                               default:
+                                       val = 0;
+                               }
+                               if (val) {
+                                       while (*val) {
+                                               if (iout < szbuf)
+                                                       buffer[iout] = *val;
+                                               iout++;
+                                               val++;
+                                       }
+                               } else {
+                                       if (spec[1] != ':' && spec[1] != '%') {
+                                               if (iout < szbuf)
+                                                       buffer[iout] = '%';
+                                               iout++;
+                                       }
+                                       if (iout < szbuf)
+                                               buffer[iout] = spec[1];
+                                       iout++;
+                               }
+                               spec += 2;
+                       } else {
+                               if (iout < szbuf)
+                                       buffer[iout] = *spec;
+                               iout++;
+                               spec++;
+                       }
+               }
+               if (inf == iout)
+                       val = 0;
+               else {
+                       val = &buffer[inf];
+                       if (iout < szbuf)
+                               buffer[iout] = 0;
+                       iout++;
+               }
+               if (key)
+                       ((const char**)key)[ikey] = val;
+       }
+       return iout;
+}
+
+static
+int
+agent_at_cb(
+       const char *name,
+       void *agent_closure,
+       const data_key_t *key,
+       const char *value,
+       on_result_cb_t *on_result_cb,
+       void *on_result_closure
+) {
+       data_key_t atkey;
+       char *block;
+       size_t size;
+
+       size = parse(value, key, 0, 0, 0);
+       block = alloca(size);
+       parse(value, key, &atkey, block, size);
+       return cyn_test_async(on_result_cb, on_result_closure, &atkey);
+}
+
+int
+agent_at_activate(
+) {
+       return cyn_agent_add("@", agent_at_cb, 0);
+}
diff --git a/src/agent-at.h b/src/agent-at.h
new file mode 100644 (file)
index 0000000..7bbffc0
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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
+
+extern
+int
+agent_at_activate(
+);
index 995cd0a..d522c96 100644 (file)
@@ -456,9 +456,6 @@ anydb_test(
        if (s.score) {
                value->value = string(db, s.idxval);
                value->expire = s.expire;
-       } else {
-               value->value = NULL;
-               value->expire = 0;
        }
        return s.score;
 }
index e08c262..f853a53 100644 (file)
--- a/src/cyn.c
+++ b/src/cyn.c
@@ -22,7 +22,6 @@
 #include <string.h>
 #include <errno.h>
 
-
 #include "data.h"
 #include "db.h"
 #include "queue.h"
@@ -35,44 +34,36 @@ struct callback
                void *any_cb;
                on_enter_cb_t *on_enter_cb;
                on_change_cb_t *on_change_cb;
-               agent_cb_t *agent_cb;
        };
        void *closure;
 };
 
-struct async_check
+struct agent
 {
-       struct async_check *next;
-       on_result_cb_t *on_result_cb;
+       struct agent *next;
+       agent_cb_t *agent_cb;
        void *closure;
-       data_key_t key;
-       struct callback *next_agent;
+       uint8_t len;
+       char name[];
 };
 
 /** locking critical section */
 static const void *lock;
 static struct callback *awaiters;
 static struct callback *observers;
-static struct callback *agents;
-static struct async_check *asynchecks;
+static struct agent *agents;
 
 static
 int
 delcb(
        void *callback,
        void *closure,
-       struct callback **head,
-       struct async_check *achecks
+       struct callback **head
 ) {
        struct callback *c;
 
        while((c = *head)) {
                if (c->any_cb == callback && c->closure == closure) {
-                       while (achecks) {
-                               if (achecks->next_agent == c)
-                                       achecks->next_agent = c->next;
-                               achecks = achecks->next;
-                       }
                        *head = c->next;
                        free(c);
                        return 1;
@@ -130,7 +121,7 @@ cyn_enter_async_cancel(
        on_enter_cb_t *enter_cb,
        void *closure
 ) {
-       return delcb(enter_cb, closure, &awaiters, 0);
+       return delcb(enter_cb, closure, &awaiters);
 }
 
 int
@@ -146,7 +137,7 @@ cyn_on_change_remove(
        on_change_cb_t *on_change_cb,
        void *closure
 ) {
-       return delcb(on_change_cb, closure, &observers, 0);
+       return delcb(on_change_cb, closure, &observers);
 }
 
 /** leave critical recoverable section */
@@ -229,19 +220,63 @@ cyn_list(
        db_for_all(closure, callback, key);
 }
 
-int
-cyn_test(
-       const data_key_t *key,
+static
+data_value_t *
+default_value(
        data_value_t *value
 ) {
-       int rc;
+       value->value = DEFAULT;
+       value->expire = 0;
+       return value;
+}
 
-       rc = db_test(key, value);
-       if (rc <= 0) {
-               value->value = DEFAULT;
-               value->expire = 0;
-       }
-       return rc;
+static
+struct agent *
+search_agent(
+       const char *name,
+       uint8_t len,
+       struct agent ***ppprev
+) {
+       struct agent *it, **pprev;
+
+       pprev = &agents;
+       while((it = *pprev)
+         &&  (len != it->len || memcmp(it->name, name, (size_t)len)))
+               pprev = &it->next;
+       *ppprev = pprev;
+       return it;
+}
+
+static
+struct agent *
+required_agent(
+       const char *value
+) {
+       struct agent **pprev;
+       uint8_t len;
+
+       for (len = 0 ; len < UINT8_MAX && value[len] ; len++)
+               if (value[len] == ':')
+                       return search_agent(value, len, &pprev);
+       return 0;
+}
+
+struct async_check
+{
+       on_result_cb_t *on_result_cb;
+       void *closure;
+       data_key_t key;
+       data_value_t value;
+       int decount;
+};
+
+static
+void
+async_reply(
+       struct async_check *achk
+) {
+       achk->on_result_cb(achk->closure, &achk->value);
+       free(achk);
 }
 
 static
@@ -249,61 +284,76 @@ void
 async_on_result(
        void *closure,
        const data_value_t *value
-) {
-       struct async_check *achk = closure, **pac;
-       struct callback *agent;
-       int rc;
-       data_value_t v;
-
-       if (!value) {
-               agent = achk->next_agent;
-               while (agent) {
-                       achk->next_agent = agent->next;
-                       rc = agent->agent_cb(
-                               agent->closure,
-                               &achk->key,
-                               async_on_result,
-                               closure);
-                       if (!rc)
-                               return;
-                       agent = achk->next_agent;
-               }
-               v.value = DEFAULT;
-               v.expire = 0;
-               value = &v;
-       }
+);
 
-       achk->on_result_cb(achk->closure, value);
-       pac = &asynchecks;
-       while (*pac != achk)
-               pac = &(*pac)->next;
-       *pac = achk->next;
-       free(achk);
+static
+void
+async_call_agent(
+       struct agent *agent,
+       struct async_check *achk
+) {
+       int rc = agent->agent_cb(
+                       agent->name,
+                       agent->closure,
+                       &achk->key,
+                       &achk->value.value[agent->len + 1],
+                       async_on_result,
+                       achk);
+       if (rc < 0)
+               async_reply(achk);
 }
 
+static
+void
+async_on_result(
+       void *closure,
+       const data_value_t *value
+) {
+       struct async_check *achk = closure;
+       struct agent *agent;
+
+       achk->value = *value;
+       agent = required_agent(value->value);
+       if (agent && achk->decount) {
+               achk->decount--;
+               async_call_agent(agent, achk);
+       } else
+               async_reply(achk);
+}
 
+static
 int
-cyn_check_async(
+async_check_or_test(
        on_result_cb_t *on_result_cb,
        void *closure,
-       const data_key_t *key
+       const data_key_t *key,
+       int agentdeep
 ) {
+       int rc;
        data_value_t value;
        size_t szcli, szses, szuse, szper;
        struct async_check *achk;
+       struct agent *agent;
        void *ptr;
 
-       cyn_test(key, &value);
-       if (!strcmp(value.value, ALLOW) || !strcmp(value.value, DENY)) {
+       /* get the direct value */
+       rc = db_test(key, &value);
+
+       /* on error or missing result */
+       if (rc <= 0) {
+               default_value(&value);
                on_result_cb(closure, &value);
-               return 0;
+               return rc;
        }
 
-       if (!agents) {
+       /* if not an agent or agent not required */
+       agent = required_agent(value.value);
+       if (!agent || !agentdeep) {
                on_result_cb(closure, &value);
-               return 0;
+               return rc;
        }
 
+       /* allocate asynchronous query */
        szcli = key->client ? 1 + strlen(key->client) : 0;
        szses = key->session ? 1 + strlen(key->session) : 0;
        szuse = key->user ? 1 + strlen(key->user) : 0;
@@ -314,56 +364,111 @@ cyn_check_async(
                return -ENOMEM;
        }
 
-       ptr = achk;
+       /* init the structure */
+       ptr = &achk[1];
        achk->on_result_cb = on_result_cb;
        achk->closure = closure;
        if (!key->client)
                achk->key.client = 0;
        else {
                achk->key.client = ptr;
-               memcpy(ptr, key->client, szcli);
-               ptr += szcli;
+               ptr = mempcpy(ptr, key->client, szcli);
        }
        if (!key->session)
                achk->key.session = 0;
        else {
                achk->key.session = ptr;
-               memcpy(ptr, key->session, szses);
-               ptr += szses;
+               ptr = mempcpy(ptr, key->session, szses);
        }
        if (!key->user)
                achk->key.user = 0;
        else {
                achk->key.user = ptr;
-               memcpy(ptr, key->user, szuse);
-               ptr += szuse;
+               ptr = mempcpy(ptr, key->user, szuse);
        }
        if (!key->permission)
                achk->key.permission = 0;
        else {
                achk->key.permission = ptr;
-               memcpy(ptr, key->permission, szper);
+               ptr = mempcpy(ptr, key->permission, szper);
        }
-       achk->next_agent = agents;
-       achk->next = asynchecks;
-       asynchecks = achk;
-       async_on_result(achk, 0);
+       achk->value = value;
+       achk->decount = agentdeep;
+
+       /* call the agent */
+       async_call_agent(agent, achk);
        return 0;
 }
 
+int
+cyn_test_async(
+       on_result_cb_t *on_result_cb,
+       void *closure,
+       const data_key_t *key
+) {
+       return async_check_or_test(on_result_cb, closure, key, 0);
+}
+
+int
+cyn_check_async(
+       on_result_cb_t *on_result_cb,
+       void *closure,
+       const data_key_t *key
+) {
+       return async_check_or_test(on_result_cb, closure, key, 10);
+}
+
 int
 cyn_agent_add(
+       const char *name,
        agent_cb_t *agent_cb,
        void *closure
 ) {
-       return addcb(agent_cb, closure, &agents);
+       struct agent *agent, **pprev;
+       size_t length;
+       uint8_t len;
+
+       length = strlen(name);
+       if (length <= 0 || length > UINT8_MAX)
+               return -EINVAL;
+       len = (uint8_t)length++;
+
+       agent = search_agent(name, len, &pprev);
+       if (agent)
+               return -EEXIST;
+
+       agent = malloc(sizeof *agent + length);
+       if (!agent)
+               return -ENOMEM;
+
+       agent->next = 0;
+       agent->agent_cb = agent_cb;
+       agent->closure = closure;
+       agent->len = len;
+       memcpy(agent->name, name, length);
+       *pprev = agent;
+
+       return 0;
 }
 
 int
 cyn_agent_remove(
-       agent_cb_t *agent_cb,
-       void *closure
+       const char *name
 ) {
-       return delcb(agent_cb, closure, &agents, asynchecks);
-}
+       struct agent *agent, **pprev;
+       size_t length;
+       uint8_t len;
 
+       length = strlen(name);
+       if (length <= 0 || length > UINT8_MAX)
+               return -EINVAL;
+       len = (uint8_t)length;
+
+       agent = search_agent(name, len, &pprev);
+       if (!agent)
+               return -ENOENT;
+
+       *pprev = agent->next;
+       free(agent);
+       return 0;
+}
index 8f8893a..2dbfa62 100644 (file)
--- a/src/cyn.h
+++ b/src/cyn.h
@@ -28,8 +28,10 @@ typedef void (list_cb_t)(
                const data_value_t *value);
 
 typedef int (agent_cb_t)(
+               const char *name,
                void *agent_closure,
                const data_key_t *key,
+               const char *value,
                on_result_cb_t *on_result_cb,
                void *on_result_closure);
 
@@ -89,13 +91,6 @@ cyn_drop(
        const data_key_t *key
 );
 
-extern
-int
-cyn_test(
-       const data_key_t *key,
-       data_value_t *value
-);
-
 extern
 void
 cyn_list(
@@ -104,6 +99,13 @@ cyn_list(
        const data_key_t *key
 );
 
+int
+cyn_test_async(
+       on_result_cb_t *on_result_cb,
+       void *closure,
+       const data_key_t *key
+);
+
 extern
 int
 cyn_check_async(
@@ -115,6 +117,7 @@ cyn_check_async(
 extern
 int
 cyn_agent_add(
+       const char *name,
        agent_cb_t *agent_cb,
        void *closure
 );
@@ -122,7 +125,6 @@ cyn_agent_add(
 extern
 int
 cyn_agent_remove(
-       agent_cb_t *agent_cb,
-       void *closure
+       const char *name
 );
 
index a8607b4..53e722a 100644 (file)
@@ -370,6 +370,8 @@ int do_check(int ac, char **av, int (*f)(rcyn_t*,const rcyn_key_t*))
                        fprintf(stdout, "denied\n");
                else if (rc == -ENOENT && f == rcyn_cache_check)
                        fprintf(stdout, "not in cache!\n");
+               else if (rc == -EEXIST)
+                       fprintf(stderr, "denied but an entry exist\n");
                else
                        fprintf(stderr, "error %s\n", strerror(-rc));
        }
index 9c51df7..75b0169 100644 (file)
@@ -41,6 +41,7 @@
 #include "rcyn-server.h"
 #include "rcyn-protocol.h"
 #include "dbinit.h"
+#include "agent-at.h"
 
 #if !defined(DEFAULT_DB_DIR)
 #    define  DEFAULT_DB_DIR      "/var/lib/cynara"
@@ -234,6 +235,9 @@ int main(int ac, char **av)
        group = group ?: DEFAULT_CYNARA_GROUP;
        init = init ?: DEFAULT_INIT_FILE;
 
+       /* activate the agents */
+       agent_at_activate();
+
        /* compute socket specs */
        spec_socket_admin = spec_socket_check = spec_socket_agent = 0;
        if (systemd) {
index 711d084..06edb2f 100644 (file)
@@ -99,7 +99,6 @@ buf_put_car(
        return 0;
 }
 
-
 /**
  * Put the 'string' into the 'buf' escaping it at need
  * returns:
index 14a9243..951e655 100644 (file)
@@ -159,8 +159,8 @@ putx(
        va_list l;
        int rc;
 
-       va_start(l, cli);
        n = 0;
+       va_start(l, cli);
        p = va_arg(l, const char *);
        while (p) {
                if (n == MAXARGS)
@@ -250,15 +250,16 @@ checkcb(
 ) {
        client_t *cli = closure;
        char text[30];
+       const char *etxt;
 
        if (!value)
                putx(cli, DEFAULT, "0", NULL);
        else {
-               exp2txt(value->expire, text, sizeof text);
+               etxt = exp2txt(value->expire, text, sizeof text);
                if (strcmp(value->value, ALLOW) && strcmp(value->value, DENY))
-                       putx(cli, _done_, value, text, NULL);
+                       putx(cli, _done_, value->value, etxt, NULL);
                else
-                       putx(cli, value->value, text, NULL);
+                       putx(cli, value->value, etxt, NULL);
        }
        flushw(cli);
 }