From 69de408d8a577191a1593c0cef2720fdd9c0018a Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Mon, 24 Jun 2013 11:35:40 +0200 Subject: [PATCH] Implementation of the users table and a generic hashtable mechanism --- core/depend.mk | 2 +- core/hashtable.c | 197 +++++++++++++++++++++++++++++++++++++++++++- core/service.c | 2 +- core/users.c | 67 ++++++++++++++- include/hashtable.h | 31 +++++-- include/users.h | 18 +++- 6 files changed, 302 insertions(+), 15 deletions(-) diff --git a/core/depend.mk b/core/depend.mk index e3c44166a..6c0955f3d 100644 --- a/core/depend.mk +++ b/core/depend.mk @@ -408,7 +408,7 @@ users.o: users.c /usr/include/stdlib.h /usr/include/features.h \ /usr/include/bits/sigset.h /usr/include/bits/time.h \ /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \ /usr/include/alloca.h /usr/include/string.h /usr/include/xlocale.h \ - ../include/users.h ../include/hashtable.h + ../include/users.h ../include/hashtable.h ../include/atomic.h hashtable.o: hashtable.c /usr/include/stdlib.h /usr/include/features.h \ /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \ diff --git a/core/hashtable.c b/core/hashtable.c index eca63f9ae..52b15a802 100644 --- a/core/hashtable.c +++ b/core/hashtable.c @@ -31,9 +31,22 @@ * @endverbatim */ +/** + * Special null function used as default memory allfunctions in the hashtable + * implementation. This avoids havign to special case the code that manipulates + * the keys and values + * + * @param data The data pointer + * @return Return the value we were called with + */ +static void * +nullfn(void *data) +{ + return data; +} /** - * Allocate a new users table + * Allocate a new hash table * * @param size The size of the hash table * @param hashfn The user supplied hash function @@ -50,6 +63,8 @@ HASHTABLE *rval; rval->hashsize = size; rval->hashfn = hashfn; rval->cmpfn = cmpfn; + rval->copyfn = nullfn; + rval->freefn = nullfn; if ((rval->entries = calloc(size, sizeof(HASHENTRIES))) == NULL) { free(rval); @@ -60,3 +75,183 @@ HASHTABLE *rval; return rval; } +/** + * Delete an entire hash table + * + * @param table The hash table to delete + */ +void +hashtable_free(HASHTABLE *table) +{ +int i; +HASHENTRIES *entry, *ptr; + + for (i = 0; i < table->hashsize; i++) + { + entry = table->entries[i]; + if (entry->key) + { + table->freefn(entry->key); + table->freefn(entry->value); + } + entry = entry->next; + while (entry) + { + ptr = entry->next; + table->freefn(entry->key); + table->freefn(entry->value); + free(entry); + entry = ptr; + } + } + free(table->entries); + free(table); +} + +/** + * Provide memory management functions to the hash table. This allows + * function pointers to be registered that can make copies of the + * key and value. + * + * @param table The hash table + * @param copyfn The copy function + * @param freefn The free function + */ +void +hashtable_memory_fns(HASHTABLE *table, HASHMEMORYFN copyfn, HASHMEMORYFN freefn) +{ + table->copyfn = copyfn; + table->freefn = freefn; +} + +/** + * Add an item to the hash table. + * + * @param table The hash table to which to add the item + * @param key The key of the item + * @param value The value for the item + * @return Return the number of items added + */ +int +hashtable_add(HASHTABLE *table, void *key, void *value) +{ +int hashkey = table->hashfn(key); +HASHENTRIES *entry; + + entry = table->entries[hashkey % table->hashsize]; + while (entry->next && entry->key && table->cmpfn(key, entry->key) != 0) + { + entry = entry->next; + } + if (entry->key == NULL) + { + /* Entry is empty - special case for first insert */ + entry->key = table->copyfn(key); + entry->value = table->copyfn(value); + } + else if (table->cmpfn(key, entry->key) == 0) + { + /* Duplicate key value */ + return 0; + } + else + { + HASHENTRIES *ptr = (HASHENTRIES *)malloc(sizeof(HASHENTRIES)); + if (ptr == NULL) + return 0; + ptr->key = table->copyfn(key); + ptr->value = table->copyfn(value); + ptr->next = NULL; + entry->next = ptr; + } + return 1; +} + +/** + * Delete an item from the hash table that has a given key + * + * @param table The hash table to delete from + * @param key The key value of the item to remove + * @return Return the number of items deleted + */ +int +hashtable_delete(HASHTABLE *table, void *key) +{ +int hashkey = table->hashfn(key); +HASHENTRIES *entry, *ptr; + + entry = table->entries[hashkey % table->hashsize]; + while (entry && entry->key && table->cmpfn(key, entry->key) != 0) + { + entry = entry->next; + } + if (entry == NULL || entry->key == NULL) + { + /* Not found */ + return 0; + } + + if (entry == table->entries[hashkey % table->hashsize]) + { + /* We are removing from the special first entry */ + if (entry->next) + { + table->freefn(entry->key); + table->freefn(entry->value); + entry->key = entry->next->key; + entry->value = entry->next->value; + ptr = entry->next; + entry->next = ptr->next; + free(ptr); + } + else + { + table->freefn(entry->key); + table->freefn(entry->value); + entry->key = NULL; + entry->value = NULL; + } + } + else + { + ptr = table->entries[hashkey % table->hashsize]; + while (ptr && ptr->next != entry) + ptr = ptr->next; + if (ptr == NULL) + return 0; /* This should never happen */ + ptr->next = entry->next; + table->freefn(entry->key); + table->freefn(entry->value); + free(entry); + } + return 1; +} + +/** + * Fetch an item with a given key value from the hash table + * + * @param table The hash table + * @param key The key value + * @return The item or NULL if the item was not found + */ +void * +hashtable_fetch(HASHTABLE *table, void *key) +{ +int hashkey = table->hashfn(key); +HASHENTRIES *entry; + + entry = table->entries[hashkey % table->hashsize]; + while (entry && entry->key && table->cmpfn(key, entry->key) != 0) + { + entry = entry->next; + } + if (entry == NULL || entry->key == NULL) + { + return NULL; + } + else + { + return entry->value; + } +} + diff --git a/core/service.c b/core/service.c index 4492c5d50..93352b5e6 100644 --- a/core/service.c +++ b/core/service.c @@ -70,7 +70,7 @@ SERVICE *service; service->state = SERVICE_STATE_ALLOC; service->credentials.name = NULL; service->credentials.authdata = NULL; - service->users = NULL; + service->users = users_alloc(); spinlock_acquire(&service_spin); service->next = allServices; diff --git a/core/users.c b/core/users.c index d341b81eb..fa5f265e3 100644 --- a/core/users.c +++ b/core/users.c @@ -18,6 +18,7 @@ #include #include #include +#include /** * @file users.c User table maintenance routines @@ -40,7 +41,7 @@ static int user_hash(char *key) { - return (*key + *(key + 1)) % 52; + return (*key + *(key + 1)); } /** @@ -62,6 +63,70 @@ USERS *rval; return NULL; } + hashtable_memory_fns(rval->data, (HASHMEMORYFN)strdup, (HASHMEMORYFN)free); + return rval; } +/** + * Remove the users table + * + * @param users The users table to remove + */ +void +users_free(USERS *users) +{ + hashtable_free(users->data); + free(users); +} + +/** + * Add a new user to the user table. The user name must be unique + * + * @param users The users table + * @param user The user name + * @param auth The authentication data + * @return The number of users added to the table + */ +int +users_add(USERS *users, char *user, char *auth) +{ +int add; + + atomic_add(&users->stats.n_adds, 1); + add = hashtable_add(users->data, user, auth); + atomic_add(&users->stats.n_entries, add); + return add; +} + +/** + * Delete a user from the user table. + * + * @param users The users table + * @param user The user name + * @return The number of users deleted from the table + */ +int +users_delete(USERS *users, char *user) +{ +int del; + + atomic_add(&users->stats.n_deletes, 1); + del = hashtable_delete(users->data, user); + atomic_add(&users->stats.n_entries, del * -1); + return del; +} + +/** + * Fetch the authentication data for a particular user from the users table + * + * @param users The users table + * @param user The user name + * @return The authentication data or NULL on error + */ +char +*users_fetch(USERS *users, char *user) +{ + atomic_add(&users->stats.n_fetches, 1); + return hashtable_fetch(users->data, user); +} diff --git a/include/hashtable.h b/include/hashtable.h index 79da81c3c..2c04ff064 100644 --- a/include/hashtable.h +++ b/include/hashtable.h @@ -38,25 +38,38 @@ * The next pointer is the overflow chain for this hashentry. */ typedef struct hashentry { - void *key; /**< The value of the key or NULL if empty entry */ - void *value; /**< The value associated with key */ - struct hashentry *next; /**< The overflow chain */ + void *key; /**< The value of the key or NULL if empty entry */ + void *value; /**< The value associated with key */ + struct hashentry *next; /**< The overflow chain */ } HASHENTRIES; +/** + * The type definition for the memory allocation functions + */ +typedef void *(*HASHMEMORYFN)(void *); + /** * The general purpose hashtable struct. */ typedef struct hashtable { int hashsize; /**< The number of HASHENTRIES */ - HASHENTRIES *entries; /**< The entries themselves */ + HASHENTRIES **entries; /**< The entries themselves */ int (*hashfn)(void *); /**< The hash function */ int (*cmpfn)(void *, void *); /**< The key comparison function */ + HASHMEMORYFN copyfn; /**< Optional copy function */ + HASHMEMORYFN freefn; /**< Optional free function */ } HASHTABLE; -extern HASHTABLE *hashtable_alloc(int, int (*hashfn)(), - int (*cmpfn)()); /**< Allocate a hashtable */ +extern HASHTABLE *hashtable_alloc(int, int (*hashfn)(), int (*cmpfn)()); + /**< Allocate a hashtable */ +extern void hashtable_memory_fns(HASHTABLE *, HASHMEMORYFN, HASHMEMORYFN); + /**< Provide an interface to control key/value memory + * manipulation + */ extern void hashtable_free(HASHTABLE *); /**< Free a hashtable */ -extern int hashtable_add(HASHTABLE *, char *, void *); /**< Add an entry */ -extern int hashtable_delete(HASHTABLE *, char *); /**< Delete an entry table */ -extern void *hashtable_fetch(HASHTABLE *, char *); /**< Fetch the data for a given key */ +extern int hashtable_add(HASHTABLE *, void *, void *); /**< Add an entry */ +extern int hashtable_delete(HASHTABLE *, void *); + /**< Delete an entry table */ +extern void *hashtable_fetch(HASHTABLE *, void *); + /**< Fetch the data for a given key */ #endif diff --git a/include/users.h b/include/users.h index 67284ae94..4b9a4f86f 100644 --- a/include/users.h +++ b/include/users.h @@ -31,12 +31,26 @@ * @endverbatim */ +/** + * The users table statistics structure + */ +typedef struct { + int n_entries; /**< The number of entries */ + int n_adds; /**< The number of inserts */ + int n_deletes; /**< The number of deletes */ + int n_fetches; /**< The number of fetchs */ +} USERS_STATS; +/** + * The user table, this contains the username and authentication data required + * for the authentication implementation within the gateway. + */ typedef struct users { - HASHTABLE *data; + HASHTABLE *data; /**< The hashtable containing the actual data */ + USERS_STATS stats; /**< The statistics for the users table */ } USERS; extern USERS *users_alloc(); /**< Allocate a users table */ -extern users_free(USERS *); /**< Free a users table */ +extern void users_free(USERS *); /**< Free a users table */ extern int users_add(USERS *, char *, char *); /**< Add a user to the users table */ extern int users_delete(USERS *, char *); /**< Delete a user from the users table */ extern char *users_fetch(USERS *, char *); /**< Fetch the authentication data for a user */