diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/CMakeLists.txt | 35 | ||||
-rw-r--r-- | src/cache.c | 48 | ||||
-rw-r--r-- | src/cache.h | 10 | ||||
-rw-r--r-- | src/cyn.c | 257 | ||||
-rw-r--r-- | src/cyn.h | 110 | ||||
-rw-r--r-- | src/data.h | 39 | ||||
-rw-r--r-- | src/db.c | 322 | ||||
-rw-r--r-- | src/db.h | 48 | ||||
-rw-r--r-- | src/dbinit.c | 125 | ||||
-rw-r--r-- | src/dbinit.h | 22 | ||||
-rw-r--r-- | src/expire.c | 84 | ||||
-rw-r--r-- | src/expire.h | 21 | ||||
-rw-r--r-- | src/fbuf.c | 268 | ||||
-rw-r--r-- | src/fbuf.h | 29 | ||||
-rw-r--r-- | src/lib-compat.c | 72 | ||||
-rw-r--r-- | src/main-cynadm.c | 223 | ||||
-rw-r--r-- | src/main-cynarad.c | 155 | ||||
-rw-r--r-- | src/pollitem.c | 91 | ||||
-rw-r--r-- | src/pollitem.h | 61 | ||||
-rw-r--r-- | src/prot.c | 196 | ||||
-rw-r--r-- | src/prot.h | 27 | ||||
-rw-r--r-- | src/queue.c | 60 | ||||
-rw-r--r-- | src/queue.h | 13 | ||||
-rw-r--r-- | src/rcyn-client.c | 272 | ||||
-rw-r--r-- | src/rcyn-client.h | 78 | ||||
-rw-r--r-- | src/rcyn-protocol.c | 38 | ||||
-rw-r--r-- | src/rcyn-protocol.h | 10 | ||||
-rw-r--r-- | src/rcyn-protocol.txt | 2 | ||||
-rw-r--r-- | src/rcyn-server.c | 211 | ||||
-rw-r--r-- | src/rcyn-server.h | 3 | ||||
-rw-r--r-- | src/socket.c | 2 |
32 files changed, 1716 insertions, 1217 deletions
@@ -1,2 +1,3 @@ build/ .*.sw* +nbproject/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 549d401..cacd659 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,8 +19,11 @@ 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 @@ -72,9 +62,24 @@ 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}) diff --git a/src/cache.c b/src/cache.c index 9bbe667..b57536d 100644 --- a/src/cache.c +++ b/src/cache.c @@ -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; diff --git a/src/cache.h b/src/cache.h index bd56601..bf85e8e 100644 --- a/src/cache.h +++ b/src/cache.h @@ -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 ); @@ -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" @@ -33,26 +31,48 @@ 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); +} + @@ -18,10 +18,20 @@ #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 index 0000000..10cc599 --- /dev/null +++ b/src/data.h @@ -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; +}; + @@ -15,10 +15,6 @@ * limitations under the License. */ - -#define _GNU_SOURCE - - #include <assert.h> #include <stdlib.h> #include <stdint.h> @@ -26,9 +22,11 @@ #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" @@ -52,29 +50,33 @@ #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; } @@ -714,25 +723,46 @@ cmpidxs( } 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++; @@ -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 index 0000000..b1b3c54 --- /dev/null +++ b/src/dbinit.c @@ -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 index 0000000..bca4afc --- /dev/null +++ b/src/dbinit.h @@ -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 index 0000000..1571db7 --- /dev/null +++ b/src/expire.c @@ -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 index 0000000..c9e46bc --- /dev/null +++ b/src/expire.h @@ -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); @@ -15,10 +15,6 @@ * 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; } @@ -22,29 +22,26 @@ */ 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 diff --git a/src/lib-compat.c b/src/lib-compat.c index b9934af..175ec5f 100644 --- a/src/lib-compat.c +++ b/src/lib-compat.c @@ -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 *********************************/ diff --git a/src/main-cynadm.c b/src/main-cynadm.c index fa44895..1aaedde 100644 --- a/src/main-cynadm.c +++ b/src/main-cynadm.c @@ -20,95 +20,59 @@ #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; } - diff --git a/src/main-cynarad.c b/src/main-cynarad.c index 1829d74..ac8106e 100644 --- a/src/main-cynarad.c +++ b/src/main-cynarad.c @@ -15,8 +15,6 @@ * limitations under the License. */ -#define _GNU_SOURCE - #include <stdlib.h> #include <stdint.h> #include <stdbool.h> @@ -38,16 +36,15 @@ #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 index 0000000..d183594 --- /dev/null +++ b/src/pollitem.c @@ -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 index 0000000..a60b7b8 --- /dev/null +++ b/src/pollitem.h @@ -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 +); @@ -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; } /** @@ -46,6 +46,33 @@ prot_reset( ); 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( prot_t *prot, diff --git a/src/queue.c b/src/queue.c index 0699bb1..b02d7da 100644 --- a/src/queue.c +++ b/src/queue.c @@ -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) diff --git a/src/queue.h b/src/queue.h index a9fc454..7efc1f8 100644 --- a/src/queue.h +++ b/src/queue.h @@ -19,21 +19,14 @@ 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 diff --git a/src/rcyn-client.c b/src/rcyn-client.c index 47a5798..c5fda4b 100644 --- a/src/rcyn-client.c +++ b/src/rcyn-client.c @@ -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; } - diff --git a/src/rcyn-client.h b/src/rcyn-client.h index 611f843..48ecbf6 100644 --- a/src/rcyn-client.h +++ b/src/rcyn-client.h @@ -18,20 +18,36 @@ #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), diff --git a/src/rcyn-protocol.c b/src/rcyn-protocol.c index 868e81f..d08c81b 100644 --- a/src/rcyn-protocol.c +++ b/src/rcyn-protocol.c @@ -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; diff --git a/src/rcyn-protocol.h b/src/rcyn-protocol.h index 631c0b7..386bd94 100644 --- a/src/rcyn-protocol.h +++ b/src/rcyn-protocol.h @@ -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[]; diff --git a/src/rcyn-protocol.txt b/src/rcyn-protocol.txt index 7a35ab9..50fd918 100644 --- a/src/rcyn-protocol.txt +++ b/src/rcyn-protocol.txt @@ -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]) ---------------------------------------------------------- diff --git a/src/rcyn-server.c b/src/rcyn-server.c index 13f3a2e..14a9243 100644 --- a/src/rcyn-server.c +++ b/src/rcyn-server.c @@ -15,8 +15,6 @@ * limitations under the License. */ -#define _GNU_SOURCE - #include <stdbool.h> #include <stdint.h> #include <stddef.h> @@ -33,76 +31,21 @@ #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; } diff --git a/src/rcyn-server.h b/src/rcyn-server.h index 2908c52..5758070 100644 --- a/src/rcyn-server.h +++ b/src/rcyn-server.h @@ -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 diff --git a/src/socket.c b/src/socket.c index b908cf0..5dbfa9e 100644 --- a/src/socket.c +++ b/src/socket.c @@ -15,8 +15,6 @@ * limitations under the License. */ -#define _GNU_SOURCE - #include <stdlib.h> #include <string.h> #include <assert.h> |