Rework of many things
authorJosé Bollo <jose.bollo@iot.bzh>
Tue, 5 Feb 2019 15:14:10 +0000 (16:14 +0100)
committerJose Bollo <jose.bollo@iot.bzh>
Mon, 29 Apr 2019 15:29:38 +0000 (17:29 +0200)
Change-Id: Iadeba6f0602f7be017244c2602fae2bbe2abf74e
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
32 files changed:
.gitignore
src/CMakeLists.txt
src/cache.c
src/cache.h
src/cyn.c
src/cyn.h
src/data.h [new file with mode: 0644]
src/db.c
src/db.h
src/dbinit.c [new file with mode: 0644]
src/dbinit.h [new file with mode: 0644]
src/expire.c [new file with mode: 0644]
src/expire.h [new file with mode: 0644]
src/fbuf.c
src/fbuf.h
src/lib-compat.c
src/main-cynadm.c
src/main-cynarad.c
src/pollitem.c [new file with mode: 0644]
src/pollitem.h [new file with mode: 0644]
src/prot.c
src/prot.h
src/queue.c
src/queue.h
src/rcyn-client.c
src/rcyn-client.h
src/rcyn-protocol.c
src/rcyn-protocol.h
src/rcyn-protocol.txt
src/rcyn-server.c
src/rcyn-server.h
src/socket.c

index ac33634..e379367 100644 (file)
@@ -1,2 +1,3 @@
 build/
 .*.sw*
+nbproject/
index 549d401..cacd659 100644 (file)
 set(SERVER_SOURCES
        cyn.c
        db.c
+       dbinit.c
+       expire.c
        fbuf.c
        main-cynarad.c
+       pollitem.c
        prot.c
        queue.c
        rcyn-protocol.c
@@ -37,20 +40,7 @@ set(LIB_SOURCES
        socket.c
 )
 
-###########################################
-# build and install cynarad
-###########################################
-add_executable(cynarad ${SERVER_SOURCES})
-target_compile_definitions(cynarad PRIVATE
-       DEFAULT_DB_DIR="${DEFAULT_DB_DIR}"
-       DEFAULT_SOCKET_DIR="${DEFAULT_SOCKET_DIR}"
-       DEFAULT_INIT_FILE="${DEFAULT_INIT_FILE}"
-       RCYN_DEFAULT_CHECK_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.check"
-       RCYN_DEFAULT_ADMIN_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.admin"
-)
-target_link_libraries(cynarad cap)
-install(TARGETS cynarad
-        RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
+add_compile_definitions(_GNU_SOURCE)
 
 ###########################################
 # build and install libcynara
@@ -71,10 +61,25 @@ TARGET_LINK_LIBRARIES(cynara
 INSTALL(TARGETS cynara LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR})
 INSTALL(FILES rcyn-client.h DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/cynara)
 
+###########################################
+# build and install cynarad
+###########################################
+add_executable(cynarad ${SERVER_SOURCES})
+target_compile_definitions(cynarad PRIVATE
+       DEFAULT_DB_DIR="${DEFAULT_DB_DIR}"
+       DEFAULT_SOCKET_DIR="${DEFAULT_SOCKET_DIR}"
+       DEFAULT_INIT_FILE="${DEFAULT_INIT_FILE}"
+       RCYN_DEFAULT_CHECK_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.check"
+       RCYN_DEFAULT_ADMIN_SOCKET_SPEC="unix:${DEFAULT_SOCKET_DIR}/cynara.admin"
+)
+target_link_libraries(cynarad cap)
+install(TARGETS cynarad
+        RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
+
 ###########################################
 # build and install cynadm
 ###########################################
-ADD_EXECUTABLE(cynadm main-cynadm.c)
+ADD_EXECUTABLE(cynadm main-cynadm.c expire.c)
 TARGET_LINK_LIBRARIES(cynadm cynara)
 INSTALL(TARGETS cynadm
         RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
index 9bbe667..b57536d 100644 (file)
@@ -17,6 +17,7 @@
 
 
 #include <stdlib.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdalign.h>
 #include <string.h>
@@ -24,6 +25,7 @@
 #include <time.h>
 #include <ctype.h>
 
+#include "rcyn-client.h"
 #include "cache.h"
 
 /**
@@ -34,7 +36,7 @@
  *  - session: zero  terminated string
  *  - user: zero  terminated string
  *  - permission: zero  terminated string
- *  - 
+ *  -
  */
 struct item
 {
@@ -166,18 +168,15 @@ static
 int
 match(
        const char *head,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 ) {
-       head = cmp(head, client);
+       head = cmp(head, key->client);
        if (head)
-               head = cmp(head, session);
+               head = cmp(head, key->session);
        if (head)
-               head = cmp(head, user);
+               head = cmp(head, key->user);
        if (head)
-               head = cmpi(head, permission);
+               head = cmpi(head, key->permission);
        return !!head;
 }
 
@@ -185,10 +184,7 @@ static
 item_t*
 search(
        cache_t *cache,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 ) {
        time_t now;
        item_t *item, *found;
@@ -202,7 +198,7 @@ search(
                if (item->expire && item->expire < now)
                        drop_at(cache, iter);
                else {
-                       if (match(&item->strings, client, session, user, permission))
+                       if (match(&item->strings, key))
                                found = item;
                        iter += item->length;
                }
@@ -213,10 +209,7 @@ search(
 int
 cache_put(
        cache_t *cache,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
+       const rcyn_key_t *key,
        int value,
        time_t expire
 ) {
@@ -227,14 +220,14 @@ cache_put(
        if (cache == NULL || value < -128 || value > 127)
                return -EINVAL;
 
-       item = search(cache, client, session, user, permission);
+       item = search(cache, key);
        if (item == NULL) {
                /* create an item */
                size = (size_t)(&((item_t*)0)->strings)
-                       + strlen(client)
-                       + strlen(session)
-                       + strlen(user)
-                       + strlen(permission);
+                       + strlen(key->client)
+                       + strlen(key->session)
+                       + strlen(key->user)
+                       + strlen(key->permission);
                size = (size + alignof(item_t) - 1) & ~(alignof(item_t) - 1);
                if (size > 65535)
                        return -EINVAL;
@@ -245,7 +238,7 @@ cache_put(
                        drop_lre(cache);
                item = itemat(cache, cache->used);
                item->length = length;
-               stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy(&item->strings, client), session), user), permission);
+               stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy(&item->strings, key->client), key->session), key->user), key->permission);
                cache->used += (uint32_t)size;
        }
        item->expire = expire;
@@ -257,14 +250,11 @@ cache_put(
 int
 cache_search(
        cache_t *cache,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 ) {
        item_t *item;
 
-       item = search(cache, client, session, user, permission);
+       item = search(cache, key);
        if (item) {
                hit(cache, item);
                return (int)item->value;
index bd56601..bf85e8e 100644 (file)
@@ -24,20 +24,14 @@ extern
 int
 cache_search(
        cache_t *cache,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 );
 
 extern
 int
 cache_put(
        cache_t *cache,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
+       const rcyn_key_t *key,
        int value,
        time_t expire
 );
index 914bf52..6f27860 100644 (file)
--- a/src/cyn.c
+++ b/src/cyn.c
@@ -15,9 +15,6 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
-
-
 #include <assert.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -26,6 +23,7 @@
 #include <errno.h>
 
 
+#include "data.h"
 #include "db.h"
 #include "queue.h"
 #include "cyn.h"
 struct callback
 {
        struct callback *next;
-       void (*callback)(void *closure);
+       union {
+               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 async_check *next;
+       on_result_cb_t *on_result_cb;
+       void *closure;
+       data_key_t key;
+       struct callback *next_agent;
+};
+
 /** 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
 int
 delcb(
-       void (*callback)(void *closure),
+       void *callback,
        void *closure,
-       struct callback **head
+       struct callback **head,
+       struct async_check *achecks
 ) {
        struct callback *c;
 
        while((c = *head)) {
-               if (c->callback == callback && c->closure == closure) {
+               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;
@@ -65,7 +85,7 @@ delcb(
 static
 int
 addcb(
-       void (*callback)(void *closure),
+       void *callback,
        void *closure,
        struct callback **head
 ) {
@@ -74,27 +94,13 @@ addcb(
        c = malloc(sizeof *c);
        if (c == NULL)
                return -(errno = ENOMEM);
-       c->callback = callback;
+       c->any_cb = callback;
        c->closure = closure;
        c->next = *head;
        *head = c;
        return 0;
 }
 
-static
-int
-changed(
-) {
-       int rc;
-       struct callback *c;
-
-       db_cleanup(0);
-       rc = db_sync();
-       for (c = observers; c ; c = c->next)
-               c->callback(c->closure);
-       return rc;
-}
-
 /** enter critical recoverable section */
 int
 cyn_enter(
@@ -108,7 +114,7 @@ cyn_enter(
 
 int
 cyn_enter_async(
-       void (*enter_cb)(void *closure),
+       on_enter_cb_t *enter_cb,
        void *closure
 ) {
        if (lock)
@@ -121,27 +127,26 @@ cyn_enter_async(
 
 int
 cyn_enter_async_cancel(
-       void (*enter_cb)(void *closure),
+       on_enter_cb_t *enter_cb,
        void *closure
 ) {
-       return delcb(enter_cb, closure, &awaiters);
+       return delcb(enter_cb, closure, &awaiters, 0);
 }
 
 int
 cyn_on_change_add(
-       void (*on_change_cb)(void *closure),
+       on_change_cb_t *on_change_cb,
        void *closure
 ) {
        return addcb(on_change_cb, closure, &observers);
 }
 
-
 int
 cyn_on_change_remove(
-       void (*on_change_cb)(void *closure),
+       on_change_cb_t *on_change_cb,
        void *closure
 ) {
-       return delcb(on_change_cb, closure, &observers);
+       return delcb(on_change_cb, closure, &observers, 0);
 }
 
 /** leave critical recoverable section */
@@ -151,7 +156,7 @@ cyn_leave(
        bool commit
 ) {
        int rc;
-       struct callback *e, **p;
+       struct callback *c, *e, **p;
 
        if (!magic)
                return -EINVAL;
@@ -164,12 +169,18 @@ cyn_leave(
        if (!commit)
                rc = 0;
        else {
+               db_backup();
                rc = queue_play();
-               if (rc < 0)
+               if (rc == 0) {
+                       db_cleanup(0);
+                       rc = db_sync();
+               }
+               if (rc < 0) {
                        db_recover();
-               else {
-                       rc = db_backup();
-                       changed();
+                       db_sync();
+               } else {
+                       for (c = observers; c ; c = c->next)
+                               c->on_change_cb(c->closure);
                }
        }
        queue_clear();
@@ -185,7 +196,7 @@ cyn_leave(
                }
                *p = NULL;
                lock = e->closure;
-               e->callback(e->closure);
+               e->on_enter_cb(e->closure);
                free(e);
        }
 
@@ -194,89 +205,169 @@ cyn_leave(
 
 int
 cyn_set(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const data_key_t *key,
+       const data_value_t *value
 ) {
        if (!lock)
                return -EPERM;
-       return queue_set(client, session, user, permission, value, expire);
+       return queue_set(key, value);
 }
 
 int
 cyn_drop(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const data_key_t *key
 ) {
        if (!lock)
                return -EPERM;
-       return queue_drop(client, session, user, permission);
+       return queue_drop(key);
 }
 
 void
 cyn_list(
        void *closure,
-       void (*callback)(
-               void *closure,
-               const char *client,
-               const char *session,
-               const char *user,
-               const char *permission,
-               const char *value,
-               time_t expire),
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       list_cb_t *callback,
+       const data_key_t *key
 ) {
-       db_for_all(closure, callback, client, session, user, permission);
+       db_for_all(closure, callback, key);
 }
 
 int
 cyn_test(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char **value,
-       time_t *expire
+       const data_key_t *key,
+       data_value_t *value
 ) {
        int rc;
 
-       rc = db_test(client, session, user, permission, value, expire);
-       if (rc <= 0)
-               *value = DEFAULT;
-       else
+       rc = db_test(key, value);
+       if (rc > 0)
                rc = 0;
+       else {
+               value->value = DEFAULT;
+               value->expire = 0;
+       }
        return rc;
 }
 
+static
+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);
+}
+
+
 int
 cyn_check_async(
-       void (*check_cb)(void *closure, const char *value, time_t expire),
+       on_result_cb_t *on_result_cb,
        void *closure,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const data_key_t *key
 ) {
-       const char *value;
-       time_t expire;
+       data_value_t value;
+       size_t szcli, szses, szuse, szper;
+       struct async_check *achk;
+       void *ptr;
+
+       cyn_test(key, &value);
+       if (!strcmp(value.value, ALLOW) || !strcmp(value.value, DENY)) {
+               on_result_cb(closure, &value);
+               return 0;
+       }
 
-       cyn_test(client, session, user, permission, &value, &expire);
-       if (!strcmp(value, ALLOW) || !strcmp(value, DENY)) {
-               check_cb(closure, value, expire);
+       if (!agents) {
+               on_result_cb(closure, &value);
                return 0;
        }
 
-       /* TODO: try to resolve AGENT?? */
+       szcli = key->client ? 1 + strlen(key->client) : 0;
+       szses = key->session ? 1 + strlen(key->session) : 0;
+       szuse = key->user ? 1 + strlen(key->user) : 0;
+       szper = key->permission ? 1 + strlen(key->permission) : 0;
+       achk = malloc(szcli + szses + szuse + szper + sizeof *achk);
+       if (!achk) {
+               on_result_cb(closure, &value);
+               return -ENOMEM;
+       }
 
-       check_cb(closure, value, expire);
+       ptr = achk;
+       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;
+       }
+       if (!key->session)
+               achk->key.session = 0;
+       else {
+               achk->key.session = ptr;
+               memcpy(ptr, key->session, szses);
+               ptr += szses;
+       }
+       if (!key->user)
+               achk->key.user = 0;
+       else {
+               achk->key.user = ptr;
+               memcpy(ptr, key->user, szuse);
+               ptr += szuse;
+       }
+       if (!key->permission)
+               achk->key.permission = 0;
+       else {
+               achk->key.permission = ptr;
+               memcpy(ptr, key->permission, szper);
+       }
+       achk->next_agent = agents;
+       achk->next = asynchecks;
+       asynchecks = achk;
+       async_on_result(achk, 0);
        return 0;
 }
 
+int
+cyn_agent_add(
+       agent_cb_t *agent_cb,
+       void *closure
+) {
+       return addcb(agent_cb, closure, &agents);
+}
+
+int
+cyn_agent_remove(
+       agent_cb_t *agent_cb,
+       void *closure
+) {
+       return delcb(agent_cb, closure, &agents, asynchecks);
+}
+
index 379deef..8f8893a 100644 (file)
--- a/src/cyn.h
+++ b/src/cyn.h
 
 #pragma once
 
-#define DENY    "no"
-#define ALLOW   "yes"
-#define ASK     "ask"
-#define DEFAULT DENY
+typedef void (on_enter_cb_t)(void *closure);
+typedef void (on_change_cb_t)(void *closure);
+typedef void (on_result_cb_t)(void *closure, const data_value_t *value);
+
+typedef void (list_cb_t)(
+               void *closure,
+               const data_key_t *key,
+               const data_value_t *value);
+
+typedef int (agent_cb_t)(
+               void *agent_closure,
+               const data_key_t *key,
+               on_result_cb_t *on_result_cb,
+               void *on_result_closure);
 
 /** enter critical recoverable section */
 extern
@@ -40,89 +50,79 @@ cyn_leave(
 
 extern
 int
-cyn_set(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+cyn_enter_async(
+       on_enter_cb_t *enter_cb,
+       void *closure
 );
 
 extern
 int
-cyn_drop(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+cyn_enter_async_cancel(
+       on_enter_cb_t *enter_cb,
+       void *closure
 );
 
 extern
 int
-cyn_test(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char **value,
-       time_t *expire
+cyn_on_change_add(
+       on_change_cb_t *on_change_cb,
+       void *closure
 );
 
 extern
-void
-cyn_list(
-       void *closure,
-       void (*callback)(
-               void *closure,
-               const char *client,
-               const char *session,
-               const char *user,
-               const char *permission,
-               const char *value,
-               time_t expire),
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+int
+cyn_on_change_remove(
+       on_change_cb_t *on_change_cb,
+       void *closure
 );
 
 extern
 int
-cyn_check_async(
-       void (*check_cb)(void *closure, const char *value, time_t expire),
-       void *closure,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+cyn_set(
+       const data_key_t *key,
+       const data_value_t *value
 );
 
 extern
 int
-cyn_enter_async(
-       void (*enter_cb)(void *closure),
-       void *closure
+cyn_drop(
+       const data_key_t *key
 );
 
 extern
 int
-cyn_enter_async_cancel(
-       void (*enter_cb)(void *closure),
-       void *closure
+cyn_test(
+       const data_key_t *key,
+       data_value_t *value
+);
+
+extern
+void
+cyn_list(
+       void *closure,
+       list_cb_t *callback,
+       const data_key_t *key
 );
 
 extern
 int
-cyn_on_change_add(
-       void (*on_change_cb)(void *closure),
+cyn_check_async(
+       on_result_cb_t *on_result_cb,
+       void *closure,
+       const data_key_t *key
+);
+
+extern
+int
+cyn_agent_add(
+       agent_cb_t *agent_cb,
        void *closure
 );
 
 extern
 int
-cyn_on_change_remove(
-       void (*on_change_cb)(void *closure),
+cyn_agent_remove(
+       agent_cb_t *agent_cb,
        void *closure
 );
 
diff --git a/src/data.h b/src/data.h
new file mode 100644 (file)
index 0000000..10cc599
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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
+
+#define DENY    "no"
+#define ALLOW   "yes"
+#define ASK     "ask"
+#define DEFAULT DENY
+
+typedef struct data_key data_key_t;
+typedef struct data_value data_value_t;
+
+struct data_key {
+       const char *client;
+       const char *session;
+       const char *user;
+       const char *permission;
+};
+
+struct data_value {
+       const char *value;
+       time_t expire;
+};
+
index f77b76f..111a4f4 100644 (file)
--- a/src/db.c
+++ b/src/db.c
  * limitations under the License.
  */
 
-
-#define _GNU_SOURCE
-
-
 #include <assert.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdalign.h>
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 #include <time.h>
 #include <errno.h>
 
+#include "data.h"
 #include "fbuf.h"
 #include "db.h"
 
 #define time2exph(x) time2expl((x) + 15)
 
 /**
- * A rule is a set of 32 bits integers
+ * A query is a set of 32 bits integers
  */
-struct rule
-{
-       union {
-               uint32_t ids[5];
-               struct {
-                       /** client string id */
-                       uint32_t client;
+struct key_ids {
+       /** client string id */
+       uint32_t client;
 
-                       /** session string id */
-                       uint32_t session;
+       /** session string id */
+       uint32_t session;
 
-                       /** user string id */
-                       uint32_t user;
+       /** user string id */
+       uint32_t user;
 
-                       /** permission string id */
-                       uint32_t permission;
+       /** permission string id */
+       uint32_t permission;
+};
+typedef struct key_ids key_ids_t;
+
+/**
+ * A rule is a set of 32 bits integers
+ */
+struct rule
+{
+       /** key part */
+       key_ids_t key;
 
-                       /** value string id */
-                       uint32_t value;
-               };
-       };
+       /** value string id */
+       uint32_t value;
 
        /**  expiration */
        uint32_t expire;
@@ -98,10 +100,16 @@ static fbuf_t fnames;
 /** the file for the rules */
 static fbuf_t frules;
 
-/** identification of names version 1 (uuidgen --sha1 -n @url -N urn:AGL:cynara:db:names:1) */
+/** identification of names version 1
+ *    $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:names:1
+ *    $> uuid -v 5 urn:AGL:cynara:db:names:1
+ */
 static const char uuid_names_v1[] = "e9481f9e-b2f4-5716-90cf-c286d98d1868\n--\n";
 
-/** identification of rules version 1 (uuidgen --sha1 -n @url -N urn:AGL:cynara:db:rules:1) */
+/** identification of rules version 1
+ *    $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:rules:1
+ *    $> uuid -v 5 ns:URL urn:AGL:cynara:db:rules:1
+ */
 static const char uuid_rules_v1[] = "8f7a5b21-48b1-57af-96c9-d5d7192be370\n--\n";
 
 /** length of the identification */
@@ -357,10 +365,10 @@ add_rule(
                return rc;
        rules = (rule_t*)(frules.buffer + uuidlen);
        rule = &rules[rules_count++];
-       rule->client = client;
-       rule->session = session;
-       rule->user = user;
-       rule->permission = permission;
+       rule->key.client = client;
+       rule->key.session = session;
+       rule->key.user = user;
+       rule->key.permission = permission;
        rule->value = value;
        rule->expire = expire;
        frules.used = c;
@@ -388,18 +396,23 @@ open_identify(
 ) {
        int rc;
        char *file, *backup;
+       size_t sd, sn;
 
-       rc = asprintf(&file, "%s/%s", directory, name);
-       if (rc < 0)
+       sd = strlen(directory);
+       sn = strlen(name);
+       file = malloc(((sd + sn) << 1) + 5);
+       if (!file)
                rc = -ENOMEM;
        else {
-               rc = asprintf(&backup, "%s~", file);
-               if (rc < 0)
-                       rc = -ENOMEM;
-               else {
-                       rc = fbuf_open_identify(fb, file, backup, id, idlen);
-                       free(backup);
-               }
+
+               memcpy(file, directory, sd);
+               file[sd] = '/';
+               memcpy(&file[sd + 1], name, sn + 1);
+               backup = &file[sd + sn + 2];
+               memcpy(backup, file, sd + sn + 1);
+               backup[sd + sn + 1] = '~';
+               backup[sd + sn + 2] = 0;
+               rc = fbuf_open_identify(fb, file, backup, id, idlen);
                free(file);
        }
        return rc;
@@ -502,49 +515,62 @@ db_recover(
        init_rules();
        return 0;
 error:
-       fprintf(stderr, "db recovering impossible: %m");
+       fprintf(stderr, "db recovery impossible: %m");
        exit(5);
        return rc;
 }
 
+static int get_query_ids(
+       const data_key_t *in,
+       key_ids_t *out,
+       bool create
+) {
+       int rc;
+
+       rc = db_get_name_index(&out->client, in->client, create);
+       if (rc) goto end;
+       rc = db_get_name_index(&out->session, in->session, create);
+       if (rc) goto end;
+       rc = db_get_name_index(&out->user, in->user, create);
+       if (rc) goto end;
+       rc = db_get_name_index(&out->permission, in->permission, create);
+end:
+       return rc;
+}
+
 /** enumerate */
 void
 db_for_all(
        void *closure,
        void (*callback)(
                void *closure,
-               const char *client,
-               const char *session,
-               const char *user,
-               const char *permission,
-               const char *value,
-               time_t expire),
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+               const data_key_t *key,
+               const data_value_t *value),
+       const data_key_t *key
 ) {
        uint32_t ucli, uusr, uses, i;
-       bool anyperm;
+       int anyperm;
+       data_key_t k;
+       data_value_t v;
 
-       if (db_get_name_index(&ucli, client, false)
-        || db_get_name_index(&uses, session, false)
-        || db_get_name_index(&uusr, user, false))
+       if (db_get_name_index(&ucli, key->client, false)
+        || db_get_name_index(&uses, key->session, false)
+        || db_get_name_index(&uusr, key->user, false))
                return; /* nothing to do! */
 
-       anyperm = is_any(permission);
+       anyperm = is_any(key->permission);
        for (i = 0; i < rules_count; i++) {
-               if ((ucli == ANYIDX || ucli == rules[i].client)
-                && (uses == ANYIDX || uses == rules[i].session)
-                && (uusr == ANYIDX || uusr == rules[i].user)
-                && (anyperm || !strcasecmp(permission, name_at(rules[i].permission)))) {
-                       callback(closure,
-                               name_at(rules[i].client),
-                               name_at(rules[i].session),
-                               name_at(rules[i].user),
-                               name_at(rules[i].permission),
-                               name_at(rules[i].value),
-                               exp2time(rules[i].expire));
+               if ((ucli == ANYIDX || ucli == rules[i].key.client)
+                && (uses == ANYIDX || uses == rules[i].key.session)
+                && (uusr == ANYIDX || uusr == rules[i].key.user)
+                && (anyperm || !strcasecmp(key->permission, name_at(rules[i].key.permission)))) {
+                       k.client = name_at(rules[i].key.client);
+                       k.session = name_at(rules[i].key.session);
+                       k.user = name_at(rules[i].key.user);
+                       k.permission = name_at(rules[i].key.permission);
+                       v.value = name_at(rules[i].value);
+                       v.expire = exp2time(rules[i].expire);
+                       callback(closure, &k, &v);
                }
        }
 }
@@ -552,26 +578,23 @@ db_for_all(
 /** drop rules */
 int
 db_drop(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const data_key_t *key
 ) {
        uint32_t ucli, uses, uusr, i;
        bool anyperm;
 
-       if (db_get_name_index(&ucli, client, false)
-        || db_get_name_index(&uses, session, false)
-        || db_get_name_index(&uusr, user, false))
+       if (db_get_name_index(&ucli, key->client, false)
+        || db_get_name_index(&uses, key->session, false)
+        || db_get_name_index(&uusr, key->user, false))
                return 0; /* nothing to do! */
 
-       anyperm = is_any(permission);
+       anyperm = is_any(key->permission);
        i = 0;
        while (i < rules_count) {
-               if ((ucli == ANYIDX || ucli == rules[i].client)
-                && (uses == ANYIDX || uses == rules[i].session)
-                && (uusr == ANYIDX || uusr == rules[i].user)
-                && (anyperm || !strcasecmp(permission, name_at(rules[i].permission))))
+               if ((ucli == ANYIDX || ucli == rules[i].key.client)
+                && (uses == ANYIDX || uses == rules[i].key.session)
+                && (uusr == ANYIDX || uusr == rules[i].key.user)
+                && (anyperm || !strcasecmp(key->permission, name_at(rules[i].key.permission))))
                        drop_at(i);
                else
                        i++;
@@ -582,54 +605,47 @@ db_drop(
 /** set rules */
 int
 db_set(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const data_key_t *key,
+       const data_value_t *value
 ) {
        int rc;
        uint32_t ucli, uses, uusr, uperm, uval, i;
+       const char *perm;
 
-       /* normalize */
-       client = is_any_or_wide(client) ? WIDESTR : client;
-       session = is_any_or_wide(session) ? WIDESTR : session;
-       user = is_any_or_wide(user) ? WIDESTR : user;
-       permission = is_any_or_wide(permission) ? WIDESTR : permission;
+       perm = is_any_or_wide(key->permission) ? WIDESTR : key->permission;
 
        /* get/create strings */
-       rc = db_get_name_index(&ucli, client, true);
+       rc = db_get_name_index(&ucli, is_any_or_wide(key->client) ? WIDESTR : key->client, true);
        if (rc)
                goto error;
-       rc = db_get_name_index(&uses, session, true);
+       rc = db_get_name_index(&uses, is_any_or_wide(key->session) ? WIDESTR : key->session, true);
        if (rc)
                goto error;
-       rc = db_get_name_index(&uusr, user, true);
+       rc = db_get_name_index(&uusr, is_any_or_wide(key->user) ? WIDESTR : key->user, true);
        if (rc)
                goto error;
-       rc = db_get_name_index(&uval, value, true);
+       rc = db_get_name_index(&uval, value->value, true);
        if (rc)
                goto error;
 
        /* search the existing rule */
        for (i = 0; i < rules_count; i++) {
-               if (ucli == rules[i].client
-                && uses == rules[i].session
-                && uusr == rules[i].user
-                && !strcasecmp(permission, name_at(rules[i].permission))) {
+               if (ucli == rules[i].key.client
+                && uses == rules[i].key.session
+                && uusr == rules[i].key.user
+                && !strcasecmp(perm, name_at(rules[i].key.permission))) {
                        /* found */
-                       set_at(i, uval, time2exph(expire));
+                       set_at(i, uval, time2exph(value->expire));
                        return 0;
                }
        }
 
        /* create the rule */
-       rc = db_get_name_index(&uperm, permission, true);
+       rc = db_get_name_index(&uperm, perm, true);
        if (rc)
                goto error;
 
-       rc = add_rule(ucli, uses, uusr, uperm, uval, time2exph(expire));
+       rc = add_rule(ucli, uses, uusr, uperm, uval, time2exph(value->expire));
 
        return 0;
 error:
@@ -639,29 +655,21 @@ error:
 /** check rules */
 int
 db_test(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char **value,
-       time_t *expire
+       const data_key_t *key,
+       data_value_t *value
 ) {
+       const char *perm;
        uint32_t ucli, uses, uusr, i, score, sc, now;
        rule_t *rule, *found;
 
-       /* check */
-       client = is_any_or_wide(client) ? WIDESTR : client;
-       session = is_any_or_wide(session) ? WIDESTR : session;
-       user = is_any_or_wide(user) ? WIDESTR : user;
-       permission = is_any_or_wide(permission) ? WIDESTR : permission;
-
-       /* search the items */
-       if (db_get_name_index(&ucli, client, false))
+       /* normalize the items */
+       if (db_get_name_index(&ucli, is_any_or_wide(key->client) ? WIDESTR : key->client, false))
                ucli = NOIDX;
-       if (db_get_name_index(&uses, session, false))
+       if (db_get_name_index(&uses, is_any_or_wide(key->session) ? WIDESTR : key->session, false))
                uses = NOIDX;
-       if (db_get_name_index(&uusr, user, false))
+       if (db_get_name_index(&uusr, is_any_or_wide(key->user) ? WIDESTR : key->user, false))
                uusr = NOIDX;
+       perm = is_any_or_wide(key->permission) ? WIDESTR : key->permission;
 
        /* search the existing rule */
        now = time2expl(time(NULL));
@@ -670,14 +678,14 @@ db_test(
        for (i = 0 ; i < rules_count ; i++) {
                rule = &rules[i];
                if ((!rule->expire || rule->expire >= now)
-                && (ucli == rule->client || WIDEIDX == rule->client)
-                && (uses == rule->session || WIDEIDX == rule->session)
-                && (uusr == rule->user || WIDEIDX == rule->user)
-                && (WIDEIDX == rule->permission
-                       || !strcasecmp(permission, name_at(rule->permission)))) {
+                && (ucli == rule->key.client || WIDEIDX == rule->key.client)
+                && (uses == rule->key.session || WIDEIDX == rule->key.session)
+                && (uusr == rule->key.user || WIDEIDX == rule->key.user)
+                && (WIDEIDX == rule->key.permission
+                       || !strcasecmp(perm, name_at(rule->key.permission)))) {
                        /* found */
-                       sc = 1 + (rule->client != WIDEIDX) + (rule->session != WIDEIDX)
-                               + (rule->user != WIDEIDX) + (rule->permission != WIDEIDX);
+                       sc = 1 + (rule->key.client != WIDEIDX) + (rule->key.session != WIDEIDX)
+                               + (rule->key.user != WIDEIDX) + (rule->key.permission != WIDEIDX);
                        if (sc > score) {
                                score = sc;
                                found = rule;
@@ -685,12 +693,13 @@ db_test(
                }
        }
        if (!found) {
-               *value = NULL;
-               *expire = 0;
+               value->value = NULL;
+               value->expire = 0;
                return 0;
        }
-       *value = name_at(found->value);
-       *expire = exp2time(found->expire);
+       value->value = name_at(found->value);
+       value->expire = exp2time(found->expire);
+
        return 1;
 }
 
@@ -713,26 +722,47 @@ cmpidxs(
        return a < b ? -1 : a != b;
 }
 
+static
+uint32_t*
+gc_after_ptr(
+       gc_t *gc,
+       uint32_t *idx
+) {
+       uint32_t *p = bsearch(idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs);
+       assert(p != NULL);
+       return &gc->afters[p - gc->befores];
+}
+
 static
 void
 gc_mark(
+       gc_t *gc,
+       uint32_t *idx
+) {
+       *gc_after_ptr(gc, idx) = 1;
+}
+
+static
+void
+gc_mark_id(
        gc_t *gc,
        uint32_t idx
 ) {
-       uint32_t *p = bsearch(&idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs);
-       assert(p != NULL);
-       gc->afters[p - gc->befores] = 1;
+       gc_mark(gc, &idx);
 }
 
 static
-uint32_t
+int
 gc_after(
        gc_t *gc,
-       uint32_t idx
+       uint32_t *idx
 ) {
-       uint32_t *p = bsearch(&idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs);
-       assert(p != NULL);
-       return gc->afters[p - gc->befores];
+       uint32_t idbef, idaft;
+
+       idbef = *idx;
+       idaft = *gc_after_ptr(gc, idx);
+       *idx = idaft;
+       return (int)(idbef - idaft);
 }
 
 static
@@ -749,9 +779,9 @@ gc_init(
        qsort(gc->befores, names_count, sizeof *gc->befores, cmpidxs);
        gc->afters = &gc->befores[names_count];
        memset(gc->afters, 0, names_count * sizeof *gc->afters);
-       
-       gc_mark(gc, ANYIDX);
-       gc_mark(gc, WIDEIDX);
+
+       gc_mark_id(gc, ANYIDX);
+       gc_mark_id(gc, WIDEIDX);
        return 0;
 }
 
@@ -807,7 +837,7 @@ int
 db_cleanup(
 ) {
        int rc, chg;
-       uint32_t idbef, idaft, i, j, now;
+       uint32_t i, now;
        gc_t gc;
        rule_t *rule;
 
@@ -826,8 +856,11 @@ db_cleanup(
                if (rule->expire && now >= rule->expire)
                        drop_at(i);
                else {
-                       for (j = 0 ; j < 5 ; j++)
-                               gc_mark(&gc, rule->ids[j]);
+                       gc_mark(&gc, &rule->key.client);
+                       gc_mark(&gc, &rule->key.session);
+                       gc_mark(&gc, &rule->key.user);
+                       gc_mark(&gc, &rule->key.permission);
+                       gc_mark(&gc, &rule->value);
                        i++;
                }
        }
@@ -838,12 +871,11 @@ db_cleanup(
                i = 0;
                while (i < rules_count) {
                        rule = &rules[i];
-                       for (chg = j = 0 ; j < 5 ; j++) {
-                               idbef = rule->ids[j];
-                               idaft = gc_after(&gc, idbef);
-                               rule->ids[j] = idaft;
-                               chg += idbef != idaft;
-                       }
+                       chg = gc_after(&gc, &rule->key.client);
+                       chg |= gc_after(&gc, &rule->key.session);
+                       chg |= gc_after(&gc, &rule->key.user);
+                       chg |= gc_after(&gc, &rule->key.permission);
+                       chg |= gc_after(&gc, &rule->value);
                        if (chg)
                                touch_at(i);
                        i++;
index 192e215..61265fc 100644 (file)
--- a/src/db.h
+++ b/src/db.h
@@ -44,19 +44,6 @@ int
 db_sync(
 );
 
-/** enter critical recoverable section */
-extern
-int
-db_enter(
-);
-
-/** leave critical recoverable section */
-extern
-int
-db_leave(
-       bool commit
-);
-
 /** get an index for a name */
 extern
 int
@@ -85,53 +72,36 @@ db_for_all(
        void *closure,
        void (*callback)(
                void *closure,
-               const char *client,
-               const char *session,
-               const char *user,
-               const char *permission,
-               const char *value,
-               time_t expire),
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+               const data_key_t *key,
+               const data_value_t *value),
+       const data_key_t *key
 );
 
 /** erase rules */
 extern
 int
 db_drop(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const data_key_t *key
 );
 
 /** set rules */
 extern
 int
 db_set(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const data_key_t *key,
+       const data_value_t *value
 );
 
 /** check rules */
 extern
 int
 db_test(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char **value,
-       time_t *expire
+       const data_key_t *key,
+       data_value_t *value
 );
 
 /** cleanup the base */
+extern
 int
 db_cleanup(
 );
diff --git a/src/dbinit.c b/src/dbinit.c
new file mode 100644 (file)
index 0000000..b1b3c54
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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 <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include "data.h"
+#include "cyn.h"
+#include "expire.h"
+#include "dbinit.h"
+
+/** initialize the database from file of 'path' */
+int dbinit_add_file(const char *path)
+{
+       int rc, lino;
+       char *item[10];
+       char buffer[2048];
+       data_key_t key;
+       data_value_t value;
+       FILE *f;
+
+       /* enter critical section */
+       rc = cyn_enter(dbinit_add_file);
+       if (rc < 0)
+               return rc;
+
+       /* open the file */
+       f = fopen(path, "r");
+       if (f == NULL) {
+               rc = -errno;
+               fprintf(stderr, "can't open file %s\n", path);
+               goto error;
+       }
+
+       /* read lines of the file */
+       lino = 0;
+       while(fgets(buffer, sizeof buffer, f)) {
+
+               /* parse the line */
+               lino++;
+               item[0] = strtok(buffer, " \t\n\r");
+               item[1] = strtok(NULL, " \t\n\r");
+               item[2] = strtok(NULL, " \t\n\r");
+               item[3] = strtok(NULL, " \t\n\r");
+               item[4] = strtok(NULL, " \t\n\r");
+               item[5] = strtok(NULL, " \t\n\r");
+               item[6] = strtok(NULL, " \t\n\r");
+
+               /* skip empty lines and comments */
+               if (item[0] == NULL)
+                       continue;
+               if (item[0][0] == '#')
+                       continue;
+
+               /* check items of the rule */
+               if (item[1] == NULL || item[2] == NULL
+                 || item[3] == NULL || item[4] == NULL
+                 || item[5] == NULL) {
+                       fprintf(stderr, "field missing (%s:%d)\n", path, lino);
+                       rc = -EINVAL;
+                       goto error2;
+               }
+               if (item[6] != NULL && item[6][0] != '#') {
+                       fprintf(stderr, "extra field (%s:%d)\n", path, lino);
+                       rc = -EINVAL;
+                       goto error2;
+               }
+
+               /* create the key and value of the rule */
+               key.client = item[0];
+               key.session = item[1];
+               key.user = item[2];
+               key.permission = item[3];
+               value.value = item[4];
+               value.expire = txt2exp(item[5]);
+               if (value.expire < 0) {
+                       fprintf(stderr, "bad expiration %s (%s:%d)\n", item[5], path, lino);
+                       rc = -EINVAL;
+                       goto error2;
+               }
+
+               /* record the rule */
+               rc = cyn_set(&key, &value);
+               if (rc < 0) {
+                       fprintf(stderr, "can't set (%s:%d)\n", path, lino);
+                       exit(1);
+               }
+       }
+       if (!feof(f)) {
+               rc = -errno;
+               fprintf(stderr, "error while reading file %s\n", path);
+               goto error2;
+       }
+       rc = 0;
+error2:
+       fclose(f);
+error:
+       if (rc)
+               cyn_leave(dbinit_add_file, 0);
+       else {
+               rc = cyn_leave(dbinit_add_file, 1);
+               if (rc < 0)
+                       fprintf(stderr, "unable to commit content of file %s\n", path);
+       }
+       return rc;
+}
diff --git a/src/dbinit.h b/src/dbinit.h
new file mode 100644 (file)
index 0000000..bca4afc
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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 dbinit_add_file(const char *path);
+
diff --git a/src/expire.c b/src/expire.c
new file mode 100644 (file)
index 0000000..1571db7
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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 <time.h>
+#include <string.h>
+#include <stdio.h>
+
+static const int SEC = 1;
+static const int MIN = 60;
+static const int HOUR = 60*60;
+static const int DAY = 24*60*60;
+static const int WEEK = 7*24*60*60;
+static const int YEAR = 365*24*60*60;
+
+time_t txt2exp(const char *txt)
+{
+       time_t r, x;
+
+       /* infinite time */
+       if (!strcmp(txt, "always") || !strcmp(txt, "*"))
+               return 0;
+
+       /* parse */
+       r = time(NULL);
+       while(*txt) {
+               x = 0;
+               while('0' <= *txt && *txt <= '9')
+                       x = 10 * x + (time_t)(*txt++ - '0');
+               switch(*txt) {
+               case 'y': r += x * YEAR; txt++; break;
+               case 'w': r += x * WEEK; txt++; break;
+               case 'd': r += x * DAY; txt++; break;
+               case 'h': r += x * HOUR; txt++; break;
+               case 'm': r += x * MIN; txt++; break;
+               case 's': txt++; /*@fallthrough@*/
+               case 0: r += x * SEC; break;
+               default: return -1;
+               }
+       }
+       return r;
+}
+
+size_t exp2txt(time_t expire, char *buffer, size_t buflen)
+{
+       char b[100];
+       size_t l;
+       int n;
+
+       if (!expire)
+               strncpy(b, "always", sizeof b);
+       else {
+               expire -= time(NULL);
+               n = 0;
+#define ADD(C,U) \
+  if (expire >= U) { \
+    n += snprintf(&b[n], sizeof b - n, "%lld" #C, (long long)(expire / U)); \
+    expire %= U; \
+  }
+               ADD(y,YEAR)
+               ADD(w,WEEK)
+               ADD(d,DAY)
+               ADD(h,HOUR)
+               ADD(m,MIN)
+               ADD(s,SEC)
+#undef ADD
+       }
+       l = strlen(b);
+       strncpy(buffer, b, buflen);
+       return l;
+}
diff --git a/src/expire.h b/src/expire.h
new file mode 100644 (file)
index 0000000..c9e46bc
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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 time_t txt2exp(const char *txt);
+extern size_t exp2txt(time_t expire, char *buffer, size_t buflen);
index c1d4475..55b8230 100644 (file)
  * limitations under the License.
  */
 
-
-#define _GNU_SOURCE
-
-
 #include <assert.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -45,88 +41,101 @@ get_asz(
 }
 
 /** open in 'fb' the file of 'name' */
+static
 int
-fbuf_open(
+read_file(
        fbuf_t  *fb,
-       const char *name,
-       const char *backup
+       const char *name
 ) {
        struct stat st;
-       int fd, fdback, rc;
-       uint32_t sz, asz;
-       void *buffer;
-
-       /* open the backup */
-       if (backup == NULL)
-               fdback = -1;
-       else {
-               fdback = open(backup, O_RDWR|O_CREAT, 0600);
-               if (fdback < 0)
-                       goto error;
-               rc = flock(fdback, LOCK_EX|LOCK_NB);
-               if (rc < 0)
-                       goto error;
-       }
+       int fd, rc;
+       uint32_t sz;
 
        /* open the file */
        fd = open(name, O_RDWR|O_CREAT, 0600);
        if (fd < 0)
                goto error;
 
-       /* lock it */
-       rc = flock(fd, LOCK_EX|LOCK_NB);
-       if (rc < 0)
-               goto error2;
-
        /* get file stat */
        rc = fstat(fd, &st);
        if (rc < 0)
-               goto error3;
+               goto error2;
 
        /* check file size */
        if ((off_t)INT32_MAX < st.st_size) {
                errno = EFBIG;
-               goto error3;
+               goto error2;
        }
-
-       /* compute allocation size */
        sz = (uint32_t)st.st_size;
-       asz = get_asz(sz);
-       buffer = malloc(asz);
-       if (buffer == NULL)
-               goto error3;
+
+       /* allocate memory */
+       rc = fbuf_ensure_capacity(fb, (uint32_t)st.st_size);
+       if (rc < 0)
+               goto error2;
 
        /* read the file */
-       if (read(fd, buffer, (size_t)sz) != (ssize_t)sz)
-               goto error4;
+       if (read(fd, fb->buffer, (size_t)sz) != (ssize_t)sz)
+               goto error2;
+
+       /* done */
+       fb->used = fb->size = sz;
+       close(fd);
+       return 0;
+
+error2:
+       close(fd);
+error:
+       rc = -errno;
+       fprintf(stderr, "can't read file %s: %m", name);
+       fb->saved = fb->used = fb->size = 0;
+       return rc;
+}
+
+/** open in 'fb' the file of 'name' */
+int
+fbuf_open(
+       fbuf_t  *fb,
+       const char *name,
+       const char *backup
+) {
+       int rc;
+       size_t sz;
+
+       /* reset */
+       memset(fb, 0, sizeof *fb);
 
        /* save name */
        fb->name = strdup(name);
        if (fb->name == NULL)
-               goto error4;
+               goto error;
 
-       /* done */
-       fb->buffer = buffer;
-       fb->saved = fb->used = fb->size = sz;
-       fb->capacity = asz;
-       fb->fd = fd;
-       fb->backup = fdback;
+       /* open the backup */
+       if (backup != NULL)
+               fb->backup = strdup(backup);
+       else {
+               sz = strlen(name);
+               fb->backup = malloc(sz + 2);
+               if (fb->backup != NULL) {
+                       memcpy(fb->backup, name, sz);
+                       fb->backup[sz] = '~';
+                       fb->backup[sz + 1] = 0;
+               }
+       }
+       if (fb->backup == NULL)
+               goto error;
+
+       /* read the file */
+       rc = read_file(fb, fb->name);
+       if (rc < 0)
+               goto error;
+
+       fb->saved = fb->used;
        return 0;
 
-error4:
-       free(buffer);
-error3:
-       flock(fd, LOCK_UN);
-error2:
-       close(fd);
 error:
        rc = -errno;
        fprintf(stderr, "can't open file %s: %m", name);
-       if (fdback >= 0) {
-               flock(fdback, LOCK_UN);
-               close(fdback);
-       }
-       memset(fb, 0, sizeof *fb);
+       fbuf_close(fb);
        return rc;
 }
 
@@ -136,77 +145,42 @@ fbuf_close(
        fbuf_t  *fb
 ) {
        free(fb->name);
+       free(fb->backup);
        free(fb->buffer);
-       flock(fb->fd, LOCK_UN);
-       close(fb->fd);
-       if (fb->backup >= 0) {
-               flock(fb->backup, LOCK_UN);
-               close(fb->backup);
-       }
        memset(fb, 0, sizeof *fb);
 }
 
-/** write to file 'fb' at 'offset' the 'count' bytes pointed by 'buffer' */
-int
-fbuf_write(
-       fbuf_t  *fb,
-       const void *buffer,
-       uint32_t count,
-       uint32_t offset
-) {
-       ssize_t rcs;
-
-       /* don't call me for nothing */
-       assert(count);
-
-       /* effective write */
-       rcs = pwrite(fb->fd, buffer, (size_t)count, (off_t)offset);
-       if (rcs != (ssize_t)count)
-               goto error;
-
-       return 0;
-error:
-       fprintf(stderr, "write of file %s failed: %m", fb->name);
-       return -errno;
-}
-
 /** write to file 'fb' the unsaved bytes and flush the content to the file */
 int
 fbuf_sync(
        fbuf_t  *fb
 ) {
-       int rc;
-       bool changed = false;
+       ssize_t rcs;
+       int rc, fd;
 
-       /* write unsaved bytes */
-       if (fb->used > fb->saved) {
-               rc = fbuf_write(fb, fb->buffer + fb->saved, fb->used - fb->saved, fb->saved);
-               if (rc < 0)
-                       return rc;
-               fb->saved = fb->used;
-               changed = true;
-       }
+       /* is sync needed? */
+       if (fb->used == fb->saved && fb->used == fb->size)
+               return 0;
 
-       /* truncate on needed */
-       if (fb->used < fb->size) {
-               rc = ftruncate(fb->fd, (off_t)fb->used);
-               if (rc < 0)
-                       goto error;
-               changed = true;
-       }
-       fb->size = fb->used;
+       /* open the file */
+       unlink(fb->name);
+       fd = creat(fb->name, 0600);
+       if (fd < 0)
+               goto error;
 
-       /* force synchronisation of the file */
-       if (changed) {
-               rc = fsync(fb->fd);
-               if (rc < 0)
-                       goto error;
-       }
+       /* write unsaved bytes */
+       rcs = write(fd, fb->buffer, fb->used);
+       close(fd);
+       if (rcs < 0)
+               goto error;
 
+       fb->size = fb->saved = fb->used;
        return 0;
+
 error:
+       rc = -errno;
        fprintf(stderr, "sync of file %s failed: %m", fb->name);
-       return -errno;
+       return rc;
 }
 
 /** allocate enough memory in 'fb' to store 'count' bytes */
@@ -316,58 +290,13 @@ fbuf_open_identify(
        return rc;
 }
 
-
-/* On versions of glibc before 2.27, we must invoke copy_file_range()
-  using syscall(2) */
-#include <features.h>
-#if (__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 27)
-#include <syscall.h>
-static
-loff_t
-copy_file_range(
-       int fd_in,
-       loff_t *off_in,
-       int fd_out,
-       loff_t *off_out,
-       size_t len,
-       unsigned int flags
-) {
-       loff_t rc;
-
-       rc = syscall(__NR_copy_file_range, fd_in, off_in, fd_out,
-                               off_out, len, flags);
-       return rc;
-}
-#endif
-
-
 /** make a backup */
 int
 fbuf_backup(
        fbuf_t  *fb
 ) {
-       int rc;
-       size_t sz;
-       ssize_t wsz;
-       loff_t ino, outo;
-
-       ino = outo = 0;
-       sz = fb->size;
-       for (;;) {
-               wsz = copy_file_range(fb->fd, &ino, fb->backup, &outo, sz, 0);
-               if (wsz < 0) {
-                       if (errno != EINTR)
-                               return -errno;
-               } else {
-                       sz -= (size_t)wsz;
-                       if (sz == 0) {
-                               rc = ftruncate(fb->backup, outo);
-                               if (rc == 0)
-                                       rc = fsync(fb->backup);
-                               return rc < 0 ? -errno : 0;
-                       }
-               }
-       }
+       unlink(fb->backup);
+       return link(fb->name, fb->backup);
 }
 
 /** recover from latest backup */
@@ -375,29 +304,10 @@ int
 fbuf_recover(
        fbuf_t  *fb
 ) {
-       ssize_t ssz;
-       struct stat st;
        int rc;
 
-       /* get the size */
-       rc = fstat(fb->backup, &st);
-       if (rc < 0)
-               return -errno;
-
-       /* ensure space */
-       if (st.st_size > UINT32_MAX)
-               return -EFBIG;
-       rc = fbuf_ensure_capacity(fb, (uint32_t)st.st_size);
-       if (rc < 0)
-               return rc;
-
-       /* read it */
-       ssz = pread(fb->backup, fb->buffer, (size_t)st.st_size, 0); 
-       if (ssz < 0)
-               return -errno;
-
-       fb->used = (uint32_t)st.st_size;
-       fb->saved = fb->size = 0; /* ensure rewrite of restored data */
-       return 0;
+       rc = read_file(fb, fb->backup);
+       fb->saved = 0; /* ensure rewrite of restored data */
+       return rc;
 }
 
index bf7d9c1..43909fa 100644 (file)
  */
 struct fbuf
 {
-        /** filename for messages */
+        /** filename */
         char *name;
 
+        /** backup filename */
+        char *backup;
+
         /** in memory copy of the file */
         void *buffer;
 
-        /** size saved to the file */
-        uint32_t saved;
-
-        /** size currently used */
-        uint32_t used;
-
         /** size currently allocated */
         uint32_t capacity;
 
         /** size of the file */
         uint32_t size;
 
-        /** opened file descriptor for the file */
-        int fd;
+        /** size saved to the file */
+        uint32_t saved;
 
-        /** opened file descriptor for the backup */
-        int backup;
+        /** size currently used */
+        uint32_t used;
 };
 
 /** short type */
@@ -67,16 +64,6 @@ fbuf_close(
        fbuf_t  *fb
 );
 
-/** write to file 'fb' at 'offset' the 'count' bytes pointed by 'buffer' */
-extern
-int
-fbuf_write(
-       fbuf_t  *fb,
-       const void *buffer,
-       uint32_t count,
-       uint32_t offset
-);
-
 /** write to file 'fb' the unsaved bytes and flush the content to the file */
 extern
 int
index b9934af..175ec5f 100644 (file)
@@ -27,7 +27,6 @@ cynara_initialize(&m_Cynara, nullptr),
 cynara_finish(m_Cynara);
 cynara_check(m_Cynara,
 */
-#define _GNU_SOURCE
 
 #include <stdbool.h>
 #include <stdlib.h>
@@ -148,7 +147,7 @@ struct cynara_admin;
 
 int cynara_admin_initialize(struct cynara_admin **pp_cynara_admin)
 {
-       return from_status(rcyn_open((rcyn_t**)pp_cynara_admin, rcyn_Admin, 0));
+       return from_status(rcyn_open((rcyn_t**)pp_cynara_admin, rcyn_Admin, 0, 0));
 }
 
 int cynara_admin_finish(struct cynara_admin *p_cynara_admin)
@@ -162,17 +161,24 @@ int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin,
 {
        int rc, rc2;
        const struct cynara_admin_policy *p;
+       rcyn_key_t key;
+       rcyn_value_t value;
 
+       key.session = "*";
+       value.expire = 0;
        rc = rcyn_enter((rcyn_t*)p_cynara_admin);
        if (rc == 0) {
                p = *policies;
                while (rc == 0 && p != NULL) {
+                       key.client = p->client;
+                       key.user = p->user;
+                       key.permission = p->privilege;
                        if (p->result == CYNARA_ADMIN_DELETE)
-                               rc = rcyn_drop((rcyn_t*)p_cynara_admin,
-                                               p->client, "*", p->user, p->privilege);
-                       else if (p->result != CYNARA_ADMIN_BUCKET && p->result != CYNARA_ADMIN_NONE)
-                               rc = rcyn_set((rcyn_t*)p_cynara_admin,
-                                               p->client, "*", p->user, p->privilege, to_value(p->result), 0);
+                               rc = rcyn_drop((rcyn_t*)p_cynara_admin, &key);
+                       else if (p->result != CYNARA_ADMIN_BUCKET && p->result != CYNARA_ADMIN_NONE) {
+                               value.value = to_value(p->result);
+                               rc = rcyn_set((rcyn_t*)p_cynara_admin, &key, &value);
+                       }
                        p = *++policies;
                }
                rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0);
@@ -184,14 +190,10 @@ int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin,
 
 static void check_cb(
        void *closure,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const rcyn_key_t *key,
+       const rcyn_value_t *value
 ) {
-       *((int*)closure) = from_value(value);
+       *((int*)closure) = from_value(value->value);
 }
 
 int cynara_admin_check(struct cynara_admin *p_cynara_admin,
@@ -199,10 +201,11 @@ int cynara_admin_check(struct cynara_admin *p_cynara_admin,
                        const char *client, const char *user, const char *privilege,
                        int *result, char **result_extra)
 {
+       rcyn_key_t key = { client, "*", user, privilege };
        if (result_extra)
                *result_extra = NULL;
        *result = CYNARA_ADMIN_DENY;
-       return from_status(rcyn_get((rcyn_t*)p_cynara_admin, client, "*", user, privilege, check_cb, result));
+       return from_status(rcyn_get((rcyn_t*)p_cynara_admin, &key, check_cb, result));
 }
 
 struct list_data
@@ -215,12 +218,8 @@ struct list_data
 
 static void list_cb(
        void *closure,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const rcyn_key_t *key,
+       const rcyn_value_t *value
 ) {
        struct list_data *data = closure;
        struct cynara_admin_policy *pol;
@@ -233,13 +232,13 @@ static void list_cb(
                goto error;
 
        pol->bucket = strdup(data->bucket ?: "");
-       pol->client = strdup(client);
-       pol->user = strdup(user);
-       pol->privilege = strdup(permission);
+       pol->client = strdup(key->client);
+       pol->user = strdup(key->user);
+       pol->privilege = strdup(key->permission);
        if (pol->bucket == NULL || pol->client == NULL || pol->user == NULL || pol->privilege == NULL)
                goto error;
 
-       pol->result = from_value(value);
+       pol->result = from_value(value->value);
        pol->result_extra = 0;
        closure = realloc(data->policies, (data->count + 1) * sizeof *data->policies);
        if (closure == NULL)
@@ -265,12 +264,13 @@ int cynara_admin_list_policies(struct cynara_admin *p_cynara_admin, const char *
 {
        int rc;
        struct list_data data;
+       rcyn_key_t key = { client, "*", user, privilege };
 
        data.policies = NULL;
        data.bucket = bucket && strcmp(bucket, "#") && strcmp(bucket, "*") ? bucket : NULL;
        data.count = 0;
        data.error = 0;
-       rc = rcyn_get((rcyn_t*)p_cynara_admin, client, "*", user, privilege, list_cb, &data);
+       rc = rcyn_get((rcyn_t*)p_cynara_admin, &key, list_cb, &data);
        if (rc == 0 && data.error != 0)
                rc = data.error;
        if (rc == 0 && !data.error) {
@@ -293,11 +293,11 @@ int cynara_admin_erase(struct cynara_admin *p_cynara_admin,
                        const char *client, const char *user, const char *privilege)
 {
        int rc, rc2;
+       rcyn_key_t key = { client, "*", user, privilege };
 
        rc = rcyn_enter((rcyn_t*)p_cynara_admin);
        if (rc == 0) {
-               rc = rcyn_drop((rcyn_t*)p_cynara_admin,
-                                       client, "*", user, privilege);
+               rc = rcyn_drop((rcyn_t*)p_cynara_admin, &key);
                rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0);
                if (rc == 0)
                        rc = rc2;
@@ -409,7 +409,7 @@ int cynara_async_initialize(cynara_async **pp_cynara, const cynara_async_configu
        if (p_cynara == NULL)
                ret = CYNARA_API_OUT_OF_MEMORY;
        else {
-               ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, p_conf ? p_conf->szcache : 0));
+               ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, p_conf ? p_conf->szcache : 0, 0));
                if (ret != CYNARA_API_SUCCESS)
                        free(p_cynara);
                else {
@@ -447,7 +447,8 @@ void cynara_async_finish(cynara_async *p_cynara)
 int cynara_async_check_cache(cynara_async *p_cynara, const char *client, const char *client_session,
                              const char *user, const char *privilege)
 {
-       return from_check_status(rcyn_cache_check(p_cynara->rcyn, client, client_session,user, privilege));
+       rcyn_key_t key = { client, client_session, user, privilege };
+       return from_check_status(rcyn_cache_check(p_cynara->rcyn, &key));
 }
 
 static void reqcb(void *closure, int status)
@@ -473,6 +474,7 @@ static int create_reqasync(cynara_async *p_cynara, const char *client,
 {
        int rc;
        struct reqasync *req;
+       rcyn_key_t key = { client, client_session, user, privilege };
 
        req = malloc(sizeof *req);
        if (req == NULL)
@@ -485,7 +487,7 @@ static int create_reqasync(cynara_async *p_cynara, const char *client,
        req->id = ++p_cynara->ids;
        req->canceled = false;
 
-       rc = rcyn_async_check(p_cynara->rcyn, client, client_session, user, privilege, simple, reqcb, req);
+       rc = rcyn_async_check(p_cynara->rcyn, &key, simple, reqcb, req);
        if (rc == 0)
                p_cynara->reqs = req;
        else
@@ -555,7 +557,7 @@ int cynara_configuration_set_cache_size(cynara_configuration *p_conf,
 
 int cynara_initialize(cynara **pp_cynara, const cynara_configuration *p_conf)
 {
-       return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, p_conf ? p_conf->szcache : 0));
+       return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, p_conf ? p_conf->szcache : 0, 0));
 }
 
 int cynara_finish(cynara *p_cynara)
@@ -567,13 +569,15 @@ int cynara_finish(cynara *p_cynara)
 int cynara_check(cynara *p_cynara, const char *client, const char *client_session, const char *user,
                  const char *privilege)
 {
-       return from_check_status(rcyn_check((rcyn_t*)p_cynara, client, client_session, user, privilege));
+       rcyn_key_t key = { client, client_session, user, privilege };
+       return from_check_status(rcyn_check((rcyn_t*)p_cynara, &key));
 }
 
 int cynara_simple_check(cynara *p_cynara, const char *client, const char *client_session,
                         const char *user, const char *privilege)
 {
-       return from_check_status(rcyn_test((rcyn_t*)p_cynara, client, client_session, user, privilege));
+       rcyn_key_t key = { client, client_session, user, privilege };
+       return from_check_status(rcyn_test((rcyn_t*)p_cynara, &key));
 }
 
 /************************************* CREDS... & SESSION *********************************/
index fa44895..1aaedde 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <getopt.h>
 #include <errno.h>
 #include <time.h>
 #include <signal.h>
 
 #include "rcyn-client.h"
-
-rcyn_t *rcyn;
-
-char buffer[4000];
-char *str[40];
-int nstr;
-
-static const int MIN = 60;
-static const int HOUR = 60*60;
-static const int DAY = 24*60*60;
-static const int WEEK = 7*24*60*60;
-static const int YEAR = 365*24*60*60;
-
-const char *client, *session, *user, *permission, *value;
-time_t expire;
-int txt2experr;
-
-time_t txt2exp(const char *txt)
-{
-       time_t r, x;
-
-       txt2experr = 0;
-       if (!strcmp(txt, "always"))
-               return 0;
-
-       r = time(NULL);
-       while(*txt) {
-               x = 0;
-               while('0' <= *txt && *txt <= '9')
-                       x = 10 * x + (time_t)(*txt++ - '0');
-               switch(*txt) {
-               case 'y': r += x * YEAR; txt++; break;
-               case 'w': r += x * WEEK; txt++; break;
-               case 'd': r += x *= DAY; txt++; break;
-               case 'h': r += x *= HOUR; txt++; break;
-               case 'm': r += x *= MIN; txt++; break;
-               case 's': txt++; /*@fallthrough@*/
-               case 0: r += x; break;
-               default: txt2experr = 1; return -1;
-               }
-       }
-       return r;
-}
-
-const char *exp2txt(time_t expire)
-{
-       static char buffer[200];
-       int n;
-
-       if (!expire)
-               return "always";
-       expire -= time(NULL);
-       n = 0;
-       if (expire >= YEAR) {
-               n += snprintf(&buffer[n], sizeof buffer - n, "%lldy",
-                       (long long)(expire / YEAR));
-               expire = expire % YEAR;
-       }
-       if (expire >= WEEK) {
-               n += snprintf(&buffer[n], sizeof buffer - n, "%lldw",
-                       (long long)(expire / WEEK));
-               expire = expire % WEEK;
-       }
-       if (expire >= DAY) {
-               n += snprintf(&buffer[n], sizeof buffer - n, "%lldd",
-                       (long long)(expire / DAY));
-               expire = expire % DAY;
-       }
-       if (expire >= HOUR) {
-               n += snprintf(&buffer[n], sizeof buffer - n, "%lldh",
-                       (long long)(expire / HOUR));
-               expire = expire % HOUR;
-       }
-       if (expire >= MIN) {
-               n += snprintf(&buffer[n], sizeof buffer - n, "%lldm",
-                       (long long)(expire / MIN));
-               expire = expire % MIN;
-       }
-       if (expire) {
-               n += snprintf(&buffer[n], sizeof buffer - n, "%llds",
-                       (long long)expire);
-       }
-       return buffer;
-}
+#include "rcyn-protocol.h"
+#include "expire.h"
+
+#define _HELP_        'h'
+#define _SOCKET_      's'
+#define _VERSION_     'v'
+
+static
+const char
+shortopts[] = "hs:v";
+
+static
+const struct option
+longopts[] = {
+       { "help", 0, NULL, _HELP_ },
+       { "socket", 1, NULL, _SOCKET_ },
+       { "version", 0, NULL, _VERSION_ },
+       { NULL, 0, NULL, 0 }
+};
+
+static
+const char
+helptxt[] =
+       "\n"
+       "usage: cynadm [options]...\n"
+       "\n"
+       "otpions:\n"
+       "       -s, --socket xxx      set the base xxx for sockets\n"
+       "                               (default: %s)\n"
+       "       -h, --help            print this help and exit\n"
+       "       -v, --version         print the version and exit\n"
+       "\n"
+;
+
+static
+const char
+versiontxt[] =
+       "cynadm version 1.99.99\n"
+;
+
+static rcyn_t *rcyn;
+static char buffer[4000];
+static char *str[40];
+static int nstr;
+
+rcyn_key_t key;
+rcyn_value_t value;
 
 int plink(int ac, char **av, int *used, int maxi)
 {
@@ -127,35 +91,34 @@ int get_csupve(int ac, char **av, int *used, const char *def)
 {
        int n = plink(ac, av, used, 7);
 
-       client = n > 1 ? av[1] : def;
-       session = n > 2 ? av[2] : def;
-       user = n > 3 ? av[3] : def;
-       permission = n > 4 ? av[4] : def;
-       value = n > 5 ? av[5] : "no";
-       expire = n > 6 ? txt2exp(av[6]) : 0;
+       key.client = n > 1 ? av[1] : def;
+       key.session = n > 2 ? av[2] : def;
+       key.user = n > 3 ? av[3] : def;
+       key.permission = n > 4 ? av[4] : def;
+       value.value = n > 5 ? av[5] : "no";
+       value.expire = n > 6 ? txt2exp(av[6]) : 0;
 
-       return client && session && user && permission && value && !txt2experr ? 0 : -EINVAL;
+       return key.client && key.session && key.user && key.permission && value.value && value.expire >= 0 ? 0 : -EINVAL;
 }
 
 int get_csup(int ac, char **av, int *used, const char *def)
 {
        int n = plink(ac, av, used, 5);
 
-       client = n > 1 ? av[1] : def;
-       session = n > 2 ? av[2] : def;
-       user = n > 3 ? av[3] : def;
-       permission = n > 4 ? av[4] : def;
+       key.client = n > 1 ? av[1] : def;
+       key.session = n > 2 ? av[2] : def;
+       key.user = n > 3 ? av[3] : def;
+       key.permission = n > 4 ? av[4] : def;
 
-       return client && session && user && permission ? 0 : -EINVAL;
+       return key.client && key.session && key.user && key.permission ? 0 : -EINVAL;
 }
 
-void listcb(void *closure, const char *client, const char *session,
-               const char *user, const char *permission,
-               const char *value, time_t expire)
+void listcb(void *closure, const rcyn_key_t *k, const rcyn_value_t *v)
 {
+       char buffer[100];
        int *p = closure;
-       const char *e = exp2txt(expire);
-       fprintf(stdout, "%s\t%s\t%s\t%s\t%s\t%s\n", client, session, user, permission, value, e);
+       exp2txt(v->expire, buffer, sizeof buffer);
+       fprintf(stdout, "%s\t%s\t%s\t%s\t%s\t%s\n", k->client, k->session, k->user, k->permission, v->value, buffer);
        (*p)++;
 }
 
@@ -166,7 +129,7 @@ int do_list(int ac, char **av)
        rc = get_csup(ac, av, &uc, "#");
        if (rc == 0) {
                count = 0;
-               rc = rcyn_get(rcyn, client, session, user, permission, listcb, &count);
+               rc = rcyn_get(rcyn, &key, listcb, &count);
                if (rc < 0)
                        fprintf(stderr, "error %s\n", strerror(-rc));
                else
@@ -183,7 +146,7 @@ int do_set(int ac, char **av)
        if (rc == 0)
                rc = rcyn_enter(rcyn);
        if (rc == 0) {
-               rc = rcyn_set(rcyn, client, session, user, permission, value, expire);
+               rc = rcyn_set(rcyn, &key, &value);
                rcyn_leave(rcyn, !rc);
        }
        if (rc < 0)
@@ -199,7 +162,7 @@ int do_drop(int ac, char **av)
        if (rc == 0)
                rc = rcyn_enter(rcyn);
        if (rc == 0) {
-               rc = rcyn_drop(rcyn, client, session, user, permission);
+               rc = rcyn_drop(rcyn, &key);
                rcyn_leave(rcyn, !rc);
        }
        if (rc < 0)
@@ -207,13 +170,13 @@ int do_drop(int ac, char **av)
        return uc;
 }
 
-int do_check(int ac, char **av, int (*f)(rcyn_t*,const char*,const char*,const char*,const char*))
+int do_check(int ac, char **av, int (*f)(rcyn_t*,const rcyn_key_t*))
 {
        int uc, rc;
 
        rc = get_csup(ac, av, &uc, NULL);
        if (rc == 0) {
-               rc = f(rcyn, client, session, user, permission);
+               rc = f(rcyn, &key);
                if (rc > 0)
                        fprintf(stdout, "allowed\n");
                else if (rc == 0)
@@ -270,16 +233,57 @@ void do_all(int ac, char **av)
 
 int main(int ac, char **av)
 {
+       int opt;
        int rc;
+       int help = 0;
+       int version = 0;
+       int error = 0;
+       const char *socket = NULL;
+
+       /* scan arguments */
+       for (;;) {
+               opt = getopt_long(ac, av, shortopts, longopts, NULL);
+               if (opt == -1)
+                       break;
 
+               switch(opt) {
+               case _HELP_:
+                       help = 1;
+                       break;
+               case _SOCKET_:
+                       socket = optarg;
+                       break;
+               case _VERSION_:
+                       version = 1;
+                       break;
+               default:
+                       error = 1;
+                       break;
+               }
+       }
+
+       /* handles help, version, error */
+       if (help) {
+               fprintf(stdout, helptxt, rcyn_default_admin_socket_spec);
+               return 0;
+       }
+       if (version) {
+               fprintf(stdout, versiontxt);
+               return 0;
+       }
+       if (error)
+               return 1;
+
+       /* initialize server */
        signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */
-       rc = rcyn_open(&rcyn, rcyn_Admin, 5000);
+       rc = rcyn_open(&rcyn, rcyn_Admin, 5000, socket);
        if (rc < 0) {
                fprintf(stderr, "initialisation failed: %s\n", strerror(-rc));
                return 1;
        }
-       if (ac > 1) {
-               do_all(ac - 1, av + 1);
+
+       if (optind < ac) {
+               do_all(ac - optind, av + optind);
                return 0;
        }
 
@@ -301,4 +305,3 @@ int main(int ac, char **av)
        }
        return 0;
 }
-
index 1829d74..ac8106e 100644 (file)
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
-
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <systemd/sd-daemon.h>
 #endif
 
+#include "data.h"
 #include "db.h"
-#include "cyn.h"
 #include "rcyn-server.h"
+#include "rcyn-protocol.h"
+#include "dbinit.h"
 
 #if !defined(DEFAULT_DB_DIR)
 #    define  DEFAULT_DB_DIR      "/var/lib/cynara"
 #endif
-#if !defined(DEFAULT_SOCKET_DIR)
-#    define  DEFAULT_SOCKET_DIR  "/var/run/cynara"
-#endif
 #if !defined(DEFAULT_INIT_FILE)
 #    define  DEFAULT_INIT_FILE   "/etc/security/cynara.initial"
 #endif
@@ -113,13 +110,13 @@ helptxt[] =
        "       -g, --group xxx       set the group\n"
        "       -i, --init xxx        initialize if needed the database with file xxx\n"
        "                               (default: "DEFAULT_INIT_FILE"\n"
-       "       -b, --dbdir xxx       set the directory of database\n"
+       "       -d, --dbdir xxx       set the directory of database\n"
        "                               (default: "DEFAULT_DB_DIR")\n"
        "       -m, --make-db-dir     make the database directory\n"
        "       -o, --own-db-dir      set user and group on database directory\n"
        "\n"
        "       -S, --socketdir xxx   set the base xxx for sockets\n"
-       "                               (default: "DEFAULT_SOCKET_DIR")\n"
+       "                               (default: %s)\n"
        "       -M, --make-socket-dir make the socket directory\n"
        "       -O, --own-socket-dir  set user and group on socket directory\n"
        "\n"
@@ -136,7 +133,7 @@ versiontxt[] =
 
 static int isid(const char *text);
 static void ensure_directory(const char *path, int uid, int gid);
-static void initdb(const char *path);
+
 int main(int ac, char **av)
 {
        int opt;
@@ -160,7 +157,7 @@ int main(int ac, char **av)
        struct group *gr;
        cap_t caps = { 0 };
        rcyn_server_t *server;
-       char *spec_socket_admin, *spec_socket_check;
+       char *spec_socket_admin, *spec_socket_check, *spec_socket_agent;
 
        /* scan arguments */
        for (;;) {
@@ -214,8 +211,12 @@ int main(int ac, char **av)
        }
 
        /* handles help, version, error */
-       if (help | version) {
-               fprintf(stdout, "%s", help ? helptxt : versiontxt);
+       if (help) {
+               fprintf(stdout, helptxt, rcyn_default_socket_dir);
+               return 0;
+       }
+       if (version) {
+               fprintf(stdout, versiontxt);
                return 0;
        }
        if (error)
@@ -228,21 +229,23 @@ int main(int ac, char **av)
 
        /* set the defaults */
        dbdir = dbdir ?: DEFAULT_DB_DIR;
-       socketdir = socketdir ?: DEFAULT_SOCKET_DIR;
+       socketdir = socketdir ?: rcyn_default_socket_dir;
        user = user ?: DEFAULT_CYNARA_USER;
        group = group ?: DEFAULT_CYNARA_GROUP;
        init = init ?: DEFAULT_INIT_FILE;
 
        /* compute socket specs */
-       spec_socket_admin = spec_socket_check = NULL;
+       spec_socket_admin = spec_socket_check = spec_socket_agent = 0;
        if (systemd) {
                spec_socket_admin = strdup("sd:admin");
                spec_socket_check = strdup("sd:check");
+               spec_socket_agent = strdup("sd:agent");
        } else {
-               rc = asprintf(&spec_socket_admin, "unix:%s/cynara.admin", socketdir);
-               rc = asprintf(&spec_socket_check, "unix:%s/cynara.check", socketdir);
+               rc = asprintf(&spec_socket_admin, "%s:%s/%s", rcyn_default_socket_scheme, socketdir, rcyn_default_admin_socket_base);
+               rc = asprintf(&spec_socket_check, "%s:%s/%s", rcyn_default_socket_scheme, socketdir, rcyn_default_check_socket_base);
+               rc = asprintf(&spec_socket_agent, "%s:%s/%s", rcyn_default_socket_scheme, socketdir, rcyn_default_agent_socket_base);
        }
-       if (spec_socket_admin == NULL || spec_socket_check == NULL) {
+       if (!spec_socket_admin || !spec_socket_check || !spec_socket_agent) {
                fprintf(stderr, "can't make socket paths\n");
                return 1;
        }
@@ -296,34 +299,37 @@ int main(int ac, char **av)
        cap_clear(caps);
        rc = cap_set_proc(caps);
 
+       /* initialize server */
+       signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */
+       rc = rcyn_server_create(&server, spec_socket_admin, spec_socket_check, spec_socket_agent);
+       if (rc < 0) {
+               fprintf(stderr, "can't initialise server: %m\n");
+               return 1;
+       }
+
        /* connection to the database */
-       rc = db_open(dbdir);
+       rc = chdir(dbdir);
        if (rc < 0) {
-               fprintf(stderr, "can not open database: %m\n");
+               fprintf(stderr, "can not chroot to database directory %s: %m\n", dbdir);
+               return 1;
+       }
+       rc = db_open(".");
+       if (rc < 0) {
+               fprintf(stderr, "can not open database of directory %s: %m\n", dbdir);
                return 1;
        }
 
        /* initialisation of the database */
        if (db_is_empty()) {
-               initdb(init);
+               rc = dbinit_add_file(init);
                if (rc == 0)
                        rc = db_sync();
-               if (rc == 0)
-                       rc = db_backup();
                if (rc < 0) {
                        fprintf(stderr, "can't initialise database: %m\n");
                        return 1;
                }
        }
 
-       /* initialize server */
-       signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */
-       rc = rcyn_server_create(&server, spec_socket_admin, spec_socket_check);
-       if (rc < 0) {
-               fprintf(stderr, "can't initialise server: %m\n");
-               return 1;
-       }
-
        /* ready ! */
 #if defined(WITH_SYSTEMD_ACTIVATION)
        if (systemd)
@@ -405,7 +411,7 @@ static void ensuredir(char *path, int length, int uid, int gid)
                        path[n] = '/';
                        n = (int)strlen(path);
                } else if (errno == ENOENT) {
-                       /* a part of the path doesn't exist, try to create it */ 
+                       /* a part of the path doesn't exist, try to create it */
                        e = enddir(path);
                        if (!e) {
                                /* can't create it because at root */
@@ -429,95 +435,10 @@ static void ensure_directory(const char *path, int uid, int gid)
        l = strlen(path);
        if (l > INT_MAX) {
                /* ?!?!?!? *#@! */
-               fprintf(stderr, "path toooooo long\n");
+               fprintf(stderr, "path toooooo long (%s)\n", path);
                exit(1);
        }
        p = strndupa(path, l);
        ensuredir(p, (int)l, uid, gid);
 }
 
-time_t txt2exp(const char *txt)
-{
-       static const int MIN = 60;
-       static const int HOUR = 60*60;
-       static const int DAY = 24*60*60;
-       static const int WEEK = 7*24*60*60;
-       static const int YEAR = 365*24*60*60;
-
-       time_t r, x;
-
-       if (!strcmp(txt, "always"))
-               return 0;
-       r = time(NULL);
-       while(*txt) {
-               x = 0;
-               while('0' <= *txt && *txt <= '9')
-                       x = 10 * x + (time_t)(*txt++ - '0');
-               switch(*txt) {
-               case 'y': r += x * YEAR; txt++; break;
-               case 'w': r += x *= WEEK; txt++; break;
-               case 'd': r += x *= DAY; txt++; break;
-               case 'h': r += x *= HOUR; txt++; break;
-               case 'm': r += x *= MIN; txt++; break;
-               case 's': txt++; /*@fallthrough@*/
-               case 0: r += x; break;
-               default: return -1;
-               }
-       }
-       return r;
-}
-
-/** initialize the database from file of 'path' */
-static void initdb(const char *path)
-{
-       int rc, lino;
-       char *item[10];
-       char buffer[2048];
-       time_t expire;
-       FILE *f;
-
-       f = fopen(path, "r");
-       if (f == NULL) {
-               fprintf(stderr, "can't open file %s\n", path);
-               exit(1);
-       }
-
-       lino = 0;
-       while(fgets(buffer, sizeof buffer, f)) {
-               lino++;
-               item[0] = strtok(buffer, " \t\n\r");
-               if (item[0] && item[0][0] != '#') {
-                       item[1] = strtok(NULL, " \t\n\r");
-                       item[2] = strtok(NULL, " \t\n\r");
-                       item[3] = strtok(NULL, " \t\n\r");
-                       item[4] = strtok(NULL, " \t\n\r");
-                       item[5] = strtok(NULL, " \t\n\r");
-                       item[6] = strtok(NULL, " \t\n\r");
-                       if (item[1] == NULL || item[2] == NULL
-                         || item[3] == NULL || item[4] == NULL
-                         || item[5] == NULL) {
-                               fprintf(stderr, "field missing (%s:%d)\n", path, lino);
-                               exit(1);
-                       } else if (item[6] != NULL && item[6][0] != '#') {
-                               fprintf(stderr, "extra field (%s:%d)\n", path, lino);
-                               exit(1);
-                       }
-                       expire = txt2exp(item[5]);
-                       if (expire < 0) {
-                               fprintf(stderr, "bad expiration %s (%s:%d)\n", item[5], path, lino);
-                               exit(1);
-                       }
-                       rc = db_set(item[0], item[1], item[2], item[3], item[4], expire);
-                       if (rc < 0) {
-                               fprintf(stderr, "can't set (%s:%d)\n", path, lino);
-                               exit(1);
-                       }
-               }
-       }
-       if (!feof(f)) {
-               fprintf(stderr, "error while reading file %s\n", path);
-               exit(1);
-       }
-       fclose(f);
-}
-
diff --git a/src/pollitem.c b/src/pollitem.c
new file mode 100644 (file)
index 0000000..d183594
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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 <stdint.h>
+#include <sys/epoll.h>
+
+/*
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+*/
+
+#include "pollitem.h"
+
+static
+int
+pollitem_do(
+       pollitem_t *pollitem,
+       uint32_t events,
+       int pollfd,
+       int op
+) {
+       struct epoll_event ev = { .events = events, .data.ptr = pollitem };
+       return epoll_ctl(pollfd, op, pollitem->fd, &ev);
+}
+
+int
+pollitem_add(
+       pollitem_t *pollitem,
+       uint32_t events,
+       int pollfd
+) {
+       return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_ADD);
+}
+
+int
+pollitem_mod(
+       pollitem_t *pollitem,
+       uint32_t events,
+       int pollfd
+) {
+       return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_MOD);
+}
+
+int
+pollitem_del(
+       pollitem_t *pollitem,
+       int pollfd
+) {
+       return pollitem_do(pollitem, 0, pollfd, EPOLL_CTL_DEL);
+}
+
+int
+pollitem_wait_dispatch(
+       int pollfd,
+       int timeout
+) {
+       int rc;
+       struct epoll_event ev;
+       pollitem_t *pi;
+
+       rc = epoll_wait(pollfd, &ev, 1, timeout);
+       if (rc == 1) {
+               pi = ev.data.ptr;
+               pi->handler(pi, ev.events, pollfd);
+       }
+       return rc;
+}
diff --git a/src/pollitem.h b/src/pollitem.h
new file mode 100644 (file)
index 0000000..a60b7b8
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+/** structure for using epoll easily */
+typedef struct pollitem pollitem_t;
+
+struct pollitem
+{
+       /** callback on event */
+       void (*handler)(pollitem_t *pollitem, uint32_t events, int pollfd);
+
+       /** data */
+       void *closure;
+
+       /** file */
+       int fd;
+};
+
+extern
+int
+pollitem_add(
+       pollitem_t *pollitem,
+       uint32_t events,
+       int pollfd
+);
+
+extern
+int
+pollitem_mod(
+       pollitem_t *pollitem,
+       uint32_t events,
+       int pollfd
+);
+
+extern
+int
+pollitem_del(
+       pollitem_t *pollitem,
+       int pollfd
+);
+
+extern
+int
+pollitem_wait_dispatch(
+       int pollfd,
+       int timeout
+);
index bb58acf..711d084 100644 (file)
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
-
 #include <stdlib.h>
 #include <limits.h>
 #include <stdarg.h>
@@ -37,6 +35,8 @@ struct buf
        /** a count */
        unsigned count;
 
+       /* TODO: add a 3rd unsigned for improving management of read and write */
+
        /** a fixed size content */
        char content[MAXBUFLEN];
 };
@@ -62,77 +62,83 @@ struct prot
        /** output buf, pos is to be written position */
        buf_t outbuf;
 
+       /** count of pending output fields */
+       unsigned outfields;
+
+       /** cancel index value value */
+       unsigned cancelidx;
+
        /** the fields */
        fields_t fields;
 };
 
 
 /**
- * Put the 'count' in 'fields' to the 'buf'
+ * Put the 'car' into the 'buf'
  * returns:
  *  - 0 on success
- *  - -EINVAL if the count of fields is too big
  *  - -ECANCELED if there is not enought space in the buffer
  */
 static
 int
-buf_put_fields(
+buf_put_car(
        buf_t *buf,
-       unsigned count,
-       const char **fields
+       char car
 ) {
-       unsigned ifield, pos, remain;
-       const char *t;
-       char c;
+       unsigned pos;
 
-       /* check the count of fields */
-       if (count > MAXARGS)
-               return -EINVAL;
+       pos = buf->count;
+       if (pos >= MAXBUFLEN)
+               return -ECANCELED;
+
+       buf->count = pos + 1;
+       pos += buf->pos;
+       if (pos >= MAXBUFLEN)
+               pos -= MAXBUFLEN;
+       buf->content[pos] = car;
+       return 0;
+}
+
+
+/**
+ * Put the 'string' into the 'buf' escaping it at need
+ * returns:
+ *  - 0 on success
+ *  - -ECANCELED if there is not enought space in the buffer
+ */
+static
+int
+buf_put_string(
+       buf_t *buf,
+       const char *string
+) {
+       unsigned pos, remain;
+       char c;
 
-       /* get the writing position and the free count */
-       pos = buf->pos + buf->count;
+       remain = buf->count;
+       pos = buf->pos + remain;
        if (pos >= MAXBUFLEN)
                pos -= MAXBUFLEN;
-       remain = MAXBUFLEN - buf->count;
+       remain = MAXBUFLEN - remain;
 
-       /* put all fields */
-       for (ifield = 0 ; ifield < count ; ifield++) {
-               /* prepend the field separator if needed */
-               if (ifield) {
+       /* put all chars of the string */
+       while((c = *string++)) {
+               /* escape special characters */
+               if (c == FS || c == RS || c == ESC) {
                        if (!remain--)
                                goto cancel;
-                       buf->content[pos++] = FS;
+                       buf->content[pos++] = ESC;
                        if (pos == MAXBUFLEN)
                                pos = 0;
                }
-               /* put the field if any (NULL aliases "") */
-               t = fields[ifield];
-               if (t) {
-                       /* put all chars of the field */
-                       while((c = *t++)) {
-                               /* escape special characters */
-                               if (c == FS || c == RS || c == ESC) {
-                                       if (!remain--)
-                                               goto cancel;
-                                       buf->content[pos++] = ESC;
-                                       if (pos == MAXBUFLEN)
-                                               pos = 0;
-                               }
-                               /* put the char */
-                               if (!remain--)
-                                       goto cancel;
-                               buf->content[pos++] = c;
-                               if (pos == MAXBUFLEN)
-                                       pos = 0;
-                       }
-               }
+               /* put the char */
+               if (!remain--)
+                       goto cancel;
+               buf->content[pos++] = c;
+               if (pos == MAXBUFLEN)
+                       pos = 0;
        }
 
-       /* put the end indicator */
-       if (!remain--)
-               goto cancel;
-       buf->content[pos] = RS;
-
        /* record the new values */
        buf->count = MAXBUFLEN - remain;
        return 0;
@@ -340,15 +346,80 @@ prot_reset(
        /* initialisation of the structure */
        prot->inbuf.pos = prot->inbuf.count = 0;
        prot->outbuf.pos = prot->outbuf.count = 0;
+       prot->outfields = 0;
        prot->fields.count = -1;
 }
 
+void
+prot_put_cancel(
+       prot_t *prot
+) {
+       unsigned count;
+
+       if (prot->outfields) {
+               count = prot->cancelidx - prot->outbuf.pos;
+               prot->outbuf.count = count > MAXBUFLEN ? count - MAXBUFLEN : count;
+               prot->outfields = 0;
+       }
+}
+
+int
+prot_put_end(
+       prot_t *prot
+) {
+       int rc;
+
+       if (!prot->outfields)
+               rc = 0;
+       else {
+               rc = buf_put_car(&prot->outbuf, RS);
+               prot->outfields = 0;
+       }
+       return rc;
+}
+
+int
+prot_put_field(
+       prot_t *prot,
+       const char *field
+) {
+       int rc;
+
+       if (prot->outfields++)
+               rc = buf_put_car(&prot->outbuf, FS);
+       else {
+               prot->cancelidx = prot->outbuf.pos + prot->outbuf.count;
+               rc = 0;
+       }
+       if (rc >= 0 && field)
+               rc = buf_put_string(&prot->outbuf, field);
+
+       return rc;
+}
+
+int
+prot_put_fields(
+       prot_t *prot,
+       unsigned count,
+       const char **fields
+) {
+       int rc;
+       if (!count)
+               rc = 0;
+       else {
+               rc = prot_put_field(prot, *fields);
+               while (rc >= 0 && --count)
+                       rc = prot_put_field(prot, *++fields);
+       }
+       return rc;
+}
+
 /**
  * Put protocol encoded 'count' 'fields' to the output buffer
  * returns:
  *  - 0 on success
  *  - -EINVAL if the count of fields is too big
- *  - -ECANCELED if there is not enought space in the buffer
+ *  - -ECANCELED if there is not enough space in the buffer
  */
 int
 prot_put(
@@ -356,7 +427,16 @@ prot_put(
        unsigned count,
        const char **fields
 ) {
-       return buf_put_fields(&prot->outbuf, count, fields);
+       int rc = 0;
+       unsigned index = 0;
+
+       while(!rc && index < count)
+               rc = prot_put_field(prot, fields[index++]);
+       if (rc)
+               prot_put_cancel(prot);
+       else
+               rc = prot_put_end(prot);
+       return rc;
 }
 
 /**
@@ -371,21 +451,21 @@ prot_putx(
        prot_t *prot,
        ...
 ) {
-       const char *p, *fields[MAXARGS];
-       unsigned n;
+       int rc = 0;
        va_list l;
+       const char *p;
 
        va_start(l, prot);
-       n = 0;
        p = va_arg(l, const char *);
-       while (p) {
-               if (n == MAXARGS)
-                       return -EINVAL;
-               fields[n++] = p;
+       while(!rc && p) {
+               rc = prot_put_field(prot, p);
                p = va_arg(l, const char *);
        }
-       va_end(l);
-       return prot_put(prot, n, fields);
+       if (rc)
+               prot_put_cancel(prot);
+       else
+               rc = prot_put_end(prot);
+       return rc;
 }
 
 /**
index b9f83ea..b961a37 100644 (file)
@@ -45,6 +45,33 @@ prot_reset(
        prot_t *prot
 );
 
+extern
+void
+prot_put_cancel(
+       prot_t *prot
+);
+
+extern
+int
+prot_put_end(
+       prot_t *prot
+);
+
+extern
+int
+prot_put_field(
+       prot_t *prot,
+       const char *field
+);
+
+extern
+int
+prot_put_fields(
+       prot_t *prot,
+       unsigned count,
+       const char **fields
+);
+
 extern
 int
 prot_put(
index 0699bb1..b02d7da 100644 (file)
@@ -21,6 +21,7 @@
 #include <string.h>
 #include <errno.h>
 
+#include "data.h"
 #include "db.h"
 
 #define DROP 0
@@ -123,34 +124,27 @@ qput_string(
 
 int
 queue_drop(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const data_key_t *key
 ) {
-       return qput_string(client)
-               && qput_string(session)
-               && qput_string(user)
-               && qput_string(permission)
+       return qput_string(key->client)
+               && qput_string(key->session)
+               && qput_string(key->user)
+               && qput_string(key->permission)
                && qput_string(0)
                        ? 0 : -(errno = ENOMEM);
 }
 
 int
 queue_set(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const data_key_t *key,
+       const data_value_t *value
 ) {
-       return qput_string(client)
-               && qput_string(session)
-               && qput_string(user)
-               && qput_string(permission)
-               && qput_string(value)
-               && qput_time(expire)
+       return qput_string(key->client)
+               && qput_string(key->session)
+               && qput_string(key->user)
+               && qput_string(key->permission)
+               && qput_string(value->value)
+               && qput_time(value->expire)
                        ? 0 : -(errno = ENOMEM);
 }
 
@@ -165,27 +159,23 @@ int
 queue_play(
 ) {
        int rc, rc2;
-       const char *client;
-       const char *session;
-       const char *user;
-       const char *permission;
-       const char *value;
-       time_t expire;
+       data_key_t key;
+       data_value_t value;
 
        rc = 0;
        queue.read = 0;
        while (queue.read < queue.write) {
                rc2 = -EINVAL;
-               if (qget_string(&client)
-                && qget_string(&session)
-                && qget_string(&user)
-                && qget_string(&permission)
-                && qget_string(&value)) {
-                       if (!value[0])
-                               rc2 = db_drop(client, session, user, permission);
+               if (qget_string(&key.client)
+                && qget_string(&key.session)
+                && qget_string(&key.user)
+                && qget_string(&key.permission)
+                && qget_string(&value.value)) {
+                       if (!value.value[0])
+                               rc2 = db_drop(&key);
                        else {
-                               if (qget_time(&expire))
-                                       rc2 = db_set(client, session, user, permission, value, expire);
+                               if (qget_time(&value.expire))
+                                       rc2 = db_set(&key, &value);
                        }
                }
                if (rc2 != 0 && rc == 0)
index a9fc454..7efc1f8 100644 (file)
 extern
 int
 queue_drop(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const data_key_t *key
 );
 
 extern
 int
 queue_set(
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const data_key_t *key,
+       const data_value_t *value
 );
 
 extern
index 47a5798..c5fda4b 100644 (file)
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
-
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -76,6 +74,9 @@ struct rcyn
        /** type of link */
        rcyn_type_t type;
 
+       /** spec of the socket */
+       const char *socketspec;
+
        /** protocol manager object */
        prot_t *prot;
 
@@ -117,8 +118,10 @@ flushw(
        int rc;
        struct pollfd pfd;
 
-       rc = prot_should_write(rcyn->prot);
-       while (rc) {
+       for (;;) {
+               rc = prot_should_write(rcyn->prot);
+               if (!rc)
+                       break;
                rc = prot_write(rcyn->prot, rcyn->fd);
                if (rc == -EAGAIN) {
                        pfd.fd = rcyn->fd;
@@ -130,7 +133,6 @@ flushw(
                if (rc < 0) {
                        break;
                }
-               rc = prot_should_write(rcyn->prot);
        }
        return rc;
 }
@@ -142,41 +144,99 @@ flushw(
  */
 static
 int
-putx(
+putxkv(
        rcyn_t *rcyn,
-       ...
+       const char *command,
+       const char *optarg,
+       const rcyn_key_t *optkey,
+       const rcyn_value_t *optval
 ) {
-       const char *p, *fields[MAXARGS];
-       unsigned n;
-       va_list l;
-       int rc;
-
-       /* reconstruct the array of arguments */
-       va_start(l, rcyn);
-       n = 0;
-       p = va_arg(l, const char *);
-       while (p && n < MAXARGS) {
-               fields[n++] = p;
-               p = va_arg(l, const char *);
-       }
-       va_end(l);
+       int rc, trial;
+       prot_t *prot;
+       char text[30];
 
-       /* put it to the output buffer */
-       rc = prot_put(rcyn->prot, n, fields);
-       if (rc == -ECANCELED) {
-               /* not enough room in the buffer, flush it */
-               rc = flushw(rcyn);
-               if (rc == 0)
-                       rc = prot_put(rcyn->prot, n, fields);
-       }
-       /* client always flushes */
-       if (rc == 0) {
-               rcyn->pending++;
+       prot = rcyn->prot;
+       for(trial = 0 ; ; trial++) {
+               rc = prot_put_field(prot, command);
+               if (!rc && optarg)
+                       rc = prot_put_field(prot, optarg);
+               if (!rc && optkey) {
+                       rc = prot_put_field(prot, optkey->client);
+                       if (!rc)
+                               rc = prot_put_field(prot, optkey->session);
+                       if (!rc)
+                               rc = prot_put_field(prot, optkey->user);
+                       if (!rc)
+                               rc = prot_put_field(prot, optkey->permission);
+               }
+               if (!rc && optval) {
+                       rc = prot_put_field(prot, optval->value);
+                       if (!rc) {
+                               if (!optval->expire)
+                                       text[0] = 0;
+                               else
+                                       snprintf(text, sizeof text, "%lld", (long long)optval->expire);
+                               rc = prot_put_field(prot, text);
+                       }
+               }
+               if (!rc)
+                       rc = prot_put_end(prot);
+               if (!rc) {
+                       /* client always flushes */
+                       rcyn->pending++;
+                       return flushw(rcyn);
+               }
+               prot_put_cancel(prot);
+               if (trial >= 1)
+                       return rc;
                rc = flushw(rcyn);
+               if (rc)
+                       return rc;
        }
-       return rc;
 }
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 static
 int
 wait_input(
@@ -342,26 +402,18 @@ connection(
        rcyn_t *rcyn
 ) {
        int rc;
-       const char *spec;
-
-       /* socket spec */
-       switch(rcyn->type) {
-       default:
-       case rcyn_Check: spec = rcyn_default_check_socket_spec; break;
-       case rcyn_Admin: spec = rcyn_default_admin_socket_spec; break;
-       }
 
        /* init the client */
        rcyn->pending = 0;
        rcyn->reply.count = -1;
        cache_clear(rcyn->cache);
        prot_reset(rcyn->prot);
-       rcyn->fd = socket_open(spec, 0);
+       rcyn->fd = socket_open(rcyn->socketspec, 0);
        if (rcyn->fd < 0)
                return -errno;
 
        /* negociate the protocol */
-       rc = putx(rcyn, _rcyn_, "1", NULL);
+       rc = putxkv(rcyn, _rcyn_, "1", 0, 0);
        if (rc >= 0) {
                rc = wait_pending_reply(rcyn);
                if (rc >= 0) {
@@ -395,13 +447,14 @@ int
 rcyn_open(
        rcyn_t **prcyn,
        rcyn_type_t type,
-       uint32_t cache_size
+       uint32_t cache_size,
+       const char *socketspec
 ) {
        rcyn_t *rcyn;
        int rc;
 
        /* allocate the structure */
-       *prcyn = rcyn = malloc(sizeof *rcyn);
+       *prcyn = rcyn = malloc(sizeof *rcyn + (socketspec ? strlen(socketspec) : 0));
        if (rcyn == NULL) {
                rc = -ENOMEM;
                goto error;
@@ -412,9 +465,21 @@ rcyn_open(
        if (rc < 0)
                goto error2;
 
+       /* socket spec */
+       if (socketspec)
+               strcpy((char*)(rcyn+1), socketspec);
+       else
+               switch(rcyn->type) {
+               default:
+               case rcyn_Check: socketspec = rcyn_default_check_socket_spec; break;
+               case rcyn_Admin: socketspec = rcyn_default_admin_socket_spec; break;
+               case rcyn_Agent: socketspec = rcyn_default_agent_socket_spec; break;
+               }
+
        /* record type and weakly create cache */
        cache_create(&rcyn->cache, cache_size < MIN_CACHE_SIZE ? MIN_CACHE_SIZE : cache_size);
        rcyn->type = type;
+       rcyn->socketspec = socketspec;
        rcyn->async.controlcb = NULL;
        rcyn->async.closure = 0;
        rcyn->async.requests = NULL;
@@ -457,7 +522,7 @@ rcyn_enter(
        if (rc < 0)
                return rc;
 
-       rc = putx(rcyn, _enter_, NULL);
+       rc = putxkv(rcyn, _enter_, 0, 0, 0);
        if (rc >= 0)
                rc = wait_done(rcyn);
        return rc;
@@ -478,7 +543,7 @@ rcyn_leave(
        if (rc < 0)
                return rc;
 
-       rc = putx(rcyn, _leave_, commit ? _commit_ : NULL/*default: rollback*/, NULL);
+       rc = putxkv(rcyn, _leave_, commit ? _commit_ : 0/*default: rollback*/, 0, 0);
        if (rc >= 0)
                rc = wait_done(rcyn);
        return rc;
@@ -488,10 +553,7 @@ static
 int
 check_or_test(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
+       const rcyn_key_t *key,
        const char *action
 ) {
        int rc;
@@ -507,19 +569,19 @@ check_or_test(
        flushr(rcyn);
 
        /* check cache item */
-       rc = cache_search(rcyn->cache, client, session, user, permission);
+       rc = cache_search(rcyn->cache, key);
        if (rc >= 0)
                return rc;
 
        /* send the request */
-       rc = putx(rcyn, action, client, session, user, permission, NULL);
+       rc = putxkv(rcyn, action, 0, key, 0);
        if (rc >= 0) {
                /* get the response */
                rc = wait_pending_reply(rcyn);
                if (rc >= 0) {
                        rc = status_check(rcyn, &expire);
                        if (rc >= 0)
-                               cache_put(rcyn->cache, client, session, user, permission, rc, expire);
+                               cache_put(rcyn->cache, key, rc, expire);
                }
        }
        return rc;
@@ -528,37 +590,25 @@ check_or_test(
 int
 rcyn_check(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 ) {
-       return check_or_test(rcyn, client, session, user, permission, _check_);
+       return check_or_test(rcyn, key, _check_);
 }
 
 int
 rcyn_test(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 ) {
-       return check_or_test(rcyn, client, session, user, permission, _test_);
+       return check_or_test(rcyn, key, _test_);
 }
 
 int
 rcyn_set(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const rcyn_key_t *key,
+       const rcyn_value_t *value
 ) {
-       char text[30];
-       const char *exp;
        int rc;
 
        if (rcyn->type != rcyn_Admin)
@@ -569,13 +619,7 @@ rcyn_set(
        if (rc < 0)
                return rc;
 
-       if (!expire)
-               exp = NULL;
-       else {
-               snprintf(text, sizeof text, "%lld", (long long)expire);
-               exp = text;
-       }
-       rc = putx(rcyn, _set_, client, session, user, permission, value, exp, NULL);
+       rc = putxkv(rcyn, _set_, 0, key, value);
        if (rc >= 0)
                rc = wait_done(rcyn);
        return rc;
@@ -584,22 +628,17 @@ rcyn_set(
 int
 rcyn_get(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
+       const rcyn_key_t *key,
        void (*callback)(
                void *closure,
-               const char *client,
-               const char *session,
-               const char *user,
-               const char *permission,
-               const char *value,
-               time_t expire
+               const rcyn_key_t *key,
+               const rcyn_value_t *value
        ),
        void *closure
 ) {
        int rc;
+       rcyn_key_t k;
+       rcyn_value_t v;
 
        if (rcyn->type != rcyn_Admin)
                return -EPERM;
@@ -609,17 +648,17 @@ rcyn_get(
        if (rc < 0)
                return rc;
 
-       rc = putx(rcyn, _get_, client, session, user, permission, NULL);
+       rc = putxkv(rcyn, _get_, 0, key, 0);
        if (rc >= 0) {
                rc = wait_reply(rcyn, true);
                while ((rc == 6 || rc == 7) && !strcmp(rcyn->reply.fields[0], _item_)) {
-                       callback(closure,
-                               rcyn->reply.fields[1],
-                               rcyn->reply.fields[2],
-                               rcyn->reply.fields[3],
-                               rcyn->reply.fields[4],
-                               rcyn->reply.fields[5],
-                               rc == 6 ? 0 : (time_t)strtoll(rcyn->reply.fields[6], NULL, 10));
+                       k.client = rcyn->reply.fields[1];
+                       k.session = rcyn->reply.fields[2];
+                       k.user = rcyn->reply.fields[3];
+                       k.permission = rcyn->reply.fields[4];
+                       v.value = rcyn->reply.fields[5];
+                       v.expire = rc == 6 ? 0 : (time_t)strtoll(rcyn->reply.fields[6], NULL, 10);
+                       callback(closure, &k, &v);
                        rc = wait_reply(rcyn, true);
                }
                rc = status_done(rcyn);
@@ -630,10 +669,7 @@ rcyn_get(
 int
 rcyn_drop(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 ) {
        int rc;
 
@@ -645,7 +681,7 @@ rcyn_drop(
        if (rc < 0)
                return rc;
 
-       rc = putx(rcyn, _drop_, client, session, user, permission, NULL);
+       rc = putxkv(rcyn, _drop_, 0, key, 0);
        if (rc >= 0)
                rc = wait_done(rcyn);
        return rc;
@@ -671,12 +707,9 @@ rcyn_cache_clear(
 int
 rcyn_cache_check(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 ) {
-       return cache_search(rcyn->cache, client, session, user, permission);
+       return cache_search(rcyn->cache, key);
 }
 
 
@@ -712,8 +745,8 @@ rcyn_async_process(
        int rc;
        const char *first;
        asreq_t *ar;
-       const char *client, *session, *user, *permission;
        time_t expire;
+       rcyn_key_t key;
 
        for (;;) {
                /* non blocking wait for a reply */
@@ -740,11 +773,11 @@ rcyn_async_process(
                rcyn->async.requests = ar->next;
                rc = status_check(rcyn, &expire);
                if (rc >= 0) {
-                       client = (const char*)(ar + 1);
-                       session = &client[1 + strlen(client)];
-                       user = &session[1 + strlen(session)];
-                       permission = &user[1 + strlen(user)];
-                       cache_put(rcyn->cache, client, session, user, permission, rc, expire);
+                       key.client = (const char*)(ar + 1);
+                       key.session = &key.client[1 + strlen(key.client)];
+                       key.user = &key.session[1 + strlen(key.session)];
+                       key.permission = &key.user[1 + strlen(key.user)];
+                       cache_put(rcyn->cache, &key, rc, expire);
                }
                ar->callback(ar->closure, rc);
                free(ar);
@@ -754,10 +787,7 @@ rcyn_async_process(
 int
 rcyn_async_check(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
+       const rcyn_key_t *key,
        bool simple,
        void (*callback)(
                void *closure,
@@ -772,7 +802,7 @@ rcyn_async_check(
                return rc;
 
        /* allocate */
-       ar = malloc(sizeof *ar + strlen(client) + strlen(session) + strlen(user) + strlen(permission) + 4);
+       ar = malloc(sizeof *ar + strlen(key->client) + strlen(key->session) + strlen(key->user) + strlen(key->permission) + 4);
        if (ar == NULL)
                return -ENOMEM;
 
@@ -780,11 +810,10 @@ rcyn_async_check(
        ar->next = NULL;
        ar->callback = callback;
        ar->closure = closure;
-       stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)(ar + 1), client), session), user), permission);
+       stpcpy(1 + stpcpy(1 + stpcpy(1 + stpcpy((char*)(ar + 1), key->client), key->session), key->user), key->permission);
 
        /* send the request */
-       rc = putx(rcyn, simple ? _test_ : _check_,
-               client, session, user, permission, NULL);
+       rc = putxkv(rcyn, simple ? _test_ : _check_, 0, key, 0);
        if (rc >= 0)
                rc = flushw(rcyn);
        if (rc < 0) {
@@ -800,4 +829,3 @@ rcyn_async_check(
        return 0;
 }
 
-
index 611f843..48ecbf6 100644 (file)
 
 #pragma once
 
-typedef enum rcyn_type {
-       rcyn_Check,
-       rcyn_Admin
-} rcyn_type_t;
-
-struct rcyn;
 typedef struct rcyn rcyn_t;
+typedef enum rcyn_type rcyn_type_t;
+typedef struct rcyn_key rcyn_key_t;
+typedef struct rcyn_value rcyn_value_t;
+
+enum rcyn_type {
+       rcyn_Check,
+       rcyn_Admin,
+       rcyn_Agent
+};
+
+struct rcyn_key {
+       const char *client;
+       const char *session;
+       const char *user;
+       const char *permission;
+};
+
+struct rcyn_value {
+       const char *value;
+       time_t expire;
+};
 
 extern
 int
 rcyn_open(
        rcyn_t **rcyn,
        rcyn_type_t type,
-       uint32_t cache_size
+       uint32_t cache_size,
+       const char *socketspec
 );
 
 extern
@@ -57,50 +73,33 @@ extern
 int
 rcyn_check(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 );
 
 extern
 int
 rcyn_test(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 );
 
 extern
 int
 rcyn_set(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const rcyn_key_t *key,
+       const rcyn_value_t *value
 );
 
 extern
 int
 rcyn_get(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
+       const rcyn_key_t *key,
        void (*callback)(
                void *closure,
-               const char *client,
-               const char *session,
-               const char *user,
-               const char *permission,
-               const char *value,
-               time_t expire
+               const rcyn_key_t *key,
+               const rcyn_value_t *value
        ),
        void *closure
 );
@@ -109,10 +108,7 @@ extern
 int
 rcyn_drop(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 );
 
 extern
@@ -125,10 +121,7 @@ extern
 int
 rcyn_cache_check(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission
+       const rcyn_key_t *key
 );
 
 typedef int (*rcyn_async_ctl_t)(
@@ -155,11 +148,8 @@ extern
 int
 rcyn_async_check(
        rcyn_t *rcyn,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       bool test,
+       const rcyn_key_t *key,
+       bool simple,
        void (*callback)(
                void *closure,
                int status),
index 868e81f..d08c81b 100644 (file)
@@ -18,6 +18,7 @@
 #include "rcyn-protocol.h"
 
 const char
+       _agent_[] = "agent",
        _check_[] = "check",
        _drop_[] = "drop",
        _enter_[] = "enter",
@@ -39,12 +40,43 @@ const char
        _no_[] = "no",
        _yes_[] = "yes";
 
+#if !defined(RCYN_DEFAULT_SOCKET_SCHEME)
+#    define  RCYN_DEFAULT_SOCKET_SCHEME  "unix"
+#endif
+
+#if !defined(RCYN_DEFAULT_SOCKET_DIR)
+#    define  RCYN_DEFAULT_SOCKET_DIR  "/var/run/cynara"
+#endif
+
+#define  DEF_PREFIX  RCYN_DEFAULT_SOCKET_SCHEME":"RCYN_DEFAULT_SOCKET_DIR"/"
+
+#if !defined(RCYN_DEFAULT_CHECK_SOCKET_BASE)
+# define RCYN_DEFAULT_CHECK_SOCKET_BASE "cynara.check"
+#endif
+#if !defined(RCYN_DEFAULT_ADMIN_SOCKET_BASE)
+# define RCYN_DEFAULT_ADMIN_SOCKET_BASE "cynara.admin"
+#endif
+#if !defined(RCYN_DEFAULT_AGENT_SOCKET_BASE)
+# define RCYN_DEFAULT_AGENT_SOCKET_BASE "cynara.agent"
+#endif
+
+
 #if !defined(RCYN_DEFAULT_CHECK_SOCKET_SPEC)
-# define RCYN_DEFAULT_CHECK_SOCKET_SPEC "unix:/run/platform/cynara.check"
+# define RCYN_DEFAULT_CHECK_SOCKET_SPEC   DEF_PREFIX RCYN_DEFAULT_CHECK_SOCKET_BASE
 #endif
 #if !defined(RCYN_DEFAULT_ADMIN_SOCKET_SPEC)
-# define RCYN_DEFAULT_ADMIN_SOCKET_SPEC "unix:/run/platform/cynara.admin"
+# define RCYN_DEFAULT_ADMIN_SOCKET_SPEC   DEF_PREFIX RCYN_DEFAULT_ADMIN_SOCKET_BASE
+#endif
+#if !defined(RCYN_DEFAULT_AGENT_SOCKET_SPEC)
+# define RCYN_DEFAULT_AGENT_SOCKET_SPEC   DEF_PREFIX RCYN_DEFAULT_AGENT_SOCKET_BASE
 #endif
+
 const char
+       rcyn_default_socket_scheme[] = RCYN_DEFAULT_SOCKET_SCHEME,
+       rcyn_default_socket_dir[] = RCYN_DEFAULT_SOCKET_DIR,
+       rcyn_default_check_socket_base[] = RCYN_DEFAULT_CHECK_SOCKET_BASE,
+       rcyn_default_admin_socket_base[] = RCYN_DEFAULT_ADMIN_SOCKET_BASE,
+       rcyn_default_agent_socket_base[] = RCYN_DEFAULT_AGENT_SOCKET_BASE,
        rcyn_default_check_socket_spec[] = RCYN_DEFAULT_CHECK_SOCKET_SPEC,
-       rcyn_default_admin_socket_spec[] = RCYN_DEFAULT_ADMIN_SOCKET_SPEC;
+       rcyn_default_admin_socket_spec[] = RCYN_DEFAULT_ADMIN_SOCKET_SPEC,
+       rcyn_default_agent_socket_spec[] = RCYN_DEFAULT_AGENT_SOCKET_SPEC;
index 631c0b7..386bd94 100644 (file)
@@ -18,6 +18,7 @@
 #pragma once
 
 extern const char
+       _agent_[],
        _check_[],
        _drop_[],
        _enter_[],
@@ -40,6 +41,11 @@ extern const char
        _yes_[];
 
 extern const char
+       rcyn_default_socket_scheme[],
+       rcyn_default_socket_dir[],
+       rcyn_default_check_socket_base[],
+       rcyn_default_admin_socket_base[],
+       rcyn_default_agent_socket_base[],
        rcyn_default_check_socket_spec[],
-       rcyn_default_admin_socket_spec[];
-
+       rcyn_default_admin_socket_spec[],
+       rcyn_default_agent_socket_spec[];
index 7a35ab9..50fd918 100644 (file)
@@ -55,7 +55,7 @@ register agent (agent):
 asking (agent ask CLIENT SESSION USER PERMISSION):
 
   s->c ask CLIENT SESSION USER PERMISSION
-  c->s done | ([yes|no] [always|session|one-time])
+  c->s done | ([yes|no] [always|session|one-time|EXPIRE])
 
 
 ----------------------------------------------------------
index 13f3a2e..14a9243 100644 (file)
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
-
 #include <stdbool.h>
 #include <stdint.h>
 #include <stddef.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#include "data.h"
 #include "prot.h"
 #include "cyn.h"
 #include "rcyn-protocol.h"
 #include "rcyn-server.h"
 #include "socket.h"
+#include "pollitem.h"
 
 typedef enum rcyn_type {
        rcyn_Check,
+       rcyn_Agent,
        rcyn_Admin
 } rcyn_type_t;
 
 
-/** structure for using epoll easily */
-typedef struct pollitem pollitem_t;
-struct pollitem
-{
-       /** callback on event */
-       void (*handler)(pollitem_t *pollitem, uint32_t events, int pollfd);
-
-       /** data */
-       void *closure;
-
-       /** file */
-       int fd;
-};
-
-static
-int
-pollitem_do(
-       pollitem_t *pollitem,
-       uint32_t events,
-       int pollfd,
-       int op
-) {
-       struct epoll_event ev = { .events = events, .data.ptr = pollitem };
-       return epoll_ctl(pollfd, op, pollitem->fd, &ev);
-}
-
-static
-int
-pollitem_add(
-       pollitem_t *pollitem,
-       uint32_t events,
-       int pollfd
-) {
-       return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_ADD);
-}
-
-#if 0
-static
-int
-pollitem_mod(
-       pollitem_t *pollitem,
-       uint32_t events,
-       int pollfd
-) {
-       return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_MOD);
-}
-#endif
-
-static
-int
-pollitem_del(
-       pollitem_t *pollitem,
-       int pollfd
-) {
-       return pollitem_do(pollitem, 0, pollfd, EPOLL_CTL_DEL);
-}
-
-
 /** structure that represents a rcyn client */
 struct client
 {
@@ -124,7 +67,7 @@ struct client
        /** enter/leave status, record if entered */
        unsigned entered: 1;
 
-       /** enter/leave status, record if entring pending */
+       /** enter/leave status, record if enter is pending */
        unsigned entering: 1;
 
        /** indicate if some check were made */
@@ -147,6 +90,9 @@ struct rcyn_server
        /** the admin socket */
        pollitem_t admin;
 
+       /** the agent socket */
+       pollitem_t agent;
+
        /** the check socket */
        pollitem_t check;
 };
@@ -181,18 +127,20 @@ flushw(
        int rc;
        struct pollfd pfd;
 
-       rc = prot_should_write(cli->prot);
-       while (rc) {
+       for(;;) {
+               rc = prot_should_write(cli->prot);
+               if (!rc)
+                       break;
                rc = prot_write(cli->prot, cli->pollitem.fd);
                if (rc == -EAGAIN) {
                        pfd.fd = cli->pollitem.fd;
                        pfd.events = POLLOUT;
                        do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR);
+                       if (rc < 0)
+                               rc = -errno;
                }
-               if (rc < 0) {
+               if (rc < 0)
                        break;
-               }
-               rc = prot_should_write(cli->prot);
        }
        return rc;
 }
@@ -298,16 +246,20 @@ static
 void
 checkcb(
        void *closure,
-       const char *value,
-       time_t expire
+       const data_value_t *value
 ) {
        client_t *cli = closure;
        char text[30];
 
-       if (strcmp(value, ALLOW) && strcmp(value, DENY))
-               putx(cli, _done_, value, exp2txt(expire, text, sizeof text), NULL);
-       else
-               putx(cli, value, exp2txt(expire, text, sizeof text), NULL);
+       if (!value)
+               putx(cli, DEFAULT, "0", NULL);
+       else {
+               exp2txt(value->expire, text, sizeof text);
+               if (strcmp(value->value, ALLOW) && strcmp(value->value, DENY))
+                       putx(cli, _done_, value, text, NULL);
+               else
+                       putx(cli, value->value, text, NULL);
+       }
        flushw(cli);
 }
 
@@ -316,18 +268,14 @@ static
 void
 getcb(
        void *closure,
-       const char *client,
-       const char *session,
-       const char *user,
-       const char *permission,
-       const char *value,
-       time_t expire
+       const data_key_t *key,
+       const data_value_t *value
 ) {
        client_t *cli = closure;
        char text[30];
 
-       putx(cli, _item_, client, session, user, permission,
-               value, exp2txt(expire, text, sizeof text), NULL);
+       putx(cli, _item_, key->client, key->session, key->user, key->permission,
+               value->value, exp2txt(value->expire, text, sizeof text), NULL);
 }
 
 /** handle a request */
@@ -339,8 +287,8 @@ onrequest(
        const char *args[]
 ) {
        int rc;
-       const char *value;
-       time_t expire;
+       data_key_t key;
+       data_value_t value;
 
        /* just ignore empty lines */
        if (count == 0)
@@ -357,10 +305,22 @@ onrequest(
        }
 
        switch(args[0][0]) {
+       case 'a': /* agent */
+               if (ckarg(args[0], _agent_, 1) && count == 5) {
+                       if (cli->type != rcyn_Agent)
+                               break;
+                       /* TODO */ break;
+                       return;
+               }
+               break;
        case 'c': /* check */
                if (ckarg(args[0], _check_, 1) && count == 5) {
                        cli->checked = 1;
-                       cyn_check_async(checkcb, cli, args[1], args[2], args[3], args[4]);
+                       key.client = args[1];
+                       key.session = args[2];
+                       key.user = args[3];
+                       key.permission = args[4];
+                       cyn_check_async(checkcb, cli, &key);
                        return;
                }
                break;
@@ -370,7 +330,11 @@ onrequest(
                                break;
                        if (!cli->entered)
                                break;
-                       rc = cyn_drop(args[1], args[2], args[3], args[4]);
+                       key.client = args[1];
+                       key.session = args[2];
+                       key.user = args[3];
+                       key.permission = args[4];
+                       rc = cyn_drop(&key);
                        send_done_or_error(cli, rc);
                        return;
                }
@@ -391,7 +355,11 @@ onrequest(
                if (ckarg(args[0], _get_, 1) && count == 5) {
                        if (cli->type != rcyn_Admin)
                                break;
-                       cyn_list(cli, getcb, args[1], args[2], args[3], args[4]);
+                       key.client = args[1];
+                       key.session = args[2];
+                       key.user = args[3];
+                       key.permission = args[4];
+                       cyn_list(cli, getcb, &key);
                        send_done(cli);
                        return;
                }
@@ -417,10 +385,15 @@ onrequest(
                        if (!cli->entered)
                                break;
                        if (count == 6)
-                               expire = 0;
+                               value.expire = 0;
                        else
-                               expire = strtoll(args[6], NULL, 10);
-                       rc = cyn_set(args[1], args[2], args[3], args[4], args[5], expire);
+                               value.expire = strtoll(args[6], NULL, 10);
+                       key.client = args[1];
+                       key.session = args[2];
+                       key.user = args[3];
+                       key.permission = args[4];
+                       value.value = args[5];
+                       rc = cyn_set(&key, &value);
                        send_done_or_error(cli, rc);
                        return;
                }
@@ -428,8 +401,12 @@ onrequest(
        case 't': /* test */
                if (ckarg(args[0], _test_, 1) && count == 5) {
                        cli->checked = 1;
-                       cyn_test(args[1], args[2], args[3], args[4], &value, &expire);
-                       checkcb(cli, value, expire);
+                       key.client = args[1];
+                       key.session = args[2];
+                       key.user = args[3];
+                       key.permission = args[4];
+                       cyn_test(&key, &value);
+                       checkcb(cli, &value);
                        return;
                }
                break;
@@ -633,6 +610,17 @@ on_admin_server_event(
        on_server_event(pollitem, events, pollfd, rcyn_Admin);
 }
 
+/** handle admin server events */
+static
+void
+on_agent_server_event(
+       pollitem_t *pollitem,
+       uint32_t events,
+       int pollfd
+) {
+       on_server_event(pollitem, events, pollfd, rcyn_Agent);
+}
+
 /** destroy a server */
 void
 rcyn_server_destroy(
@@ -654,7 +642,8 @@ int
 rcyn_server_create(
        rcyn_server_t **server,
        const char *admin_socket_spec,
-       const char *check_socket_spec
+       const char *check_socket_spec,
+       const char *agent_socket_spec
 ) {
        rcyn_server_t *srv;
        int rc;
@@ -668,7 +657,7 @@ rcyn_server_create(
        }
 
        /* create the polling fd */
-       srv->admin.fd = srv->check.fd = -1;
+       srv->admin.fd = srv->check.fd = srv->agent.fd = -1;
        srv->pollfd = epoll_create1(EPOLL_CLOEXEC);
        if (srv->pollfd < 0) {
                rc = -errno;
@@ -685,7 +674,7 @@ rcyn_server_create(
                goto error2;
        }
 
-       /* add the server to pollfd */
+       /* add the admin server to pollfd */
        srv->admin.handler = on_admin_server_event;
        srv->admin.closure = srv;
        rc = pollitem_add(&srv->admin, EPOLLIN, srv->pollfd);
@@ -695,7 +684,7 @@ rcyn_server_create(
                goto error2;
        }
 
-       /* create the server socket */
+       /* create the check server socket */
        check_socket_spec = check_socket_spec ?: rcyn_default_check_socket_spec;
        srv->check.fd = socket_open(check_socket_spec, 1);
        if (srv->check.fd < 0) {
@@ -704,7 +693,7 @@ rcyn_server_create(
                goto error2;
        }
 
-       /* add the server to pollfd */
+       /* add the check server to pollfd */
        srv->check.handler = on_check_server_event;
        srv->check.closure = srv;
        rc = pollitem_add(&srv->check, EPOLLIN, srv->pollfd);
@@ -713,6 +702,26 @@ rcyn_server_create(
                fprintf(stderr, "can't poll check server: %m\n");
                goto error2;
        }
+
+       /* create the agent server socket */
+       agent_socket_spec = agent_socket_spec ?: rcyn_default_agent_socket_spec;
+       srv->agent.fd = socket_open(agent_socket_spec, 1);
+       if (srv->agent.fd < 0) {
+               rc = -errno;
+               fprintf(stderr, "can't create agent server socket %s: %m\n", agent_socket_spec);
+               goto error2;
+       }
+
+       /* add the agent server to pollfd */
+       srv->agent.handler = on_agent_server_event;
+       srv->agent.closure = srv;
+       rc = pollitem_add(&srv->agent, EPOLLIN, srv->pollfd);
+       if (rc < 0) {
+               rc = -errno;
+               fprintf(stderr, "can't poll agent server: %m\n");
+               goto error2;
+       }
+
        return 0;
 
 error2:
@@ -722,6 +731,8 @@ error2:
                close(srv->admin.fd);
        if (srv->check.fd >= 0)
                close(srv->check.fd);
+       if (srv->agent.fd >= 0)
+               close(srv->agent.fd);
        free(srv);
 error:
        *server = NULL;
@@ -742,18 +753,10 @@ int
 rcyn_server_serve(
        rcyn_server_t *server
 ) {
-       int rc;
-       struct epoll_event ev;
-       pollitem_t *pi;
-
        /* process inputs */
        server->stopped = 0;
        while(!server->stopped) {
-               rc = epoll_wait(server->pollfd, &ev, 1, -1);
-               if (rc == 1) {
-                       pi = ev.data.ptr;
-                       pi->handler(pi, ev.events, server->pollfd);
-               }
+               pollitem_wait_dispatch(server->pollfd, -1);
        }
        return server->stopped == INT_MIN ? 0 : server->stopped;
 }
index 2908c52..5758070 100644 (file)
@@ -32,7 +32,8 @@ int
 rcyn_server_create(
        rcyn_server_t **server,
        const char *admin_socket_spec,
-       const char *check_socket_spec
+       const char *check_socket_spec,
+       const char *agent_socket_spec
 );
 
 extern
index b908cf0..5dbfa9e 100644 (file)
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
-
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>