From f161cd8b8b83a29f42c275fe31a96825a8d27d86 Mon Sep 17 00:00:00 2001 From: Jose Bollo Date: Mon, 30 Sep 2019 17:40:04 +0200 Subject: Improve comments Signed-off-by: Jose Bollo --- src/agent-at.c | 51 +++--- src/agent-at.h | 6 +- src/anydb.c | 435 ++++++++++++++++++++++++++++++++++------------------ src/anydb.h | 213 +++++++++++++++++++++---- src/cache.c | 113 +++++++++++--- src/cache.h | 43 +++++- src/cyn.c | 235 ++++++++++++++++------------ src/cyn.h | 136 +++++++++++++++- src/data.h | 58 +++++-- src/db.c | 76 ++++++--- src/db.h | 116 ++++++++++---- src/dbinit.c | 9 +- src/dbinit.h | 50 +++++- src/expire.c | 13 +- src/expire.h | 45 +++++- src/fbuf.c | 75 +++++---- src/fbuf.h | 93 +++++++++-- src/filedb.c | 412 ++++++++++++++++++++++++++++++++----------------- src/filedb.h | 15 +- src/lib-compat.c | 6 +- src/main-cynadm.c | 5 + src/main-cynarad.c | 5 + src/memdb.c | 188 +++++++++++++++-------- src/memdb.h | 10 +- src/pollitem.c | 33 ++-- src/pollitem.h | 45 +++++- src/prot.c | 147 +++++++++--------- src/prot.h | 122 +++++++++++++-- src/queue.c | 64 +++++++- src/queue.h | 31 +++- src/rcyn-client.c | 5 + src/rcyn-client.h | 8 +- src/rcyn-protocol.c | 33 ++-- src/rcyn-protocol.h | 48 ++++-- src/rcyn-server.c | 13 +- src/rcyn-server.h | 7 +- src/socket.c | 7 +- src/socket.h | 6 +- 38 files changed, 2196 insertions(+), 781 deletions(-) diff --git a/src/agent-at.c b/src/agent-at.c index e646ab7..21193f1 100644 --- a/src/agent-at.c +++ b/src/agent-at.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF AGENT AT (@) */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -27,19 +32,19 @@ /** * Parse the spec to extract the derived key to ask. * - * @param spec The specification of the derived key - * @param rkey The originally requested key - * @param key The derived key or NULL for computing length - * @param buffer The buffer that handles texts or NULL for computing length - * @param szbuf The size of the buffer or 0 for computing length + * @param spec The specification of the derived key + * @param reference_key The originally requested key of reference + * @param derived_key The derived key or NULL for computing length + * @param buffer The buffer that handles texts or NULL for computing length + * @param szbuf The size of the buffer or 0 for computing length * @return the total length of buffer used */ static size_t parse( const char *spec, - const data_key_t *rkey, - data_key_t *key, + const data_key_t *reference_key, + data_key_t *derived_key, char *buffer, size_t szbuf ) { @@ -47,8 +52,10 @@ parse( const char *val; int sub; + /* iterate through the fields of the key */ iout = 0; for (ikey = 0 ; ikey < KeyIdx_Count ; ikey++) { + /* compute value of the derived field */ inf = iout; while(*spec) { if (*spec == ':' && ikey < 3) { @@ -66,19 +73,19 @@ parse( /* what % substitution is it? */ switch(spec[1]) { case 'c': - val = rkey->client; + val = reference_key->client; sub = 1; break; case 's': - val = rkey->session; + val = reference_key->session; sub = 1; break; case 'u': - val = rkey->user; + val = reference_key->user; sub = 1; break; case 'p': - val = rkey->permission; + val = reference_key->permission; sub = 1; break; default: @@ -97,7 +104,7 @@ parse( if (iout < szbuf) buffer[iout] = spec[1]; iout++; - } else if (val) { + } else if (val != NULL) { /* substitution of the value */ while (*val) { if (iout < szbuf) @@ -109,8 +116,9 @@ parse( spec += 2; } } + /* standardize the found item*/ if (inf == iout) - val = 0; /* empty key item */ + val = NULL; /* empty key item */ else { /* set zero ended key */ val = &buffer[inf]; @@ -118,8 +126,9 @@ parse( buffer[iout] = 0; iout++; } - if (key) - key->keys[ikey] = val; + /* record the value */ + if (derived_key) + derived_key->keys[ikey] = val; } return iout; } @@ -131,9 +140,8 @@ parse( * @param agent_closure closure of the agent (not used) * @param key the original searched key * @param value the value found (string after @:) - * @param on_result_cb callback that will asynchronously handle the result - * @param on_result_closure closure for 'on_result_cb' - * @return + * @param query the query identifer for replying or subquerying + * @return 0 in case of success or -errno like negative error code */ static int @@ -149,13 +157,16 @@ agent_at_cb( size_t size; /* compute the length */ - size = parse(value, key, 0, 0, 0); + size = parse(value, key, NULL, NULL, 0); + /* alloc the length locally */ block = alloca(size); + /* initialize the derived key */ parse(value, key, &atkey, block, size); + /* ask for the derived key */ - return cyn_subquery_async(query, (on_result_cb_t*)cyn_reply_query, query, &atkey); + return cyn_query_subquery_async(query, (on_result_cb_t*)cyn_query_reply, query, &atkey); } /* see agent-at.h */ diff --git a/src/agent-at.h b/src/agent-at.h index 60dd168..68f1d3a 100644 --- a/src/agent-at.h +++ b/src/agent-at.h @@ -14,8 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF AGENT AT (@) */ +/******************************************************************************/ +/******************************************************************************/ /** * Activate the AT-agent diff --git a/src/anydb.c b/src/anydb.c index d522c96..9196e5c 100644 --- a/src/anydb.c +++ b/src/anydb.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* HIGH LEVEL ABSTRACTION OF THE DATABASES */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -31,13 +36,38 @@ #define USER_MATCH_SCORE 1 #define PERMISSION_MATCH_SCORE 1 +/** + * helper for searching items + */ +union searchkey { + struct { + /** client id */ + anydb_idx_t idxcli; + + /** session id */ + anydb_idx_t idxses; + + /** user id */ + anydb_idx_t idxusr; + + /** permission string */ + const char *strperm; + }; + anydb_key_t key; +}; +typedef union searchkey searchkey_t; + /******************************************************************************/ /******************************************************************************/ /*** UTILITIES ***/ /******************************************************************************/ /******************************************************************************/ -/** check whether the 'text' fit String_Any, NULL or "" */ +/** + * Check whether the 'text' fit String_Any, NULL or "" + * @param text the text to check + * @return true if text matches ANY possible values + */ static bool is_any( @@ -46,7 +76,11 @@ is_any( return text == NULL || text[0] == 0 || (!text[1] && text[0] == Data_Any_Char); } -/** check whether the 'text' fit String_Any, String_Wide, NULL or "" */ +/** + * check whether the 'text' fit String_Any, String_Wide, NULL or "" + * @param text the text to check + * @return true if text matches ANY or WIDE possible values + */ static bool is_any_or_wide( @@ -56,7 +90,12 @@ is_any_or_wide( || (!text[1] && (text[0] == Data_Any_Char || text[0] == Data_Wide_Char)); } -/** return the name of 'index' */ +/** + * Get the name stored for index + * @param db the anydb database to query + * @param idx the index of the string to get + * @return the name or NULL if doesn't exist + */ static const char* string( @@ -70,7 +109,14 @@ string( return db->itf.string(db->clodb, idx); } -/** search the index of 'name' and create it if 'create' */ +/** + * Search the index of 'name' and create it if 'create' + * @param db the anydb database to query + * @param idx where to store the result if needed + * @param name name to search and/or create + * @param create if not nul, the name is created if it doesn't exist + * @return 0 in case of success of -errno + */ static int idx( @@ -79,27 +125,39 @@ idx( const char *name, bool create ) { - /* special names */ + /* handle special names */ if (!name || !name[0]) { + /* no name or empty name means ANY */ *idx = AnyIdx_Any; return 0; } + /* handle special names of one character */ if (!name[1]) { if (name[0] == Data_Any_Char) { + /* Single char for ANY */ *idx = AnyIdx_Any; return 0; } if (name[0] == Data_Wide_Char) { + /* Single char for WIDE */ *idx = AnyIdx_Wide; return 0; } } - /* other case */ + /* other case: ask the database backend */ return db->itf.index(db->clodb, idx, name, create); } -/** search the index of 'name' and create it if 'create' */ +/** + * Search the index of 'name' and create it if 'create' + * Return the index for WIDE if name matches ANY or WIDE + * @param db the backend database + * @param idx where to store the found index + * @param name the name to search or create + * @param create not nul for authorizing creation of the index for name + * @return 0 on success + */ static int idx_but_any( @@ -117,7 +175,14 @@ idx_but_any( return db->itf.index(db->clodb, idx, name, create); } -/** search the index of 'name' and create it if 'create' */ +/** + * Return the index of 'name' in the database 'db'. In option 'create' it. + * If the name encode ANY or WIDE returns WIDE. + * @param db the backend database + * @param name the name to search or create + * @param create not nul for authorizing creation of the index for name + * @return the found index or AnyIdx_None if not found. + */ static anydb_idx_t idx_or_none_but_any( @@ -132,13 +197,126 @@ idx_or_none_but_any( return idx; } +/******************************************************************************/ +/******************************************************************************/ +/*** SEARCH KEYS ***/ +/******************************************************************************/ +/******************************************************************************/ + +static +bool +searchkey_prepare_match(anydb_t *db, + const data_key_t *key, + searchkey_t *skey, + bool create +) { + if (idx(db, &skey->idxcli, key->client, create) + || idx(db, &skey->idxses, key->session, create) + || idx(db, &skey->idxusr, key->user, create)) + return false; /* one of the idx doesn't exist */ + skey->strperm = is_any(key->permission) ? NULL : key->permission; + + return true; +} + +static +bool +searchkey_match( + anydb_t *db, + const anydb_key_t *key, + const searchkey_t *skey +) { + return (skey->idxcli == AnyIdx_Any || skey->idxcli == key->client) + && (skey->idxses == AnyIdx_Any || skey->idxses == key->session) + && (skey->idxusr == AnyIdx_Any || skey->idxusr == key->user) + && (!skey->strperm || !strcasecmp(skey->strperm, string(db, key->permission))); +} + +static +int +searchkey_prepare_is( + anydb_t *db, + const data_key_t *key, + searchkey_t *skey, + bool create +) { + int rc; + + rc = idx_but_any(db, &skey->idxcli, key->client, create); + if (!rc) { + rc = idx_but_any(db, &skey->idxses, key->session, create); + if (!rc) { + rc = idx_but_any(db, &skey->idxusr, key->user, create); + if (!rc) + skey->strperm = key->permission; + } + } + return rc; +} + +static +bool +searchkey_is( + anydb_t *db, + const anydb_key_t *key, + const searchkey_t *skey +) { + return skey->idxcli == key->client + && skey->idxses == key->session + && skey->idxusr == key->user + && !strcasecmp(skey->strperm, string(db, key->permission)); +} + +static +void +searchkey_prepare_test( + anydb_t *db, + const data_key_t *key, + searchkey_t *skey, + bool create +) { + skey->idxcli = idx_or_none_but_any(db, key->client, create); + skey->idxses = idx_or_none_but_any(db, key->session, create); + skey->idxusr = idx_or_none_but_any(db, key->user, create); + skey->strperm = key->permission; +} + +static +unsigned +searchkey_test( + anydb_t *db, + const anydb_key_t *key, + const searchkey_t *skey +) { + unsigned sc; + + if ((key->client != AnyIdx_Wide && skey->idxcli != key->client) + || (key->session != AnyIdx_Wide && skey->idxses != key->session) + || (key->user != AnyIdx_Wide && skey->idxusr != key->user) + || (key->permission != AnyIdx_Wide + && strcasecmp(skey->strperm, string(db, key->permission)))) { + sc = 0; + } else { + sc = 1; + if (key->client != AnyIdx_Wide) + sc += CLIENT_MATCH_SCORE; + if (key->session != AnyIdx_Wide) + sc += SESSION_MATCH_SCORE; + if (key->user != AnyIdx_Wide) + sc += USER_MATCH_SCORE; + if (key->permission != AnyIdx_Wide) + sc += PERMISSION_MATCH_SCORE; + } + return sc; +} + /******************************************************************************/ /******************************************************************************/ /*** FOR ALL ***/ /******************************************************************************/ /******************************************************************************/ -/** manage atomicity of operations */ +/* see anydb.h */ int anydb_transaction( anydb_t *db, @@ -157,17 +335,14 @@ anydb_transaction( struct for_all_s { - anydb_t *db; + anydb_t *db; /* targeted database */ + time_t now; /* also drop expired items */ + searchkey_t skey; void *closure; void (*callback)( void *closure, const data_key_t *key, const data_value_t *value); - anydb_idx_t idxcli; - anydb_idx_t idxses; - anydb_idx_t idxusr; - const char *strperm; - time_t now; }; static @@ -181,48 +356,41 @@ for_all_cb( data_key_t k; data_value_t v; + /* drop expired items */ if (value->expire && value->expire <= s->now) return Anydb_Action_Remove_And_Continue; - if ((s->idxcli == AnyIdx_Any || s->idxcli == key->client) - && (s->idxses == AnyIdx_Any || s->idxses == key->session) - && (s->idxusr == AnyIdx_Any || s->idxusr == key->user)) { + if (searchkey_match(s->db, key, &s->skey)) { + k.client = string(s->db, key->client); + k.session = string(s->db, key->session); + k.user = string(s->db, key->user); k.permission = string(s->db, key->permission); - if (!s->strperm || !strcasecmp(s->strperm, k.permission)) { - k.client = string(s->db, key->client); - k.session = string(s->db, key->session); - k.user = string(s->db, key->user); - v.value = string(s->db, value->value); - v.expire = value->expire; - s->callback(s->closure, &k, &v); - } + v.value = string(s->db, value->value); + v.expire = value->expire; + s->callback(s->closure, &k, &v); } return Anydb_Action_Continue; } -/** enumerate */ +/* see anydb.h */ void anydb_for_all( anydb_t *db, - void *closure, void (*callback)( void *closure, const data_key_t *key, const data_value_t *value), + void *closure, const data_key_t *key ) { struct for_all_s s; + if (!searchkey_prepare_match(db, key, &s.skey, false)) + return; /* nothing to do! because one of the idx doesn't exist */ + s.db = db; s.closure = closure; s.callback = callback; - - if (idx(db, &s.idxcli, key->client, false) - || idx(db, &s.idxses, key->session, false) - || idx(db, &s.idxusr, key->user, false)) - return; /* nothing to do! because one of the idx doesn't exist */ - s.strperm = is_any(key->permission) ? NULL : key->permission; - s.now = time(NULL); db->itf.apply(db->clodb, for_all_cb, &s); } @@ -233,16 +401,15 @@ anydb_for_all( /******************************************************************************/ /******************************************************************************/ +/* structure for dropping items */ struct drop_s { - anydb_t *db; - anydb_idx_t idxcli; - anydb_idx_t idxses; - anydb_idx_t idxusr; - const char *strperm; - time_t now; + anydb_t *db; /* targeted database */ + time_t now; /* also drop expired items */ + searchkey_t skey; /* the search key */ }; +/* callback for dropping items */ static anydb_action_t drop_cb( @@ -252,57 +419,50 @@ drop_cb( ) { struct drop_s *s = closure; + /* drop expired items */ if (value->expire && value->expire <= s->now) return Anydb_Action_Remove_And_Continue; - if ((s->idxcli == AnyIdx_Any || s->idxcli == key->client) - && (s->idxses == AnyIdx_Any || s->idxses == key->session) - && (s->idxusr == AnyIdx_Any || s->idxusr == key->user) - && (!s->strperm || !strcasecmp(s->strperm, string(s->db, key->permission)))) + /* remove if matches the key */ + if (searchkey_match(s->db, key, &s->skey)) return Anydb_Action_Remove_And_Continue; + /* continue to next */ return Anydb_Action_Continue; } -/** drop rules */ -int +/* see anydb.h */ +void anydb_drop( anydb_t *db, const data_key_t *key ) { struct drop_s s; - s.db = db; - - if (idx(db, &s.idxcli, key->client, false) - || idx(db, &s.idxses, key->session, false) - || idx(db, &s.idxusr, key->user, false)) - return 0; /* nothing to do! because one of the idx doesn't exist */ - s.strperm = is_any(key->permission) ? NULL : key->permission; + if (!searchkey_prepare_match(db, key, &s.skey, false)) + return; /* nothing to do! because one of the idx doesn't exist */ + s.db = db; s.now = time(NULL); db->itf.apply(db->clodb, drop_cb, &s); - return 0; } /******************************************************************************/ /******************************************************************************/ -/*** ADD ***/ +/*** SET ***/ /******************************************************************************/ /******************************************************************************/ +/* structure for setting values */ struct set_s { - anydb_t *db; - anydb_idx_t idxcli; - anydb_idx_t idxses; - anydb_idx_t idxusr; - anydb_idx_t idxval; - time_t expire; - const char *strperm; - time_t now; + anydb_t *db; /* targeted database */ + time_t now; /* also drop expired items */ + searchkey_t skey; /* searching key */ + anydb_value_t value; /* value to set */ }; +/* callback for setting values */ static anydb_action_t set_cb( @@ -312,22 +472,21 @@ set_cb( ) { struct set_s *s = closure; + /* drop expired items */ if (value->expire && value->expire <= s->now) return Anydb_Action_Remove_And_Continue; - if (s->idxcli == key->client - && s->idxses == key->session - && s->idxusr == key->user - && !strcasecmp(s->strperm, string(s->db, key->permission))) { - value->value = s->idxval; - value->expire = s->expire; - s->db = NULL; + if (searchkey_is(s->db, key, &s->skey)) { + value->value = s->value.value; + value->expire = s->value.expire; + s->db = NULL; /* indicates that is found */ return Anydb_Action_Update_And_Stop; } return Anydb_Action_Continue; } +/* see anydb.h */ int anydb_set( anydb_t *db, @@ -336,41 +495,25 @@ anydb_set( ) { int rc; struct set_s s; - anydb_key_t k; - anydb_value_t v; - - s.db = db; - s.strperm = key->permission; - s.expire = value->expire; - rc = idx_but_any(db, &s.idxcli, key->client, true); - if (rc) - goto error; - rc = idx_but_any(db, &s.idxses, key->session, true); - if (rc) - goto error; - rc = idx_but_any(db, &s.idxusr, key->user, true); + rc = searchkey_prepare_is(db, key, &s.skey, true); if (rc) goto error; - rc = idx(db, &s.idxval, value->value, true); + + rc = idx(db, &s.value.value, value->value, true); if (rc) goto error; + s.db = db; + s.value.expire = value->expire; s.now = time(NULL); db->itf.apply(db->clodb, set_cb, &s); if (s.db) { - if (idx(db, &k.permission, s.strperm, true)) - goto error; - k.client = s.idxcli; - k.user = s.idxusr; - k.session = s.idxses; - v.value = s.idxval; - v.expire = s.expire; - rc = db->itf.add(db->clodb, &k, &v); - if (rc) - goto error; + /* no item to alter so must be added */ + rc = idx(db, &s.skey.key.permission, key->permission, true); + if (!rc) + rc = db->itf.add(db->clodb, &s.skey.key, &s.value); } - return 0; error: return rc; } @@ -381,19 +524,17 @@ error: /******************************************************************************/ /******************************************************************************/ +/* structure for testing rule */ struct test_s { - anydb_t *db; - anydb_idx_t idxcli; - anydb_idx_t idxses; - anydb_idx_t idxusr; - const char *strperm; - int score; - anydb_idx_t idxval; - time_t expire; + anydb_t *db; /* targeted database */ time_t now; + unsigned score; + searchkey_t skey; + anydb_value_t value; }; +/* callback for testing rule */ static anydb_action_t test_cb( @@ -402,35 +543,22 @@ test_cb( anydb_value_t *value ) { struct test_s *s = closure; - int sc; + unsigned sc; + /* drop expired items */ if (value->expire && value->expire <= s->now) return Anydb_Action_Remove_And_Continue; - if ((s->idxcli == key->client || key->client == AnyIdx_Wide) - && (s->idxses == key->session || key->session == AnyIdx_Wide) - && (s->idxusr == key->user || key->user == AnyIdx_Wide) - && (AnyIdx_Wide == key->permission - || !strcasecmp(s->strperm, string(s->db, key->permission)))) { - sc = 1; - if (key->client != AnyIdx_Wide) - sc += CLIENT_MATCH_SCORE; - if (key->session != AnyIdx_Wide) - sc += SESSION_MATCH_SCORE; - if (key->user != AnyIdx_Wide) - sc += USER_MATCH_SCORE; - if (key->permission != AnyIdx_Wide) - sc += PERMISSION_MATCH_SCORE; - if (sc > s->score) { - s->score = sc; - s->idxval = value->value; - s->expire = value->expire; - } + sc = searchkey_test(s->db, key, &s->skey); + if (sc > s->score) { + s->score = sc; + s->value = *value; } return Anydb_Action_Continue; } -int +/* see anydb.h */ +unsigned anydb_test( anydb_t *db, const data_key_t *key, @@ -438,34 +566,27 @@ anydb_test( ) { struct test_s s; + searchkey_prepare_test(db, key, &s.skey, true); + s.db = db; s.now = time(NULL); - s.strperm = key->permission; - s.expire = value->expire; - - s.idxcli = idx_or_none_but_any(db, key->client, true); - s.idxses = idx_or_none_but_any(db, key->session, true); - s.idxusr = idx_or_none_but_any(db, key->user, true); - - s.expire = -1; - s.idxval = AnyIdx_Invalid; s.score = 0; - db->itf.apply(db->clodb, test_cb, &s); - if (s.score) { - value->value = string(db, s.idxval); - value->expire = s.expire; + value->value = string(db, s.value.value); + value->expire = s.value.expire; } return s.score; } + /******************************************************************************/ /******************************************************************************/ /*** IS EMPTY ***/ /******************************************************************************/ /******************************************************************************/ +/* callback for computing if empty */ static anydb_action_t is_empty_cb( @@ -473,18 +594,23 @@ is_empty_cb( const anydb_key_t *key, anydb_value_t *value ) { - bool *result = closure; - *result = false; + time_t *t = closure; + if (value->expire && value->expire <= *t) + return Anydb_Action_Remove_And_Continue; + *t = 0; return Anydb_Action_Stop; } +/* see anydb.h */ bool anydb_is_empty( anydb_t *db ) { - bool result = true; - db->itf.apply(db->clodb, is_empty_cb, &result); - return result; + time_t t; + + t = time(NULL); + db->itf.apply(db->clodb, is_empty_cb, &t); + return !t; } /******************************************************************************/ @@ -493,6 +619,7 @@ anydb_is_empty( /******************************************************************************/ /******************************************************************************/ +/* cleanup callback */ static anydb_action_t cleanup_cb( @@ -500,12 +627,11 @@ cleanup_cb( const anydb_key_t *key, anydb_value_t *value ) { - if (value->expire && value->expire <= *(time_t*)closure) - return Anydb_Action_Remove_And_Continue; - - return Anydb_Action_Continue; + return value->expire && value->expire <= *(time_t*)closure + ? Anydb_Action_Remove_And_Continue : Anydb_Action_Continue; } +/* see anydb.h */ void anydb_cleanup( anydb_t *db @@ -517,12 +643,27 @@ anydb_cleanup( db->itf.gc(db->clodb); } +/******************************************************************************/ +/******************************************************************************/ +/*** SYNCHRONIZE ***/ +/******************************************************************************/ +/******************************************************************************/ + +/* see anydb.h */ +int +anydb_sync( + anydb_t *db +) { + return db->itf.sync ? db->itf.sync(db->clodb) : 0; +} + /******************************************************************************/ /******************************************************************************/ /*** DESTROY ***/ /******************************************************************************/ /******************************************************************************/ +/* see anydb.h */ void anydb_destroy( anydb_t *db diff --git a/src/anydb.h b/src/anydb.h index 843ef29..64f86bd 100644 --- a/src/anydb.h +++ b/src/anydb.h @@ -14,8 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* HIGH LEVEL ABSTRACTION OF THE DATABASES */ +/******************************************************************************/ +/******************************************************************************/ /** * An index is an integer @@ -29,10 +33,16 @@ typedef uint32_t anydb_idx_t; /** The invalid index */ #define AnyIdx_Invalid ((anydb_idx_t)0xffffffffu) -/** */ +/** The index for ANY */ #define AnyIdx_Any ((anydb_idx_t)0xfffffffeu) + +/** The index for WIDE */ #define AnyIdx_Wide ((anydb_idx_t)0xfffffffdu) + +/** The index for NONE */ #define AnyIdx_None ((anydb_idx_t)0xfffffffcu) + +/** The maximum value for indexes */ #define AnyIdx_Max ((anydb_idx_t)0xfffffff7u) /** @@ -67,45 +77,155 @@ struct anydb_value typedef struct anydb_value anydb_value_t; /** + * Operation of the transaction */ -enum anydb_action -{ - Anydb_Action_Stop, - Anydb_Action_Continue, - Anydb_Action_Update_And_Stop, - Anydb_Action_Remove_And_Continue -}; -typedef enum anydb_action anydb_action_t; - enum anydb_transaction { + /** + * Operation: Start a cancelable transaction + */ Anydb_Transaction_Start = 0, + + /** + * Operation: Terminate the started transaction and commit its changes + */ Anydb_Transaction_Commit = 1, + + /** + * Operation: Terminate the started transaction and cancel its changes + */ Anydb_Transaction_Cancel = 2 }; typedef enum anydb_transaction anydb_transaction_t; +/** + * Actions to perform in response to anydb_applycb_t callbacks. + */ +enum anydb_action +{ + /** Continue to apply with the next element of the database */ + Anydb_Action_Continue = 0, + + /** Stop to apply */ + Anydb_Action_Stop = 1, + + /** Update the current element (implicitly, also continue) */ + Anydb_Action_Update = 2, + + /** Remove the current element (implicitly, also continue) */ + Anydb_Action_Remove = 4, + + /** Update the current element and stop to apply */ + Anydb_Action_Update_And_Stop = Anydb_Action_Update | Anydb_Action_Stop, + + /** Update the current element and continue to apply */ + Anydb_Action_Update_And_Continue = Anydb_Action_Update | Anydb_Action_Continue, + + /** Remove the current element and stop to apply */ + Anydb_Action_Remove_And_Stop = Anydb_Action_Remove | Anydb_Action_Stop, + + /** Remove the current element and continue to apply */ + Anydb_Action_Remove_And_Continue = Anydb_Action_Remove | Anydb_Action_Continue +}; +typedef enum anydb_action anydb_action_t; + +/** + * Callback of apply method. This callback is called for any item of + * the database and it tells through its return what the anydb has + * to do: see anydb_action_t. + * The 'closure' is the closure given by the caller of 'apply' method. + * 'key' is the iterated key of the anydb. It can not be changed. + * 'value' is the value stored in the database for the key. + */ +typedef anydb_action_t anydb_applycb_t(void *closure, const anydb_key_t *key, anydb_value_t *value); + +/** + * Interface to any database implementation + */ struct anydb_itf { + /** + * Get the index of the 'name' in 'idx'. If the name is found + * then its index is returned. If the name is not found, the + * database backend has to create it if 'create' is not zero. + * 'clodb' is the database's closure. + * Returns 0 in case of success (*idx filled with the index) + * or return a negative error code in -errno like form. + */ int (*index)(void *clodb, anydb_idx_t *idx, const char *name, bool create); + + /** + * Get the string for the index 'idx'. idx MUST be valid. + * 'clodb' is the database's closure. + */ const char *(*string)(void *clodb, anydb_idx_t idx); + + /** + * Start, Commit or Cancel a cancellable transaction. The operation + * to perform is given by 'op'. + * 'clodb' is the database's closure. + * Returns 0 in case of success or return a negative error code + * in -errno like form. + */ int (*transaction)(void *clodb, anydb_transaction_t atomic_op); - void (*apply)(void *clodb, anydb_action_t (*oper)(void *closure, const anydb_key_t *key, anydb_value_t *value), void *closure); + + /** + * Iterate over the database items and apply the operator 'oper'. + * The callback operator 'oper' is called with the given 'closure' + * and the key and value for the item. It can modify or delete the item. + * 'clodb' is the database's closure. + */ + void (*apply)(void *clodb, anydb_applycb_t *oper, void *closure); + + /** + * Add the item of 'key' and 'value'. + * 'clodb' is the database's closure. + * Returns 0 in case of success or return a negative error code + * in -errno like form. + */ int (*add)(void *clodb, const anydb_key_t *key, const anydb_value_t *value); + + /** + * Garbage collection of unused items. + * 'clodb' is the database's closure. + */ void (*gc)(void *clodb); + + /** + * Synchronize the database and its longterm support (file) + * 'clodb' is the database's closure. + * Returns 0 in case of success or return a negative error code + * in -errno like form. + */ int (*sync)(void *clodb); + + /** + * Destroys the database + * 'clodb' is the database's closure. + */ void (*destroy)(void *clodb); }; typedef struct anydb_itf anydb_itf_t; +/** + * The structure for abstracting backend databases + */ struct anydb { + /** the closure */ void *clodb; + + /** the implementation methods */ anydb_itf_t itf; }; typedef struct anydb anydb_t; -/** manage atomicity of operations */ +/** + * Manage atomicity of modifications by enabling cancellation + * @param db database to manage + * @param oper operation to perform + * @return 0 in case of success or a negative error code in -errno like form. + */ extern int anydb_transaction( @@ -113,28 +233,44 @@ anydb_transaction( anydb_transaction_t oper ); -/** enumerate */ +/** + * Enumerate items of the database matching the given key + * @param db database to enumerate + * @param callback callback function receiving the item that matches the key + * @param closure closure for the callback + * @param key key to restrict enumeration can't be NULL + */ extern void anydb_for_all( anydb_t *db, - void *closure, void (*callback)( void *closure, const data_key_t *key, const data_value_t *value), + void *closure, const data_key_t *key ); -/** drop rules */ +/** + * Drop any rule that matches the key + * @param db database to modify + * @param key the key that select items to be dropped + */ extern -int +void anydb_drop( anydb_t *db, const data_key_t *key ); -/** set a rules */ +/** + * Set the rule described by key and value + * @param db the database to set + * @param key the key of the rule + * @param value the value of the rule + * @return 0 on success or a negative error code + */ extern int anydb_set( @@ -143,39 +279,60 @@ anydb_set( const data_value_t *value ); -/** test a rule, returns 0 or the score: count of exact keys */ +/** + * Test a rule and return its score and the value + * @param db the database + * @param key key to be matched by rules + * @param value value found for the key, filled only if a key matched + * @return 0 if no rule matched or a positive integer when the rule matched + * The higher the integer is, the more accurate is the rule found. + */ extern -int +unsigned anydb_test( anydb_t *db, const data_key_t *key, data_value_t *value ); -/** drop rules */ +/** + * Drop any expired rule + * @param db the database to clean + */ extern void anydb_cleanup( anydb_t *db ); -/** is the database empty? */ +/** + * Is the database empty? + * @param db the database to test + * @return true if the database is empty or otherwise false + */ extern bool anydb_is_empty( anydb_t *db ); -/** destroy the database */ +/** + * Synchronize the database if needed + * @param db the database to + * @return 0 on success or a negative -errno like code + */ extern -void -anydb_destroy( +int +anydb_sync( anydb_t *db ); -/** synchronize database */ +/** + * Destroy the database + * @param db the database to destroy + */ extern -int -anydb_sync( +void +anydb_destroy( anydb_t *db ); diff --git a/src/cache.c b/src/cache.c index ca4234a..bed0b49 100644 --- a/src/cache.c +++ b/src/cache.c @@ -14,7 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF CACHE IN CLIENTS */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -64,12 +68,25 @@ typedef struct item item_t; */ struct cache { + /** used for clearing */ uint32_t cacheid; + + /** count of bytes used */ uint32_t used; + + /** count of bytes allocated */ uint32_t count; + + /** content of the cache */ uint8_t content[]; }; +/** + * return the item at a given position + * @param cache the cache + * @param pos the position of the item + * @return the item + */ static inline item_t * @@ -80,6 +97,11 @@ itemat( return (item_t*)(&cache->content[pos]); } +/** + * Removes the item at position pos + * @param cache the cache + * @param pos the position of the item to remove + */ static void drop_at( @@ -95,6 +117,10 @@ drop_at( memmove(&cache->content[pos], &cache->content[e], cache->used - pos); } +/** + * Removes the oldest hit target + * @param cache the cache + */ static void drop_lre( @@ -117,6 +143,11 @@ drop_lre( drop_at(cache, found); } +/** + * tells the target is used + * @param cache the cache + * @param target the target to hit + */ static void hit( @@ -141,34 +172,55 @@ hit( } } +/** + * Compare the head with a string and either return NULL if it doesn't match or + * otherwise return the pointer to the next string for heading. + * @param head head of scan + * @param other string to compare + * @return NULL if no match or pointer to the strings that follows head if match + */ static const char* -cmpi( +cmp( const char *head, const char *other ) { char c; - while(toupper(c = *head++) == toupper(*other++)) + while((c = *head++) == *other++) if (!c) return head; - return 0; + return NULL; } +/** + * Compare in a case independant method the head with a string and either + * return NULL if it doesn't match or otherwise return the pointer to the + * next string for heading. + * @param head head of scan + * @param other string to compare + * @return NULL if no match or pointer to the strings that follows head if match + */ static const char* -cmp( +cmpi( const char *head, const char *other ) { char c; - while((c = *head++) == *other++) + while(toupper(c = *head++) == toupper(*other++)) if (!c) return head; return 0; } +/** + * Check if a head of strings matche the key + * @param head the head of strings + * @param key the key + * @return true if matches or false other wise + */ static -int +bool match( const char *head, const rcyn_key_t *key @@ -181,13 +233,19 @@ match( if (head) { head = cmpi(head, key->permission); if (head) - return 1; + return true; } } } - return 0; + return false; } +/** + * Search the item matching key and return it. Also remove expired entries + * @param cache the cache + * @param key the key to search + * @return the found item or NULL if not found + */ static item_t* search( @@ -214,6 +272,7 @@ search( return found; } +/* see cache.h */ int cache_put( cache_t *cache, @@ -255,6 +314,7 @@ cache_put( return 0; } +/* see cache.h */ int cache_search( cache_t *cache, @@ -272,6 +332,7 @@ cache_search( return -ENOENT; } +/* see cache.h */ void cache_clear( cache_t *cache, @@ -283,35 +344,43 @@ cache_clear( } } +/* see cache.h */ int cache_resize( cache_t **cache, uint32_t newsize ) { - cache_t *c = *cache, *nc; + cache_t *oldcache = *cache, *newcache; if (newsize == 0) { - free(c); - nc = NULL; + /* erase all */ + free(oldcache); + newcache = NULL; } else { - if (c) - while (c->used > newsize) - drop_lre(c); + /* coerce cache values if downsizing */ + if (oldcache) { + while (oldcache->used > newsize) + drop_lre(oldcache); + } - nc = realloc(c, newsize + sizeof *c); - if (nc == NULL) + /* reallocate the cache */ + newcache = realloc(oldcache, newsize + sizeof *oldcache); + if (newcache == NULL) return -ENOMEM; - nc->count = newsize; - if (!c) { - nc->cacheid = 0; - nc->used = 0; + /* init */ + newcache->count = newsize; + if (!oldcache) { + newcache->cacheid = 0; + newcache->used = 0; } } - *cache = nc; + /* update cache */ + *cache = newcache; return 0; } +/* see cache.h */ int cache_create( cache_t **cache, diff --git a/src/cache.h b/src/cache.h index dd37d92..d694e9b 100644 --- a/src/cache.h +++ b/src/cache.h @@ -14,12 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF CACHE IN CLIENTS */ +/******************************************************************************/ +/******************************************************************************/ -struct cache; +/** opaque structure for cache */ typedef struct cache cache_t; +/** + * Search the stored value for the key + * @param cache the cache handler + * @param key the key to search + * @return the stored value or -ENOENT if not found + */ extern int cache_search( @@ -27,6 +37,16 @@ cache_search( const rcyn_key_t *key ); +/** + * Add the value for the key in the cache + * @param cache the cache handler + * @param key the key to cache + * @param value the value (must be an integer from -128 to 127) + * @param expire expiration date + * @return 0 on success + * -EINVAL invalid argument + * -ENOMEM too big for the cache size + */ extern int cache_put( @@ -36,6 +56,11 @@ cache_put( time_t expire ); +/** + * Clear the content of the cache if the cacheid doesn't match the current one + * @param cache the cache handler + * @param cacheid the cacheid to set or zero to force clearing + */ extern void cache_clear( @@ -43,6 +68,13 @@ cache_clear( uint32_t cacheid ); +/** + * + * @param cache pointer to the cache handler + * @param newsize new size to set to the cache + * @return 0 on success + * -ENOMEM not enough memory + */ extern int cache_resize( @@ -50,6 +82,13 @@ cache_resize( uint32_t newsize ); +/** + * + * @param cache pointer to the cache handler + * @param size size to set to the cache + * @return 0 on success + * -ENOMEM not enough memory + */ extern int cache_create( diff --git a/src/cyn.c b/src/cyn.c index 6978251..f82e3cd 100644 --- a/src/cyn.c +++ b/src/cyn.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF LOCAL CYNARA API */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -31,6 +36,9 @@ #if !CYN_SEARCH_DEEP_MAX # define CYN_SEARCH_DEEP_MAX 10 #endif +#if !defined(AGENT_SEPARATOR_CHARACTER) +# define AGENT_SEPARATOR_CHARACTER ':' +#endif /** * items of the list of observers or awaiters @@ -39,14 +47,19 @@ struct callback { /** link to the next item of the list */ struct callback *next; + + /** recording of callback function */ union { /** any callback value */ void *any_cb; + /** awaiter callback */ on_enter_cb_t *on_enter_cb; + /** observer callback */ on_change_cb_t *on_change_cb; }; + /** closure of the callback */ void *closure; }; @@ -65,7 +78,7 @@ struct agent /** closure of the callback */ void *closure; - /** length of the name */ + /** length of the name (without terminating zero) */ uint8_t len; /** name of the agent */ @@ -73,7 +86,7 @@ struct agent }; /** - * structure handling an asynchronous check + * structure handling an asynchronous requests */ struct cyn_query { @@ -86,15 +99,12 @@ struct cyn_query /** key of the check */ data_key_t key; - /** value of the check */ - data_value_t value; - /** down counter for recursivity limitation */ int decount; }; -/** locking critical section */ -static const void *lock; +/** for locking critical section with magic */ +static const void *magic_locker; /** head of the list of critical section awaiters */ static struct callback *awaiters; @@ -102,17 +112,24 @@ static struct callback *awaiters; /** head of the list of change observers */ static struct callback *observers; -/** head of the list of agents */ +/** head of the list of recorded agents */ static struct agent *agents; -/** last changeid */ -static uint32_t last_changeid; +/** holding of changeid */ +static struct { + /** current changeid */ + uint32_t current; -/** changeid of the current 'changeid_string' */ -static uint32_t last_changeid_string; + /** changeid set in string */ + uint32_t instring; -/** string buffer for changeid */ -static char changeid_string[12]; + /** string value for a changeid */ + char string[12]; +} changeid = { + .current = 1, + .instring = 0, + .string = { 0 } +}; /** * Delete from the list represented by 'head' the entry for @@ -179,7 +196,7 @@ changed( ) { struct callback *c; - ++last_changeid; + changeid.current = changeid.current + 1 ?: 1; for (c = observers; c ; c = c->next) c->on_change_cb(c->closure); } @@ -191,9 +208,9 @@ cyn_enter( ) { if (!magic) return -EINVAL; - if (lock) + if (magic_locker) return -EBUSY; - lock = magic; + magic_locker = magic; return 0; } @@ -205,10 +222,10 @@ cyn_enter_async( ) { if (!magic) return -EINVAL; - if (lock) + if (magic_locker) return addcb(enter_cb, magic, &awaiters); - lock = magic; + magic_locker = magic; enter_cb(magic); return 0; } @@ -251,12 +268,12 @@ cyn_leave( if (!magic) return -EINVAL; - if (!lock) + if (!magic_locker) return -EALREADY; - if (lock != magic) + if (magic_locker != magic) return -EPERM; - lock = &lock; + magic_locker = &magic_locker; if (!commit) rc = 0; else { @@ -273,7 +290,7 @@ cyn_leave( /* wake up awaiting client */ e = awaiters; if (!e) - lock = 0; + magic_locker = 0; else { /* the one to awake is at the end of the list */ p = &awaiters; @@ -282,7 +299,7 @@ cyn_leave( e = *p; } *p = NULL; - lock = e->closure; + magic_locker = e->closure; e->on_enter_cb(e->closure); free(e); } @@ -296,7 +313,7 @@ cyn_set( const data_key_t *key, const data_value_t *value ) { - if (!lock) + if (!magic_locker) return -EPERM; return queue_set(key, value); } @@ -306,7 +323,7 @@ int cyn_drop( const data_key_t *key ) { - if (!lock) + if (!magic_locker) return -EPERM; return queue_drop(key); } @@ -314,15 +331,16 @@ cyn_drop( /* see cyn.h */ void cyn_list( - void *closure, list_cb_t *callback, + void *closure, const data_key_t *key ) { - db_for_all(closure, callback, key); + db_for_all(callback, closure, key); } /** * initialize value to its default + * * @param value to initialize * @return the initialized value */ @@ -338,8 +356,9 @@ default_value( /** * Search the agent of name and return its item in the list + * * @param name of the agent to find (optionally zero terminated) - * @param len length of the name + * @param len length of the name (without terminating zero) * @param ppprev for catching the pointer referencing the return item * @return 0 if not found or the pointer to the item of the found agent */ @@ -347,56 +366,51 @@ static struct agent * search_agent( const char *name, - uint8_t len, + size_t length, struct agent ***ppprev ) { struct agent *it, **pprev; pprev = &agents; while((it = *pprev) - && (len != it->len || memcmp(it->name, name, (size_t)len))) + && ((uint8_t)length != it->len || memcmp(it->name, name, length))) pprev = &it->next; *ppprev = pprev; return it; } /** - * Return the agent required by the value or 0 if no agent is required + * Return the agent required by the value or NULL if no agent is required * or if the agent is not found. + * * @param value string where agent is the prefix followed by one colon - * @return the item of the required agent or 0 when no agent is required + * @return the item of the required agent or NULL when no agent is required */ static -struct agent * +struct agent* required_agent( const char *value ) { struct agent **pprev; - uint8_t len; - - for (len = 0 ; len < UINT8_MAX && value[len] ; len++) - if (value[len] == ':') - return search_agent(value, len, &pprev); - return 0; -} + size_t length; -static -void -async_call_agent( - struct agent *agent, - cyn_query_t *query, - const data_value_t *value -) { - int rc = agent->agent_cb( - agent->name, - agent->closure, - &query->key, - &value->value[agent->len + 1], - query); - if (rc < 0) - cyn_reply_query(query, value); + for (length = 0 ; length <= UINT8_MAX && value[length] ; length++) + if (value[length] == AGENT_SEPARATOR_CHARACTER) + return search_agent(value, length, &pprev); + return NULL; } +/** + * Allocates the query structure for handling the given parameters + * and return it. The query structure copies the key data to be compatible + * with asynchronous processing. + * + * @param on_result_cb the result callback to record + * @param closure the closure for the result callback + * @param key the key of the query + * @param maxdepth maximum depth of the agent subrequests + * @return the allocated structure or NULL in case of memory depletion + */ static cyn_query_t * alloc_query( @@ -420,6 +434,9 @@ alloc_query( ptr = &query[1]; query->on_result_cb = on_result_cb; query->closure = closure; + query->decount = maxdepth; + + /* copy strings of the key */ if (!key->client) query->key.client = 0; else { @@ -442,19 +459,13 @@ alloc_query( query->key.permission = 0; else { query->key.permission = ptr; - ptr = mempcpy(ptr, key->permission, szper); + mempcpy(ptr, key->permission, szper); } - query->decount = maxdepth; } return query; } - - - - - - +/* see cyn.h */ int cyn_query_async( on_result_cb_t *on_result_cb, @@ -463,25 +474,26 @@ cyn_query_async( int maxdepth ) { int rc; + unsigned score; data_value_t value; cyn_query_t *query; struct agent *agent; /* get the direct value */ - rc = db_test(key, &value); + score = db_test(key, &value); - /* on error or missing result */ - if (rc <= 0) { + /* missing value */ + if (score == 0) { default_value(&value); on_result_cb(closure, &value); - return rc; + return 0; } /* if not an agent or agent not required */ agent = required_agent(value.value); if (!agent || maxdepth <= 0) { on_result_cb(closure, &value); - return rc; + return 0; } /* allocate asynchronous query */ @@ -492,8 +504,15 @@ cyn_query_async( } /* call the agent */ - async_call_agent(agent, query, &value); - return 0; + rc = agent->agent_cb( + agent->name, + agent->closure, + &query->key, + &value.value[agent->len + 1], + query); + if (rc < 0) + cyn_query_reply(query, &value); + return rc; } /* see cyn.h */ @@ -516,8 +535,9 @@ cyn_check_async( return cyn_query_async(on_result_cb, closure, key, CYN_SEARCH_DEEP_MAX); } +/* see cyn.h */ int -cyn_subquery_async( +cyn_query_subquery_async( cyn_query_t *query, on_result_cb_t *on_result_cb, void *closure, @@ -526,8 +546,9 @@ cyn_subquery_async( return cyn_query_async(on_result_cb, closure, key, query->decount - 1); } +/* see cyn.h */ void -cyn_reply_query( +cyn_query_reply( cyn_query_t *query, const data_value_t *value ) { @@ -535,9 +556,28 @@ cyn_reply_query( free(query); } - - - +/** + * Check the name and compute its length. Returns 0 in case of invalid name + * @param name the name to check + * @return the length of the name or zero if invalid + */ +static +size_t +check_agent_name( + const char *name +) { + size_t length = 0; + if (name) { + while (name[length]) { + if (length > UINT8_MAX || name[length] == AGENT_SEPARATOR_CHARACTER) { + length = 0; + break; + } + length++; + } + } + return length; +} /* see cyn.h */ int @@ -548,26 +588,28 @@ cyn_agent_add( ) { struct agent *agent, **pprev; size_t length; - uint8_t len; - length = strlen(name); - if (length <= 0 || length > UINT8_MAX) + /* compute and check name length */ + length = check_agent_name(name); + if (!length) return -EINVAL; - len = (uint8_t)length++; - agent = search_agent(name, len, &pprev); + /* search the agent */ + agent = search_agent(name, length, &pprev); if (agent) return -EEXIST; - agent = malloc(sizeof *agent + length); + /* allocates the memory */ + agent = malloc(sizeof *agent + 1 + length); if (!agent) return -ENOMEM; + /* initialize the agent */ agent->next = 0; agent->agent_cb = agent_cb; agent->closure = closure; - agent->len = len; - memcpy(agent->name, name, length); + agent->len = (uint8_t)length; + memcpy(agent->name, name, length + 1); *pprev = agent; return 0; @@ -580,17 +622,18 @@ cyn_agent_remove( ) { struct agent *agent, **pprev; size_t length; - uint8_t len; - length = strlen(name); - if (length <= 0 || length > UINT8_MAX) + /* compute and check name length */ + length = check_agent_name(name); + if (!length) return -EINVAL; - len = (uint8_t)length; - agent = search_agent(name, len, &pprev); + /* search the agent */ + agent = search_agent(name, length, &pprev); if (!agent) return -ENOENT; + /* remove the found agent */ *pprev = agent->next; free(agent); return 0; @@ -600,14 +643,15 @@ cyn_agent_remove( void cyn_changeid_reset( ) { - last_changeid = 1; + changeid.current = 1; + changeid.instring = 0; } /* see cyn.h */ uint32_t cyn_changeid( ) { - return last_changeid; + return changeid.current; } /* see cyn.h */ @@ -615,9 +659,10 @@ const char * cyn_changeid_string( ) { /* regenerate the string on need */ - if (last_changeid != last_changeid_string) { - last_changeid_string = last_changeid; - snprintf(changeid_string, sizeof changeid_string, "%u", last_changeid); + if (changeid.current != changeid.instring) { + changeid.instring = changeid.current; + snprintf(changeid.string, sizeof changeid.string, "%u", changeid.current); } - return changeid_string; + /* return the string */ + return changeid.string; } diff --git a/src/cyn.h b/src/cyn.h index 5772608..fa361f6 100644 --- a/src/cyn.h +++ b/src/cyn.h @@ -14,11 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF LOCAL CYNARA API */ +/******************************************************************************/ +/******************************************************************************/ -#define CYN_VERSION 99 +#define CYN_VERSION 100 /** * Callback for entering asynchronousely the critical section @@ -120,7 +123,7 @@ cyn_leave( * -ENOMEM critical section already locked but request can't be queued * -EINVAL magic == NULL * - * @see cyn_leave, cyn_enter + * @see cyn_leave, cyn_enter, cyn_enter_async_cancel */ extern int @@ -131,9 +134,12 @@ cyn_enter_async( /** * Cancel a an asynchonous waiter to enter + * * @param enter_cb the enter callback of the waiter * @param magic the closure of the waiter * @return 0 if entry found and removed, -ENOENT if not found + * + * @see cyn_enter_async */ extern int @@ -149,6 +155,8 @@ cyn_enter_async_cancel( * @param closure closure of the callback * @return 0 success * -ENOMEM can't queue the observer + * + * @see cyn_on_change_remove */ extern int @@ -159,9 +167,12 @@ cyn_on_change_add( /** * Removes an on change observer + * * @param on_change_cb the callback of the observer * @param closure the closure of the observer * @return 0 if entry found and removed, -ENOENT if not found + * + * @see cyn_on_change_add */ extern int @@ -172,6 +183,7 @@ cyn_on_change_remove( /** * Set or add the rule key/value to the change list to commit + * * @param key the key of the rule * @param value the value of the rule for the key * @return 0 on success @@ -187,6 +199,7 @@ cyn_set( /** * Drop any rule matching the key + * * @param key the key of the rule * @return 0 on success * -EPERM if not locked in critical recoverable section @@ -198,14 +211,37 @@ cyn_drop( const data_key_t *key ); +/** + * Enumerate all items matching the key + * + * @param callback callback function called for each found item + * @param closure the closure to the callback + * @param key the key to select items + */ extern void cyn_list( - void *closure, list_cb_t *callback, + void *closure, const data_key_t *key ); +/** + * Query the value for the given key. + * + * Note that the callback can be called during the call to that function. + * + * Note also that the callback will always be called either before the + * function returns or else later. + * + * @param on_result_cb callback function receiving the result + * @param closure closure for the callback + * @param key key to be queried + * @param maxdepth maximum imbrication of agent resolution + * @return 0 if there was no error or return the error code + * + * @see cyn_test_async, cyn_check_async + */ extern int cyn_query_async( @@ -215,6 +251,21 @@ cyn_query_async( int maxdepth ); +/** + * Same as cyn_query_async but with a maxdepth of 0 + * + * Note that the callback can be called during the call to that function. + * + * Note also that the callback will always be called either before the + * function returns or else later. + * + * @param on_result_cb callback function receiving the result + * @param closure closure for the callback + * @param key key to be queried + * @return + * + * @see cyn_query_async, cyn_check_async + */ extern int cyn_test_async( @@ -223,6 +274,21 @@ cyn_test_async( const data_key_t *key ); +/** + * Same as cyn_query_async but with a default maxdepth for agent subqueries + * + * Note that the callback can be called during the call to that function. + * + * Note also that the callback will always be called either before the + * function returns or else later. + * + * @param on_result_cb callback function receiving the result + * @param closure closure for the callback + * @param key key to be queried + * @return 0 or -ENOMEM if some error occured + * + * @see cyn_query_async, cyn_test_async + */ extern int cyn_check_async( @@ -231,22 +297,53 @@ cyn_check_async( const data_key_t *key ); +/** + * Makes a recursive query asynchronous + * + * Note that the callback can be called during the call to that function. + * + * Note also that the callback will always be called either before the + * function returns or else later. + * + * @param query the query + * @param on_result_cb callback function receiving the result + * @param closure closure for the callback + * @param key key to be queried + * @return + */ extern int -cyn_subquery_async( +cyn_query_subquery_async( cyn_query_t *query, on_result_cb_t *on_result_cb, void *closure, const data_key_t *key ); +/** + * Send the reply to a query + * + * @param query the query to reply + * @param value the reply value + */ extern void -cyn_reply_query( +cyn_query_reply( cyn_query_t *query, const data_value_t *value ); +/** + * Add the agent of name + * + * @param name name of the agent to add + * @param agent_cb callback of the agent + * @param closure closure of the callback of the agent + * @return 0 in case of success + * -EINVAL if the name is too long + * -EEXIST if an agent of the same name is already recorded + * -ENOMEM if out of memory + */ extern int cyn_agent_add( @@ -255,22 +352,47 @@ cyn_agent_add( void *closure ); +/** + * Remove the agent of 'name' + * + * @param name name of the agent to remove + * @return 0 in case of successful removal + * -EINVAL if the name is too long + * -ENOENT if the agent isn't recorded + */ extern int cyn_agent_remove( const char *name ); +/** + * Reset the changeid + * + * @see cyn_changeid, cyn_changeid_string + */ extern void cyn_changeid_reset( ); +/** + * Get the current changeid + * @return the current changeid + * + * @see cyn_changeid_rest, cyn_changeid_string + */ extern uint32_t cyn_changeid( ); +/** + * Get the current changeid as a string + * @return the string of the current change id + * + * @see cyn_changeid_rest, cyn_changeid + */ extern const char * cyn_changeid_string( diff --git a/src/data.h b/src/data.h index 0e1a3b0..3c87ac3 100644 --- a/src/data.h +++ b/src/data.h @@ -14,24 +14,44 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* GENERIC COMMON DATA TYPES FOR CLIENTS */ +/******************************************************************************/ +/******************************************************************************/ + +/** Maximum length of any string */ +#define MAX_NAME_LENGTH 8000 +/** string for deniying access */ #define DENY "no" + +/** string for allowing access */ #define ALLOW "yes" -#define ASK "ask" + +/** default is denying */ #define DEFAULT DENY +/** + * ANY string, made of one single character, is used to match + * rules and keys that can contain WIDE or other value. + * This allow to search specifically to WIDE when WIDE is specified in the + * search key or to any value (including WIDE) when any is used. + */ #define Data_Any_Char '#' -#define Data_Wide_Char '*' - #define Data_Any_String "#" -#define Data_Wide_String "*" -typedef enum data_keyidx data_keyidx_t; -typedef union data_key data_key_t; -typedef struct data_value data_value_t; +/** + * WIDE string, made of one character, is used in rules to match any + * queried value. + */ +#define Data_Wide_Char '*' +#define Data_Wide_String "*" +/** + * Name of the index on keys + */ enum data_keyidx { KeyIdx_Client, KeyIdx_Session, @@ -39,21 +59,39 @@ enum data_keyidx { KeyIdx_Permission, KeyIdx_Count }; +typedef enum data_keyidx data_keyidx_t; +/** + * A key is made of 4 strings that can be accessed by index or by name + */ union data_key { /* name access */ struct { + /** the client */ const char *client; + + /** the session */ const char *session; + + /** the user */ const char *user; + + /** the permission */ const char *permission; }; - /* arrayed access, see data_keyidx_t */ + /** Array for index access, see data_keyidx_t */ const char *keys[KeyIdx_Count]; }; +typedef union data_key data_key_t; +/** + * A value is made of a string (mainly ALLOW or DENY) and an expiration. + */ struct data_value { + /** judgment of the rule: ALLOW, DENY or agent description */ const char *value; + + /** expiration time of the rule */ time_t expire; }; - +typedef struct data_value data_value_t; diff --git a/src/db.c b/src/db.c index aebead2..09151a1 100644 --- a/src/db.c +++ b/src/db.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* INTERNAL DATABASE IMPLEMENTATION */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -34,8 +39,13 @@ static anydb_t *memdb; static anydb_t *filedb; +static bool modifiable; -/** check whether the 'text' fit String_Any, String_Wide, NULL or "" */ +/** + * check whether the 'text' fit String_Any, String_Wide, NULL or "" + * @param text the text to check + * @return true if ANY or WIDE + */ static bool is_any_or_wide( @@ -46,7 +56,7 @@ is_any_or_wide( } -/** open the database for files 'names' and 'rules' (can be NULL) */ +/* see db.h */ int db_open( const char *directory @@ -62,7 +72,7 @@ db_open( return rc; } -/** close the database */ +/* see db.h */ void db_close( ) { @@ -70,32 +80,41 @@ db_close( anydb_destroy(memdb); } -/** is the database empty */ +/* see db.h */ bool db_is_empty( ) { return anydb_is_empty(filedb); } -/** enter atomic mode */ +/* see db.h */ int db_transaction_begin( ) { - int rc1, rc2; + int rc1, rc2, rc; + + if (modifiable) + return -EALREADY; rc1 = anydb_transaction(filedb, Anydb_Transaction_Start); rc2 = anydb_transaction(memdb, Anydb_Transaction_Start); - return rc1 ?: rc2; + rc = rc1 ?: rc2; + modifiable = !rc; + + return rc; } -/** leave atomic mode */ +/* see db.h */ int db_transaction_end( bool commit ) { int rc1, rc2, rc3, rc4; + if (!modifiable) + return -EALREADY; + if (commit) { rc1 = anydb_transaction(filedb, Anydb_Transaction_Commit); rc2 = anydb_transaction(memdb, Anydb_Transaction_Commit); @@ -106,54 +125,61 @@ db_transaction_end( rc3 = 0; } rc4 = db_sync(); + modifiable = false; return rc1 ?: rc2 ?: rc3 ?: rc4; } -/** enumerate */ +/* see db.h */ void db_for_all( - void *closure, void (*callback)( void *closure, const data_key_t *key, const data_value_t *value), + void *closure, const data_key_t *key ) { - anydb_for_all(filedb, closure, callback, key); - anydb_for_all(memdb, closure, callback, key); + anydb_for_all(filedb, callback, closure, key); + anydb_for_all(memdb, callback, closure, key); } -/** drop rules */ +/* see db.h */ int db_drop( const data_key_t *key ) { + if (!modifiable) + return -EACCES; + anydb_drop(filedb, key); anydb_drop(memdb, key); return 0; } -/** set rules */ +/* see db.h */ int db_set( const data_key_t *key, const data_value_t *value ) { - if (is_any_or_wide(key->session)) - return anydb_set(filedb, key, value); - else - return anydb_set(memdb, key, value); + anydb_t *db; + + if (!modifiable) + return -EACCES; + + db = is_any_or_wide(key->session) ? filedb : memdb; + return anydb_set(db, key, value); } -/** check rules */ -int +/* see db.h */ +unsigned db_test( const data_key_t *key, data_value_t *value ) { - int s1, s2; + unsigned s1, s2; data_value_t v1, v2; s1 = anydb_test(memdb, key, &v1); @@ -161,12 +187,13 @@ db_test( if (s2 > s1) { *value = v2; return s2; - } else { - *value = v1; - return s1; } + if (s1) + *value = v1; + return s1; } +/* see db.h */ int db_cleanup( ) { @@ -175,6 +202,7 @@ db_cleanup( return 0; } +/* see db.h */ int db_sync( ) { diff --git a/src/db.h b/src/db.h index edb4f29..0171c02 100644 --- a/src/db.h +++ b/src/db.h @@ -14,63 +14,94 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* INTERNAL DATABASE IMPLEMENTATION */ +/******************************************************************************/ +/******************************************************************************/ -#define MAX_NAME_LENGTH 32767 - -/** open the database for files 'names' and 'rules' (can be NULL) */ +/** + * Open the database in the directory + * + * @param directory the directory containing the database + * @return 0 in case of success or a negative error code + * + * @see db_close + */ extern int db_open( const char *directory ); -/** close the database */ +/** + * close the database + */ extern void db_close( ); -/** is the database empty */ +/** + * Is the database empty? + * + * @return true if empty or else false + */ extern bool db_is_empty( ); -/** enter atomic mode */ +/** + * Enter atomic mode or cancelable mode + * + * @return 0 in case of success or a negative -errno like value + * + * @see db_transaction_end, db_drop, db_set + */ extern int db_transaction_begin( ); -/** leave atomic mode */ +/** + * Leave atomic mode commiting or not the changes + * + * @param commit if true changes are commited otherwise, if false cancel them + * @return 0 in case of success or a negative -errno like value + * + * @see db_transaction_begin, db_drop, db_set + */ extern int db_transaction_end( bool commit ); -/** enumerate */ -extern -void -db_for_all( - void *closure, - void (*callback)( - void *closure, - const data_key_t *key, - const data_value_t *value), - const data_key_t *key -); - -/** erase rules */ +/** + * Erase rules matching the key + * + * @param key the search key for the rules to remove + * @return 0 in case of success or a negative -errno like value + * + * @see db_transaction_begin, db_transaction_end, db_set + */ extern int db_drop( const data_key_t *key ); -/** set rules */ +/** + * Add the rule of key and value + * + * @param key the key of the rule to add + * @param value the value of the rule + * @return 0 in case of success or a negative -errno like value + * + * @see db_transaction_begin, db_transaction_end, db_drop + */ extern int db_set( @@ -78,21 +109,54 @@ db_set( const data_value_t *value ); -/** check rules */ +/** + * Iterate over rules matching the key: call the callback for each found item + * + * @param callback the callback function to be call for each rule matching key + * @param closure the closure of the callback + * @param key the searching key + */ extern -int +void +db_for_all( + void (*callback)( + void *closure, + const data_key_t *key, + const data_value_t *value), + void *closure, + const data_key_t *key +); + +/** + * Get the rule value for the key + * + * @param key The key to query + * @param value Where to store the result if any + * @return 0 if no rule matched (value unchanged then) or a positive integer + * when a value was found for the key + */ +extern +unsigned db_test( const data_key_t *key, data_value_t *value ); -/** cleanup the base */ +/** + * Cleanup the database by removing expired items + * + * @return 0 in case of success or a negative -errno like value + */ extern int db_cleanup( ); -/** cleanup the base */ +/** + * Write the database to the file system (synchrnize it) + * + * @return 0 in case of success or a negative -errno like value + */ extern int db_sync( diff --git a/src/dbinit.c b/src/dbinit.c index b1b3c54..e2366ad 100644 --- a/src/dbinit.c +++ b/src/dbinit.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* READING DATABASE RULE FILES FOR INITIALISATION */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -28,7 +33,7 @@ #include "expire.h" #include "dbinit.h" -/** initialize the database from file of 'path' */ +/* see dbinit.h */ int dbinit_add_file(const char *path) { int rc, lino; @@ -115,8 +120,10 @@ error2: fclose(f); error: if (rc) + /* cancel changes if error occured */ cyn_leave(dbinit_add_file, 0); else { + /* commit the changes */ rc = cyn_leave(dbinit_add_file, 1); if (rc < 0) fprintf(stderr, "unable to commit content of file %s\n", path); diff --git a/src/dbinit.h b/src/dbinit.h index bca4afc..a546575 100644 --- a/src/dbinit.h +++ b/src/dbinit.h @@ -14,9 +14,53 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* READING DATABASE RULE FILES FOR INITIALISATION */ +/******************************************************************************/ +/******************************************************************************/ -extern int dbinit_add_file(const char *path); +/** + * Add to the database the data from file of 'path' + * + * --------------------------------------------------------------------------- + * The file must be made of lines being either empty, comment or rule + * + * Empty lines or comment lines are ignored. + * + * An empty line only contains spaces and/or tabs + * + * A comment line start with the character #. It can be preceded by any count + * of spaces and/or tabs. + * + * Other lines are rules. They must be made of 6 fields separated by any count + * of space and/or tabs. Spaces and tabs at the begining of the line are + * ignored. + * + * The 6 fields of a rule are: + * + * CLIENT SESSION USER PERMISSION VALUE EXPIRATION + * + * CLIENT, SESSION, USER, PERMISSION are arbitrary strings. The single star + * mean 'any value'. + * + * Value must be a string of some meaning. Known values are: + * + * - yes: grant the permission + * - no: forbid the permission + * - @:...: agent at (@) + * + * Expiration can be expressed + * + * --------------------------------------------------------------------------- + * + * @param path path of the initialization file + * @return 0 in case of success or a negative -errno like code + */ +extern +int +dbinit_add_file( + const char *path +); diff --git a/src/expire.c b/src/expire.c index 68278ca..cfa5dff 100644 --- a/src/expire.c +++ b/src/expire.c @@ -14,18 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* CONVERTION OF EXPIRATIONS TO AND FROM TEXT */ +/******************************************************************************/ +/******************************************************************************/ #include #include #include +#include "expire.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; +static const int YEAR = 365*24*60*60 + 24*60*60/4; +/* see expire.h */ time_t txt2exp(const char *txt) { time_t r, x; @@ -39,7 +47,7 @@ time_t txt2exp(const char *txt) while(*txt) { x = 0; while('0' <= *txt && *txt <= '9') - x = 10 * x + (time_t)(*txt++ - '0'); + x = (x << 3) + (x << 1) + (time_t)(*txt++ - '0'); switch(*txt) { case 'y': r += x * YEAR; txt++; break; case 'w': r += x * WEEK; txt++; break; @@ -54,6 +62,7 @@ time_t txt2exp(const char *txt) return r; } +/* see expire.h */ size_t exp2txt(time_t expire, char *buffer, size_t buflen) { char b[100]; diff --git a/src/expire.h b/src/expire.h index c9e46bc..1de82c2 100644 --- a/src/expire.h +++ b/src/expire.h @@ -14,8 +14,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* CONVERTION OF EXPIRATIONS TO AND FROM TEXT */ +/******************************************************************************/ +/******************************************************************************/ + +/** + * Converts the time of the string to an expiration + * + * The string code a time relative to now using the format + * XXXyXXXwXXXdXXXhXXXmXXXs where XXX are numbers. + * + * Examples: + * - 15 means 15 seconds + * - 4h15m30s means 4 hours 15 minutes 30 seconds + * - forever means forever + * - 2m2w means two months and 2 weeks + * + * @param txt the text to convert + * @return the value for the text + */ +extern +time_t +txt2exp( + const char *txt +); -extern time_t txt2exp(const char *txt); -extern size_t exp2txt(time_t expire, char *buffer, size_t buflen); +/** + * Converts the expiration in to its relative string representation + * + * @param expire the epiration to convert + * @param buffer the buffer where to store the converted string + * @param buflen length of the buffer + * @return the length of the resulting string, can be greater than buflen but + * in that case, no more than buflen characters are copied to buffer + */ +extern +size_t +exp2txt( + time_t expire, + char *buffer, + size_t buflen +); diff --git a/src/fbuf.c b/src/fbuf.c index c4243e6..0c88fcb 100644 --- a/src/fbuf.c +++ b/src/fbuf.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF BUFFERED FILES */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -31,16 +36,28 @@ #include "fbuf.h" -/** compute the size to allocate for ensuring 'sz' bytes */ +/** + * compute the size to allocate for ensuring 'sz' bytes + * @param sz the expected size + * @return a size greater than sz + */ static uint32_t get_asz( uint32_t sz ) { - return (sz & 0xfffffc00) + 0x000004cf; + uint32_t r = (sz & 0xfffffc00) + 0x000004cf; + return r > sz ? r : UINT32_MAX; } -/** open in 'fb' the file of 'name' */ +/** + * Open in 'fb' the file of 'name' + * @param fb the fbuf + * @param name the name of the file to read + * @return 0 on success + * -EFBIG if the file is too big + * -errno system error + */ static int read_file( @@ -91,7 +108,7 @@ error: return rc; } -/** open in 'fb' the file of 'name' */ +/* see fbuf.h */ int fbuf_open( fbuf_t *fb, @@ -105,18 +122,19 @@ fbuf_open( memset(fb, 0, sizeof *fb); /* save name */ - fb->name = strdup(name); + sz = strlen(name); + fb->name = malloc(sz + 1); if (fb->name == NULL) goto error; + mempcpy(fb->name, name, sz + 1); /* 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); + mempcpy(fb->backup, name, sz); fb->backup[sz] = '~'; fb->backup[sz + 1] = 0; } @@ -129,6 +147,7 @@ fbuf_open( if (rc < 0) goto error; + /* any read data is already saved */ fb->saved = fb->used; return 0; @@ -139,7 +158,7 @@ error: return rc; } -/** close the file 'fb' */ +/* see fbuf.h */ void fbuf_close( fbuf_t *fb @@ -150,7 +169,7 @@ fbuf_close( memset(fb, 0, sizeof *fb); } -/** write to file 'fb' the unsaved bytes and flush the content to the file */ +/* see fbuf.h */ int fbuf_sync( fbuf_t *fb @@ -173,6 +192,8 @@ fbuf_sync( close(fd); if (rcs < 0) goto error; + if ((uint32_t)rcs != fb->used) + goto error; /* TODO: set some errno? */ fb->size = fb->saved = fb->used; return 0; @@ -183,29 +204,29 @@ error: return rc; } -/** allocate enough memory in 'fb' to store 'count' bytes */ +/* see fbuf.h */ int fbuf_ensure_capacity( fbuf_t *fb, - uint32_t count + uint32_t capacity ) { - uint32_t capacity; + uint32_t asz; void *buffer; - if (count > fb->capacity) { - capacity = get_asz(count); - buffer = realloc(fb->buffer, capacity); + if (capacity > fb->capacity) { + asz = get_asz(capacity); + buffer = realloc(fb->buffer, asz); if (buffer == NULL) { - fprintf(stderr, "alloc %u for file %s failed: %m\n", capacity, fb->name); + fprintf(stderr, "alloc %u for file %s failed: %m\n", asz, fb->name); return -ENOMEM; } fb->buffer = buffer; - fb->capacity = capacity; + fb->capacity = asz; } return 0; } -/** put at 'offset' in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +/* see fbuf.h */ int fbuf_put( fbuf_t *fb, @@ -214,12 +235,13 @@ fbuf_put( uint32_t offset ) { int rc; - uint32_t end = offset + count; + uint32_t end; /* don't call me for nothing */ assert(count); /* grow as necessary */ + end = offset + count; if (end > fb->used) { rc = fbuf_ensure_capacity(fb, end); if (rc < 0) @@ -236,7 +258,7 @@ fbuf_put( return 0; } -/** append at end in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +/* see fbuf.h */ int fbuf_append( fbuf_t *fb, @@ -249,7 +271,7 @@ fbuf_append( return fbuf_put(fb, buffer, count, fb->used); } -/** check or make identification of file 'fb' by 'id' of 'len' */ +/* see fbuf.h */ int fbuf_identify( fbuf_t *fb, @@ -265,12 +287,11 @@ fbuf_identify( return 0; /* bad identification */ - errno = ENOKEY; fprintf(stderr, "identification of file %s failed: %m\n", fb->name); return -ENOKEY; } -/** check or make identification by 'uuid' of file 'fb' */ +/* see fbuf.h */ int fbuf_open_identify( fbuf_t *fb, @@ -281,16 +302,18 @@ fbuf_open_identify( ) { int rc; + /* open the files */ rc = fbuf_open(fb, name, backup); if (rc == 0) { + /* check identifier */ rc = fbuf_identify(fb, id, idlen); if (rc < 0) - fbuf_close(fb); + fbuf_close(fb); /* close if error */ } return rc; } -/** make a backup */ +/* see fbuf.h */ int fbuf_backup( fbuf_t *fb @@ -299,7 +322,7 @@ fbuf_backup( return link(fb->name, fb->backup); } -/** recover from latest backup */ +/* see fbuf.h */ int fbuf_recover( fbuf_t *fb diff --git a/src/fbuf.h b/src/fbuf.h index 43909fa..ee9ac25 100644 --- a/src/fbuf.h +++ b/src/fbuf.h @@ -14,8 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF BUFFERED FILES */ +/******************************************************************************/ +/******************************************************************************/ /** * A fbuf records file data and access @@ -48,7 +52,14 @@ struct fbuf typedef struct fbuf fbuf_t; -/** open in 'fb' the file of 'name' and optionnal 'backup' name */ +/** + * open in 'fb' the file of 'name' + * @param fb the fbuf + * @param name name of the filename to read + * @param backup name of the backup to use (can be NULL) + * @return 0 on success + * a negative -errno code + */ extern int fbuf_open( @@ -57,21 +68,35 @@ fbuf_open( const char *backup ); -/** close the file 'fb' */ +/** + * close the fbuf 'fb' + * @param fb the fbuf to close + */ extern void fbuf_close( fbuf_t *fb ); -/** write to file 'fb' the unsaved bytes and flush the content to the file */ +/** + * write to fbuf 'fb' the unsaved bytes and flush the content to the file + * @param fb the fbuf + * @return 0 on success + * a negative -errno code + */ extern int fbuf_sync( fbuf_t *fb ); -/** allocate enough memory in 'fb' to store 'count' bytes */ +/** + * allocate enough memory in 'fb' to store 'count' bytes + * @param fb the fbuf + * @param capacity expected capacity + * @return 0 on success + * -ENOMEM if out of memory + */ extern int fbuf_ensure_capacity( @@ -79,7 +104,15 @@ fbuf_ensure_capacity( uint32_t count ); -/** put at 'offset' in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +/** + * put at 'offset' in the memory of 'fb' the 'count' bytes pointed by 'buffer' + * @param fb the fbuf + * @param buffer pointer to the data + * @param count size of data MUST BE GREATER THAN ZERO + * @param offset where to put the data + * @return 0 on success + * -ENOMEM if out of memory + */ extern int fbuf_put( @@ -89,7 +122,14 @@ fbuf_put( uint32_t offset ); -/** append at end in the memory of 'fb' the 'count' bytes pointed by 'buffer' */ +/** + * append at end in the memory of 'fb' the 'count' bytes pointed by 'buffer' + * @param fb the fbuf + * @param buffer pointer to the data + * @param count size of data MUST BE GREATER THAN ZERO + * @return 0 on success + * -ENOMEM if out of memory + */ extern int fbuf_append( @@ -98,7 +138,16 @@ fbuf_append( uint32_t count ); -/** check or make identification of file 'fb' by 'id' of 'len' */ +/** + * Check or make identification of file 'fb' by 'id' of 'idlen' + * If the content is empty, it initialize the identification prefix. + * Otherwise, not empty, the check is performed. + * @param fb the fbuf to check + * @param id the prefix identifier to check + * @param idlen the length of the identifier + * @return 0 on success + * -ENOKEY if identification failed + */ extern int fbuf_identify( @@ -107,7 +156,19 @@ fbuf_identify( uint32_t idlen ); -/** check or make identification by 'uuid' of file 'fb' */ +/** + * Open the fbuf 'fb' of 'name', 'backup' and check that it has the + * prefix identifier 'id' of length 'idlen'. + * @param fb the fbuf to open + * @param name file name to open + * @param backup name of the backup file + * @param id identifier prefix value + * @param idlen length of the identifier prefix + * @return 0 in case of success + * -ENOMEM if out of memory + * -ENOKEY if identification failed + * a negative -errno code + */ extern int fbuf_open_identify( @@ -118,12 +179,26 @@ fbuf_open_identify( uint32_t idlen ); +/** + * Create the back-up file + * Backup is managed using hard links. It implies that the operating system + * handles hard links. + * @param fb the fbuf + * @return 0 in case of success + * a negative -errno code + */ extern int fbuf_backup( fbuf_t *fb ); +/** + * recover data from latest backup + * @param fb the fbuf + * @return 0 on success + * a negative -errno code + */ extern int fbuf_recover( diff --git a/src/filedb.c b/src/filedb.c index ce37ad1..9a57a68 100644 --- a/src/filedb.c +++ b/src/filedb.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF DATABASE WITH FILE BACKEND */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -31,8 +36,6 @@ #include "fbuf.h" #include "filedb.h" -#define MAX_NAME_LENGTH 32768 - /* * for the first version, save enougth time up to 4149 * 4149 = 1970 + (4294967296 * 16) / (365 * 24 * 60 * 60) @@ -125,11 +128,16 @@ struct filedb bool has_backup; /** the anydb interface */ - anydb_t db; + anydb_t anydb; }; typedef struct filedb filedb_t; -/** return the name of 'index' */ +/** + * Return the name of the given index + * @param filedb the database handler + * @param index index of the string MUST be valid + * @return the name for the index + */ static const char* name_at( @@ -153,57 +161,62 @@ cmpnames( return strcmp(name_at(filedb, a), name_at(filedb, b)); } -/** initialize names */ +/** + * Initialize the fields 'names_sorted' and 'names_count' for the + * current database. + * @param filedb the database handler + * @return 0 in case of success or -ENOMEM or -ENOEXEC + */ static int init_names( filedb_t *filedb ) { - uint32_t pos, len, *ns, *p, all, nc; + uint32_t pos, length, *sorted, *p, allocated, name_count; - all = 0; - nc = 0; - ns = NULL; + allocated = 0; + name_count = 0; + sorted = NULL; /* iterate over names */ pos = uuidlen; while (pos < filedb->fnames.used) { /* get name length */ - len = (uint32_t)strlen(name_at(filedb, pos)); - if (pos + len <= pos || pos + len > filedb->fnames.used) { - free(ns); - goto bad_file; + length = (uint32_t)strlen(name_at(filedb, pos)); + if (pos + length <= pos || pos + length > filedb->fnames.used) { + /* overflow */ + free(sorted); + fprintf(stderr, "bad file %s\n", filedb->fnames.name); + return -ENOEXEC; } /* store the position */ - if (all <= nc) { - all += 1024; - p = realloc(ns, all * sizeof *ns); + if (allocated <= name_count) { + allocated += 1024; + p = realloc(sorted, allocated * sizeof *sorted); if (p == NULL) { - free(ns); + free(sorted); fprintf(stderr, "out of memory\n"); - goto error; + return -ENOMEM; } - ns = p; + sorted = p; } - ns[nc++] = pos; + sorted[name_count++] = pos; /* next */ - pos += len + 1; + pos += length + 1; } /* sort and record */ - qsort_r(ns, nc, sizeof *ns, cmpnames, filedb); - filedb->names_sorted = ns; - filedb->names_count = nc; + qsort_r(sorted, name_count, sizeof *sorted, cmpnames, filedb); + filedb->names_sorted = sorted; + filedb->names_count = name_count; return 0; - -bad_file: - fprintf(stderr, "bad file %s\n", filedb->fnames.name); - errno = ENOEXEC; -error: - return -1; } -/** init the rules from the file */ +/** + * Initialize the fields 'rules' and 'rules_count' for the + * current database. + * @param filedb the database handler + */ static void init_rules( @@ -213,7 +226,21 @@ init_rules( filedb->rules_count = (filedb->frules.used - uuidlen) / sizeof *filedb->rules; } -/** open a fbuf */ +/** + * Open the fbuf 'fb' in the directory, the name and the extension. + * Check that the identifier prefix matches or if the file doesn't exist + * create the prefix. + * @param fb the buffer to open + * @param directory the directory containing the file + * @param name the basename for the file + * @param extension the extension of the file + * @param id the identifier prefix + * @param idlen the length of the identifier prefix + * @return 0 in case of success + * -ENOMEM if out of memory + * -ENOKEY if identification failed + * a negative -errno code + */ static int open_identify( @@ -224,25 +251,38 @@ open_identify( const char *id, uint32_t idlen ) { - char *file, *backup, *p; + char *file, *p; size_t ldir, lext, lname; + /* compute sizes */ ldir = strlen(directory); lname = strlen(name); lext = strlen(extension); - file = alloca(((ldir + lname + lext) << 1) + 7); + + /* allocate memory for file */ + file = alloca((ldir + lname + lext) + 3); + + /* make the file's name: directory/name.extension */ p = mempcpy(file, directory, ldir); *p++ = '/'; p = mempcpy(p, name, lname); *p++ = '.'; - backup = mempcpy(p, extension, lext + 1); - p = mempcpy(backup, file, ldir + lname + lext + 2); - *p++ = '~'; - *p = 0; - return fbuf_open_identify(fb, file, backup, id, idlen); + mempcpy(p, extension, lext + 1); + + /* open the fbuf now */ + return fbuf_open_identify(fb, file, NULL, id, idlen); } -/** open the database for files 'names' and 'rules' (can be NULL) */ +/** + * Open the database of 'name' in 'directory' + * @param filedb the database handler to open + * @param directory the directory containing the database (or null for default) + * @param name the basename for the file + * @return 0 in case of success + * -ENOMEM if out of memory + * -ENOKEY if identification failed + * a negative -errno code + */ static int opendb( @@ -262,26 +302,27 @@ opendb( /* open the names */ rc = open_identify(&filedb->fnames, directory, name, "names", uuid_names_v2, uuidlen); - if (rc < 0) - goto error; - - /* open the rules */ - rc = open_identify(&filedb->frules, directory, name, "rules", uuid_rules_v2, uuidlen); - if (rc < 0) - goto error; - - /* connect internals */ - rc = init_names(filedb); - if (rc < 0) - goto error; - - init_rules(filedb); - return 0; -error: - return -1; + if (rc == 0) { + /* open the rules */ + rc = open_identify(&filedb->frules, directory, name, "rules", uuid_rules_v2, uuidlen); + if (rc == 0) { + /* connect internals */ + rc = init_names(filedb); + if (rc == 0) { + init_rules(filedb); + return 0; + } + fbuf_close(&filedb->frules); + } + fbuf_close(&filedb->fnames); + } + return rc; } -/** close the database */ +/** + * Close the database + * @param filedb database to close + */ static void closedb( @@ -292,7 +333,12 @@ closedb( fbuf_close(&filedb->frules); } -/** synchronize db on files */ +/** + * Synchronize database and its files (write it to the filesystem) + * @param filedb database to synchronize + * @return 0 in case of success + * a negative -errno code + */ static int syncdb( @@ -302,10 +348,12 @@ syncdb( assert(filedb->fnames.name && filedb->frules.name); if (!filedb->is_changed) - rc = 0; + rc = 0; /* unchanged */ else { + /* sync the names */ rc = fbuf_sync(&filedb->fnames); if (rc == 0) { + /* sync the rules */ rc = fbuf_sync(&filedb->frules); if (rc == 0) { filedb->is_changed = false; @@ -316,7 +364,12 @@ syncdb( return rc; } -/** make a backup of the database */ +/** + * Creates backups of the database + * @param filedb the database to backup + * @return 0 in case of success + * a negative -errno code + */ static int backupdb( @@ -326,21 +379,26 @@ backupdb( assert(filedb->fnames.name && filedb->frules.name); if (filedb->has_backup) - rc = 0; + rc = 0; /* already backuped */ else { + /* backup names */ rc = fbuf_backup(&filedb->fnames); if (rc == 0) { + /* backup rules */ rc = fbuf_backup(&filedb->frules); - if (rc == 0) { + if (rc == 0) filedb->has_backup = true; - filedb->is_changed = false; - } } } return rc; } -/** recover the database from latest backup */ +/** + * recover the database from latest backup + * @param filedb database to recover + * @return 0 in case of success + * a negative -errno code + */ static int recoverdb( @@ -352,14 +410,17 @@ recoverdb( if (!filedb->is_changed || !filedb->has_backup) rc = 0; else { + /* recover names */ rc = fbuf_recover(&filedb->fnames); if (rc < 0) goto error; + /* recover rules */ rc = fbuf_recover(&filedb->frules); if (rc < 0) goto error; + /* init names */ rc = init_names(filedb); if (rc < 0) goto error; @@ -375,6 +436,7 @@ error: return rc; } +/** implementation of anydb_itf.index */ static int index_itf( @@ -446,6 +508,7 @@ index_itf( return 0; } +/** implementation of anydb_itf.string */ static const char * string_itf( @@ -454,14 +517,16 @@ string_itf( ) { filedb_t *filedb = clodb; + assert(idx < filedb->fnames.used); return name_at(filedb, idx); } +/** implementation of anydb_itf.apply */ static void apply_itf( void *clodb, - anydb_action_t (*oper)(void *closure, const anydb_key_t *key, anydb_value_t *value), + anydb_applycb_t *oper, void *closure ) { filedb_t *filedb = clodb; @@ -469,7 +534,7 @@ apply_itf( rule_t *rule; anydb_key_t key; anydb_value_t value; - uint32_t i; + uint32_t i, saved; key.session = AnyIdx_Wide; i = 0; @@ -481,29 +546,30 @@ apply_itf( value.value = rule->value; value.expire = exp2time(rule->expire); a = oper(closure, &key, &value); - switch (a) { - case Anydb_Action_Stop: - return; - case Anydb_Action_Continue: - i++; - break; - case Anydb_Action_Update_And_Stop: - rule->value = value.value; - rule->expire = time2exp(value.expire); - filedb->need_cleanup = true; - filedb->is_changed = true; - filedb->frules.saved = (uint32_t)((void*)rule - filedb->frules.buffer); - return; - case Anydb_Action_Remove_And_Continue: + if (a & Anydb_Action_Remove) { *rule = filedb->rules[--filedb->rules_count]; filedb->is_changed = true; filedb->need_cleanup = true; + saved = (uint32_t)((void*)rule - filedb->frules.buffer); + if (saved < filedb->frules.saved) + filedb->frules.saved = saved; filedb->frules.used -= (uint32_t)sizeof *rule; - break; + } else if (a & Anydb_Action_Update) { + rule->value = value.value; + rule->expire = time2exp(value.expire); + filedb->need_cleanup = true; + filedb->is_changed = true; + saved = (uint32_t)((void*)rule - filedb->frules.buffer); + if (saved < filedb->frules.saved) + filedb->frules.saved = saved; } + if (a & Anydb_Action_Stop) + return; + i += !(a & Anydb_Action_Remove); } } +/** implementation of anydb_itf.transaction */ static int transaction_itf( @@ -531,6 +597,7 @@ transaction_itf( return rc; } +/** implementation of anydb_itf.add */ static int add_itf( @@ -562,6 +629,16 @@ add_itf( return 0; } +/** + * Search (dig) dichotomically in 'array' of 'count' elements the index of + * 'item'. Store '*index' either the index of the found item or the index + * where inserting the item. Return true if found of false otherwise. + * @param array array to dig + * @param count count of elements in array + * @param item item to dig + * @param index where to store the found index + * @return true if found of false otherwise. + */ static bool gc_dig( @@ -593,6 +670,13 @@ gc_dig( return false; } +/** + * Add dichotomically the 'item' in the 'array' of 'count' elements + * @param array array to alter + * @param count count of element in the input array + * @param item the item to add + * @return the new count of elements + */ static uint32_t gc_add( @@ -602,18 +686,29 @@ gc_add( ) { uint32_t index, i; + /* search the item */ if (gc_dig(array, count, item, &index)) - return count; + return count; /* already in */ + /* shift the elemetns above index */ i = count; while (i > index) { array[i] = array[i - 1]; i = i - 1; } + + /* add the item */ array[i] = item; return count + 1; } +/** + * Mark in 'array' of 'count' elements the 'item' + * @param array the sorted array of marked items + * @param count the count of marked items + * @param item the item to mark + * @return the new count of marked items + */ static uint32_t gc_mark( @@ -624,30 +719,60 @@ gc_mark( return item > AnyIdx_Max ? count : gc_add(array, count, item); } +/** + * Test if 'item' is marked in 'array' of 'count' marked elements + * @param array the sorted array of marked items + * @param count the count of marked items + * @param item the item to search + * @param index where to store the index of the item if found + * @return true is found (marked) or false otherwise (not marked) + */ static bool -gc_new( +gc_is_marked( uint32_t *array, uint32_t count, uint32_t item, uint32_t *index ) { - return item > AnyIdx_Max ? false : gc_dig(array, count, item, index); + return item <= AnyIdx_Max && gc_dig(array, count, item, index); +} + +/** + * Translate the item pointed by 'item' to its new value after renumeration + * @param marked the sorted array of marked items + * @param renum the renumerotation of the marked items + * @param count the count of marked items + * @param item the pointer to the item to modify + */ +static +void +gc_renum( + uint32_t *marked, + uint32_t *renum, + uint32_t count, + uint32_t *item +) { + uint32_t index; + + if (gc_is_marked(marked, count, *item, &index)) + *item = renum[index]; } +/** implementation of anydb_itf.gc */ static void gc_itf( void *clodb ) { filedb_t *filedb = clodb; - uint32_t nr; - uint32_t nn; + uint32_t rule_count; + uint32_t name_count; struct rule *rules; - uint32_t *used; - uint32_t *sorted; + uint32_t *marked; + uint32_t *renum; char *strings; - uint32_t ir, nu, idx, is, ios, lenz; + uint32_t irule, new_count, imarked, istr_before, istr_after, lenz; /* check cleanup required */ if (!filedb->need_cleanup) @@ -655,60 +780,57 @@ gc_itf( filedb->need_cleanup = false; /* mark items */ - nr = filedb->rules_count; - nn = filedb->names_count; + rule_count = filedb->rules_count; + name_count = filedb->names_count; rules = filedb->rules; - used = alloca(nn * sizeof *used); - nu = 0; - for (ir = 0 ; ir < nr ; ir++) { - nu = gc_mark(used, nu, rules[ir].client); - nu = gc_mark(used, nu, rules[ir].user); - nu = gc_mark(used, nu, rules[ir].permission); - nu = gc_mark(used, nu, rules[ir].value); + marked = alloca(name_count * sizeof *marked); + new_count = 0; + for (irule = 0 ; irule < rule_count ; irule++) { + new_count = gc_mark(marked, new_count, rules[irule].client); + new_count = gc_mark(marked, new_count, rules[irule].user); + new_count = gc_mark(marked, new_count, rules[irule].permission); + new_count = gc_mark(marked, new_count, rules[irule].value); } /* pack if too much unused */ - if (nu + (nu >> 2) <= nn) + if (new_count + (new_count >> 2) >= name_count) return; - /* pack the names */ + /* pack the names by removing the unused strings */ strings = (char*)filedb->fnames.buffer; - sorted = filedb->names_sorted; - is = ios = uuidlen; - while (is < filedb->fnames.used) { + renum = filedb->names_sorted; + istr_before = istr_after = uuidlen; + while (istr_before < filedb->fnames.used) { /* get name length */ - lenz = 1 + (uint32_t)strlen(strings + is); - if (gc_dig(used, nu, is, &idx)) { - sorted[idx] = ios; - if (is != ios) - memcpy(strings + ios, strings + is, lenz); - ios += lenz; + lenz = 1 + (uint32_t)strlen(strings + istr_before); + if (gc_is_marked(marked, new_count, istr_before, &imarked)) { + renum[imarked] = istr_after; + if (istr_before != istr_after) + memcpy(strings + istr_after, strings + istr_before, lenz); + istr_after += lenz; } /* next */ - is += lenz; + istr_before += lenz; } /* renum the rules */ - for (ir = 0 ; ir < nr ; ir++) { - if (gc_new(used, nu, rules[ir].client, &idx)) - rules[ir].client = sorted[idx]; - if (gc_new(used, nu, rules[ir].user, &idx)) - rules[ir].user = sorted[idx]; - if (gc_new(used, nu, rules[ir].permission, &idx)) - rules[ir].permission = sorted[idx]; - if (gc_new(used, nu, rules[ir].value, &idx)) - rules[ir].value = sorted[idx]; + for (irule = 0 ; irule < rule_count ; irule++) { + gc_renum(marked, renum, new_count, &rules[irule].client); + gc_renum(marked, renum, new_count, &rules[irule].user); + gc_renum(marked, renum, new_count, &rules[irule].permission); + gc_renum(marked, renum, new_count, &rules[irule].value); } /* record and sort */ - filedb->names_count = nu; - filedb->fnames.used = ios; - qsort_r(sorted, nu, sizeof *sorted, cmpnames, filedb); + filedb->names_count = new_count; + filedb->fnames.used = istr_after; + qsort_r(renum, new_count, sizeof *renum, cmpnames, filedb); /* set as changed */ filedb->is_changed = true; } +/** implementation of anydb_itf.sync */ static int sync_itf( @@ -718,6 +840,7 @@ sync_itf( return syncdb(filedb); } +/** implementation of anydb_itf.destroy */ static void destroy_itf( @@ -731,23 +854,28 @@ destroy_itf( } } +/** + * Initialize the anydb interface of filedb + * @param filedb the structure to initialize + */ static void -init( +init_anydb_itf( filedb_t *filedb ) { - filedb->db.clodb = filedb; - - filedb->db.itf.index = index_itf; - filedb->db.itf.string = string_itf; - filedb->db.itf.transaction = transaction_itf; - filedb->db.itf.apply = apply_itf; - filedb->db.itf.add = add_itf; - filedb->db.itf.gc = gc_itf; - filedb->db.itf.sync = sync_itf; - filedb->db.itf.destroy = destroy_itf; + filedb->anydb.clodb = filedb; + + filedb->anydb.itf.index = index_itf; + filedb->anydb.itf.string = string_itf; + filedb->anydb.itf.transaction = transaction_itf; + filedb->anydb.itf.apply = apply_itf; + filedb->anydb.itf.add = add_itf; + filedb->anydb.itf.gc = gc_itf; + filedb->anydb.itf.sync = sync_itf; + filedb->anydb.itf.destroy = destroy_itf; } +/* see filedb.h */ int filedb_create( anydb_t **adb, @@ -757,25 +885,21 @@ filedb_create( int rc; filedb_t *filedb; + /* allocates */ *adb = NULL; filedb = calloc(1, sizeof *filedb); if (!filedb) return -ENOMEM; - init(filedb); + /* init anydb interface */ + init_anydb_itf(filedb); + /* open the database file */ rc = opendb(filedb, directory, basename); if (rc) free(filedb); else - *adb = &filedb->db; + *adb = &filedb->anydb; return rc; } -/** synchronize database */ -int -anydb_sync( - anydb_t *db -) { - return db->itf.sync ? db->itf.sync(db->clodb) : 0; -} diff --git a/src/filedb.h b/src/filedb.h index d575e50..85d6c26 100644 --- a/src/filedb.h +++ b/src/filedb.h @@ -14,11 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF DATABASE WITH FILE BACKEND */ +/******************************************************************************/ +/******************************************************************************/ - -/** is the database empty */ +/** + * Create the object handling the file database + * @param filedb pointer to the handling object to return + * @param directory the directory of the database + * @param basename the basename of the database + * @return 0 in case of success with *filedb fulfilled or negative -errno error + */ int filedb_create( anydb_t **filedb, diff --git a/src/lib-compat.c b/src/lib-compat.c index eb98895..63696fe 100644 --- a/src/lib-compat.c +++ b/src/lib-compat.c @@ -14,7 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/******************************************************************************/ +/******************************************************************************/ +/* COMPATIBILITY LAYER TO PREVIOUS CYNARA */ +/******************************************************************************/ +/******************************************************************************/ /* cynara_admin_initialize(&m_CynaraAdmin), cynara_admin_finish(m_CynaraAdmin); diff --git a/src/main-cynadm.c b/src/main-cynadm.c index e099d64..9f3a179 100644 --- a/src/main-cynadm.c +++ b/src/main-cynadm.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF CYNARA ADMINISTRATION TOOL */ +/******************************************************************************/ +/******************************************************************************/ #include #include diff --git a/src/main-cynarad.c b/src/main-cynarad.c index 2b8b0d3..6ed1878 100644 --- a/src/main-cynarad.c +++ b/src/main-cynarad.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF CYNARA SERVER */ +/******************************************************************************/ +/******************************************************************************/ #include #include diff --git a/src/memdb.c b/src/memdb.c index 285d7a4..a67d1ef 100644 --- a/src/memdb.c +++ b/src/memdb.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF IN MEMORY DATABASE WITHOUT FILE BACKEND */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -21,51 +26,76 @@ #include #include #include +#include #include "data.h" #include "anydb.h" +#include "memdb.h" -#define RBS 20 /**< rule block size */ -#define SBS 30 /**< string bloc size */ +#define RULE_BLOC_SIZE 20 /**< rule block size */ +#define STRING_BLOC_SIZE 30 /**< string bloc size */ -#define TCLE 0 /**< tag for clean */ -#define TDEL 1 /**< tag for deleted */ -#define TMOD 2 /**< tag for modified */ +#define TAG_CLEAN 0 /**< tag for clean */ +#define TAG_DELETED 1 /**< tag for deleted */ +#define TAG_CHANGED 2 /**< tag for modified */ +/** + * structure for rules of memory database + */ struct rule { + /** the key */ anydb_key_t key; + + /** the current value */ anydb_value_t value; + + /** the next value (depends on tag) */ anydb_value_t saved; + + /** tag for the value saved */ uint8_t tag; }; +/** + * Structure for the memory database + */ struct memdb { - /* first for the fun */ + /** first for the fun */ anydb_t db; - /* strings */ + /** strings */ struct { + /** allocated count for strings */ uint32_t alloc; + /** used count for strings */ uint32_t count; + /** array of strings */ char **values; } strings; - /* rules */ + /** rules */ struct { + /** allocated count for rules */ uint32_t alloc; + /** used count for rules */ uint32_t count; + /** array of rules */ struct rule *values; } rules; + /** transaction */ struct { + /** rule count at the beginning of the transaction */ uint32_t count; + /** indicator for an active transaction */ bool active; } transaction; }; typedef struct memdb memdb_t; +/** implementation of anydb_itf.index */ static int index_itf( @@ -99,13 +129,14 @@ index_itf( if (s == NULL) return -ENOMEM; if (memdb->strings.count == memdb->strings.alloc) { - strings = realloc(strings, (memdb->strings.alloc + SBS) * sizeof *strings); + strings = realloc(strings, (memdb->strings.alloc + + STRING_BLOC_SIZE) * sizeof *strings); if (!strings) { free(s); return -ENOMEM; } memdb->strings.values = strings; - memdb->strings.alloc += SBS; + memdb->strings.alloc += STRING_BLOC_SIZE; } i = memdb->strings.count; *idx = i; @@ -114,6 +145,7 @@ index_itf( return 0; } +/** implementation of anydb_itf.string */ static const char * string_itf( @@ -122,14 +154,16 @@ string_itf( ) { memdb_t *memdb = clodb; + assert(idx < memdb->strings.count); return memdb->strings.values[idx]; } +/** implementation of anydb_itf.apply */ static void apply_itf( void *clodb, - anydb_action_t (*oper)(void *closure, const anydb_key_t *key, anydb_value_t *value), + anydb_applycb_t *oper, void *closure ) { memdb_t *memdb = clodb; @@ -139,32 +173,29 @@ apply_itf( ir = 0; while (ir < memdb->rules.count) { - if (memdb->transaction.active && rules[ir].tag == TDEL) - a = Anydb_Action_Continue; - else - a = oper(closure, &rules[ir].key, &rules[ir].value); - switch (a) { - case Anydb_Action_Stop: - return; - case Anydb_Action_Continue: + if (memdb->transaction.active && rules[ir].tag == TAG_DELETED) ir++; - break; - case Anydb_Action_Update_And_Stop: - if (memdb->transaction.active) - rules[ir].tag = TMOD; - else - rules[ir].saved = rules[ir].value; - return; - case Anydb_Action_Remove_And_Continue: - if (memdb->transaction.active) - rules[ir++].tag = TDEL; - else - rules[ir] = rules[--memdb->rules.count]; - break; + else { + a = oper(closure, &rules[ir].key, &rules[ir].value); + if (a & Anydb_Action_Remove) { + if (memdb->transaction.active) + rules[ir++].tag = TAG_DELETED; + else + rules[ir] = rules[--memdb->rules.count]; + } else if (a & Anydb_Action_Update) { + if (memdb->transaction.active) + rules[ir].tag = TAG_CHANGED; + else + rules[ir].saved = rules[ir].value; + } + if (a & Anydb_Action_Stop) + return; + ir += !(a & Anydb_Action_Remove); } } } +/** implementation of anydb_itf.transaction */ static int transaction_itf( @@ -191,14 +222,14 @@ transaction_itf( ir = 0; while(ir < count) { switch (rules[ir].tag) { - case TCLE: + case TAG_CLEAN: ir++; break; - case TDEL: + case TAG_DELETED: rules[ir] = rules[--count]; break; - case TMOD: - rules[ir++].tag = TCLE; + case TAG_CHANGED: + rules[ir++].tag = TAG_CLEAN; break; } } @@ -211,9 +242,9 @@ transaction_itf( rules = memdb->rules.values; count = memdb->rules.count = memdb->transaction.count; for (ir = 0 ; ir < count ; ir++) { - if (rules[ir].tag != TCLE) { + if (rules[ir].tag != TAG_CLEAN) { rules[ir].value = rules[ir].saved; - rules[ir].tag = TCLE; + rules[ir].tag = TAG_CLEAN; } } memdb->transaction.active = false; @@ -222,6 +253,7 @@ transaction_itf( return 0; } +/** implementation of anydb_itf.add */ static int add_itf( @@ -238,7 +270,7 @@ add_itf( count = memdb->rules.count; alloc = memdb->rules.alloc; if (count == alloc) { - alloc += RBS; + alloc += RULE_BLOC_SIZE; rules = realloc(rules, alloc * sizeof *rules); if (!rules) return -ENOMEM; @@ -248,11 +280,16 @@ add_itf( rules = &rules[count]; rules->key = *key; rules->saved = rules->value = *value; - rules->tag = TCLE; + rules->tag = TAG_CLEAN; memdb->rules.count = count + 1; return 0; } +/** + * Mark the 'item' as being used + * @param renum array handling marked items + * @param item the item to check + */ static void gc_mark( @@ -263,32 +300,38 @@ gc_mark( renum[item] = 1; } +/** + * return the renumring of 'item' within 'renum' + * @param renum the renumbering array + * @param item the item to renumber + * @return the renumbered item + */ static anydb_idx_t -gc_new( +gc_renum( anydb_idx_t *renum, anydb_idx_t item ) { return item > AnyIdx_Max ? item : renum[item]; } -#include + +/** implementation of anydb_itf.gc */ static void gc_itf( void *clodb ) { memdb_t *memdb = clodb; - uint32_t nr = memdb->rules.count; - uint32_t ns = memdb->strings.count; + uint32_t i, j; + uint32_t rule_count = memdb->rules.count; + uint32_t name_count = memdb->strings.count; char **strings = memdb->strings.values; struct rule *rules = memdb->rules.values; - anydb_idx_t *renum = alloca(ns * sizeof *renum); - uint32_t i, j; - - for (i = 0 ; i < ns ; i++) - renum[i] = 0; + anydb_idx_t *renum = alloca(name_count * sizeof *renum); - for (i = 0 ; i < nr ; i++) { + /* mark used strings */ + memset(renum, 0, name_count * sizeof *renum); + for (i = 0 ; i < rule_count ; i++) { gc_mark(renum, rules[i].key.client); gc_mark(renum, rules[i].key.session); gc_mark(renum, rules[i].key.user); @@ -296,7 +339,8 @@ gc_itf( gc_mark(renum, rules[i].value.value); } - for (i = j = 0 ; i < ns ; i++) { + /* pack the used strings */ + for (i = j = 0 ; i < name_count ; i++) { if (renum[i]) { strings[j] = strings[i]; renum[i] = j++; @@ -305,34 +349,38 @@ gc_itf( renum[i] = AnyIdx_Invalid; } } - if (ns != j) { - memdb->strings.count = ns = j; - for (i = 0 ; i < nr ; i++) { - rules[i].key.client = gc_new(renum, rules[i].key.client); - rules[i].key.session = gc_new(renum, rules[i].key.session); - rules[i].key.user = gc_new(renum, rules[i].key.user); - rules[i].key.permission = gc_new(renum, rules[i].key.permission); - rules[i].value.value = gc_new(renum, rules[i].value.value); + if (name_count != j) { + /* renumber the items of the database */ + memdb->strings.count = name_count = j; + for (i = 0 ; i < rule_count ; i++) { + rules[i].key.client = gc_renum(renum, rules[i].key.client); + rules[i].key.session = gc_renum(renum, rules[i].key.session); + rules[i].key.user = gc_renum(renum, rules[i].key.user); + rules[i].key.permission = gc_renum(renum, rules[i].key.permission); + rules[i].value.value = gc_renum(renum, rules[i].value.value); } } + /* decrease size of array for strings */ i = memdb->strings.alloc; - while (ns + SBS < i) - i -= SBS; + while (name_count + STRING_BLOC_SIZE < i) + i -= STRING_BLOC_SIZE; if (i != memdb->strings.alloc) { memdb->strings.alloc = i; memdb->strings.values = realloc(strings, i * sizeof *strings); } + /* decrease size of array for rules */ i = memdb->rules.alloc; - while (nr + RBS < i) - i -= RBS; + while (rule_count + RULE_BLOC_SIZE < i) + i -= RULE_BLOC_SIZE; if (i != memdb->rules.alloc) { memdb->rules.alloc = i; - memdb->rules.values = realloc(rules, i * sizeof *strings); + memdb->rules.values = realloc(rules, i * sizeof *rules); } } +/** implementation of anydb_itf.destroy */ static void destroy_itf( @@ -346,9 +394,13 @@ destroy_itf( } } +/** + * Initialize the structure of the memory database + * @param memdb the structure to initialize + */ static void -init( +init_memdb( memdb_t *memdb ) { memdb->db.clodb = memdb; @@ -374,18 +426,22 @@ init( memdb->transaction.active = false; } +/* see memdb.h */ int memdb_create( anydb_t **memdb ) { memdb_t *mdb; + /* allocate */ mdb = malloc(sizeof *mdb); if (!mdb) { *memdb = NULL; return -ENOMEM; } - init(mdb); + + /* init */ + init_memdb(mdb); *memdb = &mdb->db; return 0; } diff --git a/src/memdb.h b/src/memdb.h index 0f1c9e7..a947296 100644 --- a/src/memdb.h +++ b/src/memdb.h @@ -14,9 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF IN MEMORY DATABASE WITHOUT FILE BACKEND */ +/******************************************************************************/ +/******************************************************************************/ +/** + * Create the object handling the memory database + * @param memdb pointer to the handling object to return + */ extern int memdb_create( diff --git a/src/pollitem.c b/src/pollitem.c index d183594..22d2505 100644 --- a/src/pollitem.c +++ b/src/pollitem.c @@ -14,27 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF EPOLL HELPER */ +/******************************************************************************/ +/******************************************************************************/ #include #include -/* -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -*/ - #include "pollitem.h" +/** + * Wraps the call to epoll_ctl for operation 'op' + * + * @param pollitem the pollitem to process + * @param events the expected events + * @param pollfd the file descriptor for epoll + * @param op the operation to perform + * @return 0 on success or -1 with errno set accordingly to epoll_ctl + */ static int pollitem_do( @@ -47,6 +46,7 @@ pollitem_do( return epoll_ctl(pollfd, op, pollitem->fd, &ev); } +/* see pollitem.h */ int pollitem_add( pollitem_t *pollitem, @@ -56,6 +56,7 @@ pollitem_add( return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_ADD); } +/* see pollitem.h */ int pollitem_mod( pollitem_t *pollitem, @@ -65,6 +66,7 @@ pollitem_mod( return pollitem_do(pollitem, events, pollfd, EPOLL_CTL_MOD); } +/* see pollitem.h */ int pollitem_del( pollitem_t *pollitem, @@ -73,6 +75,7 @@ pollitem_del( return pollitem_do(pollitem, 0, pollfd, EPOLL_CTL_DEL); } +/* see pollitem.h */ int pollitem_wait_dispatch( int pollfd, diff --git a/src/pollitem.h b/src/pollitem.h index a60b7b8..375c674 100644 --- a/src/pollitem.h +++ b/src/pollitem.h @@ -14,22 +14,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF EPOLL HELPER */ +/******************************************************************************/ +/******************************************************************************/ /** structure for using epoll easily */ typedef struct pollitem pollitem_t; +/** + * Structure for using epoll easily + */ struct pollitem { /** callback on event */ void (*handler)(pollitem_t *pollitem, uint32_t events, int pollfd); - /** data */ + /** data of any kind free to use */ void *closure; - /** file */ + /** file descriptor */ int fd; }; +/** + * Add a pollitem to epoll + * + * @param pollitem the pollitem to add + * @param events expected events + * @param pollfd file descriptor of the epoll + * @return 0 on success or -1 with errno set accordingly to epoll_ctl + */ extern int pollitem_add( @@ -38,6 +55,14 @@ pollitem_add( int pollfd ); +/** + * Modify a pollitem of epoll + * + * @param pollitem the pollitem to modify + * @param events expected events + * @param pollfd file descriptor of the epoll + * @return 0 on success or -1 with errno set accordingly to epoll_ctl + */ extern int pollitem_mod( @@ -46,6 +71,13 @@ pollitem_mod( int pollfd ); +/** + * Delete a pollitem from epoll + * + * @param pollitem the pollitem to delete + * @param pollfd file descriptor of the epoll + * @return 0 on success or -1 with errno set accordingly to epoll_ctl + */ extern int pollitem_del( @@ -53,6 +85,15 @@ pollitem_del( int pollfd ); +/** + * Wait one event on epoll and dispatch it to its pollitem callback + * + * @param pollfd file descriptor of the epoll + * @param timeout time to wait + * @return 0 on timeout + * 1 if a callback was called + * -1 with errno set accordingly to epoll_wait + */ extern int pollitem_wait_dispatch( diff --git a/src/prot.c b/src/prot.c index 06edb2f..7edcc50 100644 --- a/src/prot.c +++ b/src/prot.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF THE PROTOCOL */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -26,7 +31,15 @@ #include "prot.h" -/** the structure buf is generic the meaning of pos/count is not fixed */ +#define MAX_FIELDS 20 +#define MAX_BUFFER_LENGTH 2000 +#define FIELD_SEPARATOR ' ' +#define RECORD_SEPARATOR '\n' +#define ESCAPE '\\' + +/** + * the structure buf is generic the meaning of pos/count is not fixed + */ struct buf { /** a position */ @@ -38,7 +51,7 @@ struct buf /* TODO: add a 3rd unsigned for improving management of read and write */ /** a fixed size content */ - char content[MAXBUFLEN]; + char content[MAX_BUFFER_LENGTH]; }; typedef struct buf buf_t; @@ -49,11 +62,13 @@ struct fields int count; /** the fields as strings */ - const char *fields[MAXARGS]; + const char *fields[MAX_FIELDS]; }; typedef struct fields fields_t; -/** structure for handling the protocol */ +/** + * structure for handling the protocol + */ struct prot { /** input buf, pos is the scanning position */ @@ -65,14 +80,13 @@ struct prot /** count of pending output fields */ unsigned outfields; - /** cancel index value value */ + /** cancel index when putting values */ unsigned cancelidx; /** the fields */ fields_t fields; }; - /** * Put the 'car' into the 'buf' * returns: @@ -88,13 +102,13 @@ buf_put_car( unsigned pos; pos = buf->count; - if (pos >= MAXBUFLEN) + if (pos >= MAX_BUFFER_LENGTH) return -ECANCELED; buf->count = pos + 1; pos += buf->pos; - if (pos >= MAXBUFLEN) - pos -= MAXBUFLEN; + if (pos >= MAX_BUFFER_LENGTH) + pos -= MAX_BUFFER_LENGTH; buf->content[pos] = car; return 0; } @@ -116,30 +130,30 @@ buf_put_string( remain = buf->count; pos = buf->pos + remain; - if (pos >= MAXBUFLEN) - pos -= MAXBUFLEN; - remain = MAXBUFLEN - remain; + if (pos >= MAX_BUFFER_LENGTH) + pos -= MAX_BUFFER_LENGTH; + remain = MAX_BUFFER_LENGTH - remain; /* put all chars of the string */ while((c = *string++)) { /* escape special characters */ - if (c == FS || c == RS || c == ESC) { + if (c == FIELD_SEPARATOR || c == RECORD_SEPARATOR || c == ESCAPE) { if (!remain--) goto cancel; - buf->content[pos++] = ESC; - if (pos == MAXBUFLEN) + buf->content[pos++] = ESCAPE; + if (pos == MAX_BUFFER_LENGTH) pos = 0; } /* put the char */ if (!remain--) goto cancel; buf->content[pos++] = c; - if (pos == MAXBUFLEN) + if (pos == MAX_BUFFER_LENGTH) pos = 0; } /* record the new values */ - buf->count = MAXBUFLEN - remain; + buf->count = MAX_BUFFER_LENGTH - remain; return 0; cancel: @@ -169,11 +183,11 @@ buf_write( /* prepare the iovec */ vec[0].iov_base = buf->content + buf->pos; - if (buf->pos + count <= MAXBUFLEN) { + if (buf->pos + count <= MAX_BUFFER_LENGTH) { vec[0].iov_len = count; n = 1; } else { - vec[0].iov_len = MAXBUFLEN - buf->pos; + vec[0].iov_len = MAX_BUFFER_LENGTH - buf->pos; vec[1].iov_base = buf->content; vec[1].iov_len = count - vec[0].iov_len; n = 2; @@ -191,14 +205,16 @@ buf_write( /* update the state */ buf->count -= (unsigned)rc; buf->pos += (unsigned)rc; - if (buf->pos >= MAXBUFLEN) - buf->pos -= MAXBUFLEN; + if (buf->pos >= MAX_BUFFER_LENGTH) + buf->pos -= MAX_BUFFER_LENGTH; } return (int)rc; } -/* get the 'fields' from 'buf' */ +/** + * get the 'fields' from 'buf' + */ static void buf_get_fields( @@ -209,7 +225,7 @@ buf_get_fields( unsigned read, write; /* advance the pos after the end */ - assert(buf->content[buf->pos] == RS); + assert(buf->content[buf->pos] == RECORD_SEPARATOR); buf->pos++; /* init first field */ @@ -218,20 +234,20 @@ buf_get_fields( for (;;) { c = buf->content[read++]; switch(c) { - case FS: /* field separator */ + case FIELD_SEPARATOR: /* field separator */ buf->content[write++] = 0; - if (fields->count >= MAXARGS) + if (fields->count >= MAX_FIELDS) return; fields->fields[++fields->count] = &buf->content[write]; break; - case RS: /* end of line (record separator) */ + case RECORD_SEPARATOR: /* end of line (record separator) */ buf->content[write] = 0; fields->count += (write > 0); return; - case ESC: /* escaping */ + case ESCAPE: /* escaping */ c = buf->content[read++]; - if (c != FS && c != RS && c != ESC) - buf->content[write++] = ESC; + if (c != FIELD_SEPARATOR && c != RECORD_SEPARATOR && c != ESCAPE) + buf->content[write++] = ESCAPE; buf->content[write++] = c; break; default: /* other characters */ @@ -254,10 +270,10 @@ buf_scan_end_record( /* search the next RS */ while(buf->pos < buf->count) { - if (buf->content[buf->pos] == RS) { + if (buf->content[buf->pos] == RECORD_SEPARATOR) { /* check whether RS is escaped */ nesc = 0; - while (buf->pos > nesc && buf->content[buf->pos - (nesc + 1)] == ESC) + while (buf->pos > nesc && buf->content[buf->pos - (nesc + 1)] == ESCAPE) nesc++; if ((nesc & 1) == 0) return 1; /* not escaped */ @@ -267,7 +283,9 @@ buf_scan_end_record( return 0; } -/** remove chars of 'buf' until pos */ +/** + * remove chars of 'buf' until pos + */ static void buf_crop( @@ -279,7 +297,9 @@ buf_crop( buf->pos = 0; } -/** read input 'buf' from 'fd' */ +/** + * read input 'buf' from 'fd' + */ static int inbuf_read( @@ -289,11 +309,11 @@ inbuf_read( ssize_t szr; int rc; - if (buf->count == MAXBUFLEN) + if (buf->count == MAX_BUFFER_LENGTH) return -ENOBUFS; do { - szr = read(fd, buf->content + buf->count, MAXBUFLEN - buf->count); + szr = read(fd, buf->content + buf->count, MAX_BUFFER_LENGTH - buf->count); } while(szr < 0 && errno == EINTR); if (szr >= 0) buf->count += (unsigned)(rc = (int)szr); @@ -303,10 +323,7 @@ inbuf_read( return rc; } -/** - * create the prot structure in 'prot' - * Return 0 in case of success or -ENOMEM in case of error - */ +/* see prot.h */ int prot_create( prot_t **prot @@ -325,9 +342,7 @@ prot_create( return 0; } -/** - * Destroys the protocol 'prot' - */ +/* see prot.h */ void prot_destroy( prot_t *prot @@ -335,9 +350,7 @@ prot_destroy( free(prot); } -/** - * reset the protocol 'prot' - */ +/* see prot.h */ void prot_reset( prot_t *prot @@ -349,6 +362,7 @@ prot_reset( prot->fields.count = -1; } +/* see prot.h */ void prot_put_cancel( prot_t *prot @@ -357,11 +371,12 @@ prot_put_cancel( if (prot->outfields) { count = prot->cancelidx - prot->outbuf.pos; - prot->outbuf.count = count > MAXBUFLEN ? count - MAXBUFLEN : count; + prot->outbuf.count = count > MAX_BUFFER_LENGTH ? count - MAX_BUFFER_LENGTH : count; prot->outfields = 0; } } +/* see prot.h */ int prot_put_end( prot_t *prot @@ -371,12 +386,13 @@ prot_put_end( if (!prot->outfields) rc = 0; else { - rc = buf_put_car(&prot->outbuf, RS); + rc = buf_put_car(&prot->outbuf, RECORD_SEPARATOR); prot->outfields = 0; } return rc; } +/* see prot.h */ int prot_put_field( prot_t *prot, @@ -385,7 +401,7 @@ prot_put_field( int rc; if (prot->outfields++) - rc = buf_put_car(&prot->outbuf, FS); + rc = buf_put_car(&prot->outbuf, FIELD_SEPARATOR); else { prot->cancelidx = prot->outbuf.pos + prot->outbuf.count; rc = 0; @@ -396,6 +412,7 @@ prot_put_field( return rc; } +/* see prot.h */ int prot_put_fields( prot_t *prot, @@ -413,24 +430,16 @@ prot_put_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 enough space in the buffer - */ +/* see prot.h */ int prot_put( prot_t *prot, unsigned count, const char **fields ) { - int rc = 0; - unsigned index = 0; + int rc; - while(!rc && index < count) - rc = prot_put_field(prot, fields[index++]); + rc = prot_put_fields(prot, count, fields); if (rc) prot_put_cancel(prot); else @@ -445,6 +454,7 @@ prot_put( * - -EINVAL if the count of fields is too big * - -ECANCELED if there is not enought space in the buffer */ +/* see prot.h */ int prot_putx( prot_t *prot, @@ -467,10 +477,7 @@ prot_putx( return rc; } -/** - * Check whether write should be done or not - * Returns 1 if there is something to write or 0 otherwise - */ +/* see prot.h */ int prot_should_write( prot_t *prot @@ -478,13 +485,7 @@ prot_should_write( return prot->outbuf.count > 0; } -/** - * Write the content to write and return either the count - * of bytes written or an error code (negative). Note that - * the returned value tries to be the same as those returned - * by "man 2 write". The only exception is -ENODATA that is - * returned if there is nothing to be written. - */ +/* see prot.h */ int prot_write( prot_t *prot, @@ -493,13 +494,15 @@ prot_write( return buf_write(&prot->outbuf, fdout); } +/* see prot.h */ int prot_can_read( prot_t *prot ) { - return prot->inbuf.count < MAXBUFLEN; + return prot->inbuf.count < MAX_BUFFER_LENGTH; } +/* see prot.h */ int prot_read( prot_t *prot, @@ -508,6 +511,7 @@ prot_read( return inbuf_read(&prot->inbuf, fdin); } +/* see prot.h */ int prot_get( prot_t *prot, @@ -523,6 +527,7 @@ prot_get( return (int)prot->fields.count; } +/* see prot.h */ void prot_next( prot_t *prot diff --git a/src/prot.h b/src/prot.h index b961a37..a65c4b0 100644 --- a/src/prot.h +++ b/src/prot.h @@ -14,49 +14,84 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF THE PROTOCOL */ +/******************************************************************************/ +/******************************************************************************/ -struct prot; typedef struct prot prot_t; -#define MAXBUFLEN 2000 -#define MAXARGS 20 -#define FS ' ' -#define RS '\n' -#define ESC '\\' - - +/** + * Create the prot handler in 'prot' + * + * @param prot where to store the protocol handler + * @return 0 in case of success or -ENOMEM in case of error + */ extern int prot_create( prot_t **prot ); +/** + * Destroys the protocol handler 'prot' + * + * @param prot the protocol handler + */ +/** + * + */ extern void prot_destroy( prot_t *prot ); +/** + * Reset the protocol handler 'prot' + * + * @param prot the protocol handler + */ extern void prot_reset( prot_t *prot ); +/** + * Cancel any previous put not terminated with prot_put_end + * + * @param prot the protocol handler + */ extern void prot_put_cancel( prot_t *prot ); +/** + * Terminate a protocol record + * + * @param prot the protocol handler + * @return 0 on success + * -ECANCELED if the send buffer is full + */ extern int prot_put_end( prot_t *prot ); +/** + * Add a field to a protocol record + * + * @param prot the protocol handler + * @param field the field to add + * @return 0 on success + * -ECANCELED if the send buffer is full + */ extern int prot_put_field( @@ -64,6 +99,15 @@ prot_put_field( const char *field ); +/** + * Add a set of fields to a protocol record + * + * @param prot the protocol handler + * @param count count of fields + * @param fields array of the fields to add + * @return 0 on success + * -ECANCELED if the send buffer is full + */ extern int prot_put_fields( @@ -72,6 +116,15 @@ prot_put_fields( const char **fields ); +/** + * Add a set of fields to the record of protocol and terminate it + * + * @param prot the protocol handler + * @param count count of fields + * @param fields array of the fields to add + * @return 0 on success + * -ECANCELED if the send buffer is full + */ extern int prot_put( @@ -80,6 +133,14 @@ prot_put( const char **fields ); +/** + * Add a variable length of items in protocol and terminate it + * + * @param prot the protocol handler + * @param ... a NULL terminated list of strings + * @return 0 on success + * -ECANCELED if the send buffer is full + */ extern int prot_putx( @@ -87,12 +148,29 @@ prot_putx( ... ); +/** + * Check whether write should be done or not + * + * @param prot the protocol handler + * @return 1 if there is something to write or 0 otherwise + */ extern int prot_should_write( prot_t *prot ); +/** + * Write the content to write and return either the count + * of bytes written or an error code (negative). Note that + * the returned value tries to be the same as those returned + * by "man 2 write". The only exception is -ENODATA that is + * returned if there is nothing to be written. + * + * @param prot the protocol handler + * @param fdout the file to write + * @return the count of bytes written or a negative -errno error code + */ extern int prot_write( @@ -100,12 +178,25 @@ prot_write( int fdout ); +/** + * Is there space to receive data + * + * @param prot the protocol handler + * @return 0 if there is no space or 1 if read can be called + */ extern int prot_can_read( prot_t *prot ); +/** + * Read data from the input file fdin + * + * @param prot the protocol handler + * @param fdin the file to read + * @return the count of bytes read or a negative -errno error code + */ extern int prot_read( @@ -113,6 +204,13 @@ prot_read( int fdin ); +/** + * Get the currently received fields and its count + * + * @param prot the protocol handler + * @param fields where to store the array of received fields (can be NULL) + * @return the count of fields or -EAGAIN if no field is available + */ extern int prot_get( @@ -120,6 +218,12 @@ prot_get( const char ***fields ); +/** + * Forgive the current received fields so that the next call to prot_get + * returns the next received fields + * + * @param prot the protocol handler + */ extern void prot_next( diff --git a/src/queue.c b/src/queue.c index b02d7da..9d847da 100644 --- a/src/queue.c +++ b/src/queue.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF QUEUE OF DATABASE MODIFIERS */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -32,7 +37,16 @@ */ struct queue { - uint32_t read, write, capacity; + /** read index within queue */ + uint32_t read; + + /** write index within queue */ + uint32_t write; + + /** capacity of the queue */ + uint32_t capacity; + + /** the memory of the queue */ void *queue; }; typedef struct queue queue_t; @@ -40,6 +54,13 @@ typedef struct queue queue_t; /** the queue */ static queue_t queue; +/** + * Read data from the queue + * + * @param data where to store the data + * @param length length of the data to read + * @return true on success or false on error + */ static bool qread( @@ -54,6 +75,12 @@ qread( return true; } +/** + * Get a time from the queue + * + * @param value where to store the time + * @return true on success or false on error + */ static bool qget_time( @@ -62,6 +89,12 @@ qget_time( return qread(value, sizeof *value); } +/** + * Get a string from the queue + * + * @param text where to store the string + * @return true on success or false on error + */ static bool qget_string( @@ -76,6 +109,13 @@ qget_string( return true; } +/** + * Write the data to the queue + * + * @param data data to write + * @param length length of the data + * @return true on success or false on error + */ static bool qwrite( @@ -100,6 +140,12 @@ qwrite( return true; } +/** + * Put the time in the queue + * + * @param value the value to put + * @return true on success or false on error + */ static bool qput_time( @@ -108,6 +154,13 @@ qput_time( return qwrite(&value, sizeof value); } +/** + * Put the text in the queue + * + * @param text the text to put + * + * @return true on success or false on error + */ static bool qput_string( @@ -115,13 +168,16 @@ qput_string( ) { size_t len; text = text ?: ""; + /* check length */ len = strnlen(text, MAX_NAME_LENGTH + 1); if (len > MAX_NAME_LENGTH) return false; + return qwrite(text, 1 + (uint32_t)len); } +/* see queue.h */ int queue_drop( const data_key_t *key @@ -130,10 +186,11 @@ queue_drop( && qput_string(key->session) && qput_string(key->user) && qput_string(key->permission) - && qput_string(0) + && qput_string(NULL) ? 0 : -(errno = ENOMEM); } +/* see queue.h */ int queue_set( const data_key_t *key, @@ -148,13 +205,14 @@ queue_set( ? 0 : -(errno = ENOMEM); } - +/* see queue.h */ void queue_clear( ) { queue.write = 0; } +/* see queue.h */ int queue_play( ) { diff --git a/src/queue.h b/src/queue.h index 7efc1f8..99aa9e2 100644 --- a/src/queue.h +++ b/src/queue.h @@ -14,14 +14,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF QUEUE OF DATABASE MODIFIERS */ +/******************************************************************************/ +/******************************************************************************/ - +/** + * Queue droping the key + * + * @param key the key for dropping + * @return 0 on success or -ENOMEM on memory depletion + */ extern int queue_drop( const data_key_t *key ); +/** + * Queue setting of the key with the value + * + * @param key the key to set + * @param value the value to set to the key + * @return 0 on success or -ENOMEM on memory depletion + */ extern int queue_set( @@ -29,14 +47,21 @@ queue_set( const data_value_t *value ); +/** + * Clear the content of the queue + */ extern void queue_clear( ); +/** + * Play the content of the queue to alter the database accordingly to what + * is recorded + * + * @return 0 in case of success or a negative error code like -errno + */ extern int queue_play( ); - - diff --git a/src/rcyn-client.c b/src/rcyn-client.c index 2d9ca9b..e865888 100644 --- a/src/rcyn-client.c +++ b/src/rcyn-client.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF CLIENT PART OF RCYN-PROTOCOL */ +/******************************************************************************/ +/******************************************************************************/ #include #include diff --git a/src/rcyn-client.h b/src/rcyn-client.h index f0fffe9..5128c85 100644 --- a/src/rcyn-client.h +++ b/src/rcyn-client.h @@ -14,9 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF CLIENT PART OF RCYN-PROTOCOL */ +/******************************************************************************/ +/******************************************************************************/ + typedef struct rcyn rcyn_t; typedef enum rcyn_type rcyn_type_t; diff --git a/src/rcyn-protocol.c b/src/rcyn-protocol.c index d570b04..09db418 100644 --- a/src/rcyn-protocol.c +++ b/src/rcyn-protocol.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF COMMON PROTOCOL VALUES, CONSTANTS, PROCESSES */ +/******************************************************************************/ +/******************************************************************************/ #include @@ -22,29 +27,26 @@ const char _agent_[] = "agent", _check_[] = "check", + _clear_[] = "clear", + _commit_[] = "commit", + _done_[] = "done", _drop_[] = "drop", _enter_[] = "enter", + _error_[] = "error", _get_[] = "get", + _item_[] = "item", _leave_[] = "leave", _log_[] = "log", - _rcyn_[] = "rcyn", - _set_[] = "set", - _test_[] = "test"; - -const char - _commit_[] = "commit", - _rollback_[] = "rollback"; - -const char - _clear_[] = "clear", - _done_[] = "done", - _error_[] = "error", - _item_[] = "item", _no_[] = "no", - _on_[] = "on", _off_[] = "off", + _on_[] = "on", + _rcyn_[] = "rcyn", + _rollback_[] = "rollback", + _set_[] = "set", + _test_[] = "test", _yes_[] = "yes"; + #if !defined(RCYN_DEFAULT_SOCKET_SCHEME) # define RCYN_DEFAULT_SOCKET_SCHEME "unix" #endif @@ -86,6 +88,7 @@ const char rcyn_default_admin_socket_spec[] = RCYN_DEFAULT_ADMIN_SOCKET_SPEC, rcyn_default_agent_socket_spec[] = RCYN_DEFAULT_AGENT_SOCKET_SPEC; +/* see rcyn-protocol.h */ const char * rcyn_get_socket_check( const char *value @@ -95,6 +98,7 @@ rcyn_get_socket_check( ?: rcyn_default_check_socket_spec; } +/* see rcyn-protocol.h */ const char * rcyn_get_socket_admin( const char *value @@ -104,6 +108,7 @@ rcyn_get_socket_admin( ?: rcyn_default_admin_socket_spec; } +/* see rcyn-protocol.h */ const char * rcyn_get_socket_agent( const char *value diff --git a/src/rcyn-protocol.h b/src/rcyn-protocol.h index 8640186..553a3df 100644 --- a/src/rcyn-protocol.h +++ b/src/rcyn-protocol.h @@ -14,35 +14,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF COMMON PROTOCOL VALUES, CONSTANTS, PROCESSES */ +/******************************************************************************/ +/******************************************************************************/ +/* predefined protocol strings */ extern const char _agent_[], _check_[], + _clear_[], + _commit_[], + _done_[], _drop_[], _enter_[], + _error_[], _get_[], + _item_[], _leave_[], _log_[], - _rcyn_[], - _set_[], - _test_[]; - -extern const char - _commit_[], - _rollback_[]; - -extern const char - _clear_[], - _done_[], - _error_[], - _item_[], _no_[], _off_[], _on_[], + _rcyn_[], + _rollback_[], + _set_[], + _test_[], _yes_[]; +/* predefined names */ extern const char rcyn_default_socket_scheme[], rcyn_default_socket_dir[], @@ -53,18 +55,36 @@ extern const char rcyn_default_admin_socket_spec[], rcyn_default_agent_socket_spec[]; +/** + * Get the socket specification for check usage + * + * @param value some value or NULL for getting default + * @return the socket specification for check usage + */ extern const char * rcyn_get_socket_check( const char *value ); +/** + * Get the socket specification for admin usage + * + * @param value some value or NULL for getting default + * @return the socket specification for admin usage + */ extern const char * rcyn_get_socket_admin( const char *value ); +/** + * Get the socket specification for agent usage + * + * @param value some value or NULL for getting default + * @return the socket specification for agent usage + */ extern const char * rcyn_get_socket_agent( diff --git a/src/rcyn-server.c b/src/rcyn-server.c index c6a0ef9..378a07d 100644 --- a/src/rcyn-server.c +++ b/src/rcyn-server.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF SERVER PART OF RCYN-PROTOCOL */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -39,6 +44,8 @@ #include "socket.h" #include "pollitem.h" +#define MAX_PUTX_ITEMS 15 + /** should log? */ bool rcyn_server_log = 0; @@ -177,7 +184,7 @@ putx( client_t *cli, ... ) { - const char *p, *fields[MAXARGS]; + const char *p, *fields[MAX_PUTX_ITEMS]; unsigned n; va_list l; int rc; @@ -187,7 +194,7 @@ putx( va_start(l, cli); p = va_arg(l, const char *); while (p) { - if (n == MAXARGS) + if (n == MAX_PUTX_ITEMS) return -EINVAL; fields[n++] = p; p = va_arg(l, const char *); @@ -421,7 +428,7 @@ onrequest( key.session = args[2]; key.user = args[3]; key.permission = args[4]; - cyn_list(cli, getcb, &key); + cyn_list(getcb, cli, &key); send_done(cli); return; } diff --git a/src/rcyn-server.h b/src/rcyn-server.h index 34cd46a..7982867 100644 --- a/src/rcyn-server.h +++ b/src/rcyn-server.h @@ -14,9 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF SERVER PART OF RCYN-PROTOCOL */ +/******************************************************************************/ +/******************************************************************************/ struct rcyn_server; typedef struct rcyn_server rcyn_server_t; diff --git a/src/socket.c b/src/socket.c index fc01b8f..bd6b804 100644 --- a/src/socket.c +++ b/src/socket.c @@ -14,6 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF SOCKET OPENING FOLLOWING SPECIFICATION */ +/******************************************************************************/ +/******************************************************************************/ #include #include @@ -33,7 +38,7 @@ #include "socket.h" -#define BACKLOG 30 +#define BACKLOG 8 /******************************************************************************/ diff --git a/src/socket.h b/src/socket.h index b5989e1..c0f9ddb 100644 --- a/src/socket.h +++ b/src/socket.h @@ -14,8 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF SOCKET OPENING FOLLOWING SPECIFICATION */ +/******************************************************************************/ +/******************************************************************************/ extern int socket_open(const char *uri, int server); -- cgit 1.2.3-korg