From 4df9822196d53a1aaeba3ec0709c86389133bd5a Mon Sep 17 00:00:00 2001 From: Robin Hack Date: Tue, 10 Sep 2013 15:24:55 +0200 Subject: Account: Added associative thread locking. --- src/account/CMakeLists.txt | 1 + src/account/LMI_AccountProvider.c | 27 ++++++ src/account/lock.c | 187 ++++++++++++++++++++++++++++++++++++++ src/account/lock.h | 27 ++++++ 4 files changed, 242 insertions(+) create mode 100644 src/account/lock.c create mode 100644 src/account/lock.h (limited to 'src/account') diff --git a/src/account/CMakeLists.txt b/src/account/CMakeLists.txt index 495d7c6..a993094 100644 --- a/src/account/CMakeLists.txt +++ b/src/account/CMakeLists.txt @@ -8,6 +8,7 @@ set(provider_SRCS aux_lu.c account_globals.c indication_common.c + lock.c ) if (NOT DEFINED CRYPT_ALGS) diff --git a/src/account/LMI_AccountProvider.c b/src/account/LMI_AccountProvider.c index c7c16bb..5805f5e 100644 --- a/src/account/LMI_AccountProvider.c +++ b/src/account/LMI_AccountProvider.c @@ -43,6 +43,7 @@ #include "aux_lu.h" #include "macros.h" #include "globals.h" +#include "lock.h" // Return values of functions // Delete user @@ -55,6 +56,9 @@ static const CMPIBroker* _cb = NULL; static void LMI_AccountInitialize() { + if (init_lock_pool() == 0) { + CMReturn(CMPI_RC_ERR_FAILED); + } } static CMPIStatus LMI_AccountCleanup( @@ -62,6 +66,7 @@ static CMPIStatus LMI_AccountCleanup( const CMPIContext* cc, CMPIBoolean term) { + destroy_lock_pool(); CMReturn(CMPI_RC_OK); } @@ -270,9 +275,18 @@ static CMPIStatus LMI_AccountModifyInstance( LMI_Account_InitFromObjectPath(&la, _cb, cop); + /* boilerplate code */ + char userlock[USERNAME_LEN_MAX] = {0}; + /* -1 for NULL char */ + strncpy(userlock, la.Name.chars, sizeof(userlock) - 1); + if (get_lock(userlock) == 0) { + KReturn2(_cb, ERR_FAILED, "Unable to obtain lock."); + } + luc = lu_start(NULL, lu_user, NULL, NULL, lu_prompt_console_quiet, NULL, &error); if (!luc) { + release_lock (userlock); KReturn2(_cb, ERR_FAILED, "Unable to initialize libuser: %s\n", lu_strerror(error)); } @@ -362,6 +376,7 @@ static CMPIStatus LMI_AccountModifyInstance( last_change = aux_lu_get_long(lue, LU_SHADOWLASTCHANGE); #define FAIL(msg) \ + release_lock (userlock); \ lu_end(luc); \ lu_ent_free(lue); \ KReturn2(_cb, ERR_FAILED, msg); @@ -452,6 +467,7 @@ static CMPIStatus LMI_AccountModifyInstance( } fail: + release_lock (userlock); lu_ent_free(lue); lu_end(luc); @@ -488,8 +504,18 @@ static CMPIrc delete_user( struct lu_ent *lue = NULL; struct lu_ent *lueg = NULL; + /* boilerplate code */ + char userlock[USERNAME_LEN_MAX] = {0}; + /* -1 for NULL char */ + strncpy(userlock, username, sizeof(userlock) - 1); + if (get_lock(userlock) == 0) { + asprintf(&errormsg, "Unable to obtain lock."); + return CMPI_RC_ERR_FAILED; + } + luc = lu_start(NULL, 0, NULL, NULL, lu_prompt_console_quiet, NULL, &error); if (!luc) { + release_lock(userlock); asprintf(&errormsg, "Unable to initialize libuser: %s\n", lu_strerror(error)); return CMPI_RC_ERR_FAILED; } @@ -568,6 +594,7 @@ static CMPIrc delete_user( } clean: + release_lock(userlock); lu_ent_free(lue); lu_ent_free(lueg); lu_end(luc); diff --git a/src/account/lock.c b/src/account/lock.c new file mode 100644 index 0000000..cc51190 --- /dev/null +++ b/src/account/lock.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include + +#include "lock.h" + +#define LOCK_CSEC() do { pthread_mutex_lock (&pool->csec); } while (0) +#define UNLOCK_CSEC() do { pthread_mutex_unlock (&pool->csec); } while (0) +#define RETURN_UNLOCK_CSEC(code) do { UNLOCK_CSEC(); return (code); } while (0) + +static lock_pool_t *pool = NULL; + +static pthread_once_t pool_is_initialized = PTHREAD_ONCE_INIT; +static unsigned int ref_count = 0; + +/* Callbacks */ +static void new_pool (void); +static void free_lock (gpointer lock); +/**/ + +/* Internal functions */ +static int search_key (const char *const username, lock_t **lck) __attribute__((nonnull)); +static void free_lock (gpointer lock) __attribute__((nonnull)); +static int add_key (const char *const username); + +int init_lock_pool (void) +{ + pthread_once (&pool_is_initialized, new_pool); + if ( pool == NULL ) { return 0; } + + LOCK_CSEC (); + + ++ref_count; + if (ref_count >= UINT_MAX) { RETURN_UNLOCK_CSEC (0); } + + UNLOCK_CSEC (); + + return 1; +} + +static void new_pool (void) +{ + pool = calloc (1, sizeof (lock_pool_t)); + if ( pool == NULL ) { + return; + } + + pool->hash_table = g_hash_table_new_full (&g_str_hash, &g_str_equal, NULL, &free_lock); + if ( pool->hash_table == NULL ) { + free (pool); + pool = NULL; + return; + } + + pthread_mutex_init (&pool->csec, NULL); +} + +static void free_lock (gpointer lock) +{ + lock_t *const lck = (lock_t *) lock; + + pthread_mutex_unlock (&lck->mutex); + pthread_mutex_destroy (&lck->mutex); + + free (lock); +} + +void destroy_lock_pool (void) +{ + assert (pool != NULL); + + LOCK_CSEC (); + + --ref_count; + + if ( ref_count > 0 ) { UNLOCK_CSEC(); return; } + + assert (pool->hash_table != NULL); + /* Remove all pairs. Memory is deallocated by free_lock callback. */ + g_hash_table_destroy (pool->hash_table); + + UNLOCK_CSEC (); + + pthread_mutex_destroy (&pool->csec); + free ((void *) pool); + + /* Initializae lock pool. */ + pool = NULL; + pool_is_initialized = PTHREAD_ONCE_INIT; +} + +/* search for keys */ +static int search_key (const char *const username, lock_t **lck) +{ + assert (pool != NULL); + + *lck = (lock_t *) g_hash_table_lookup (pool->hash_table, username); + + if ( *lck != NULL ) { + return 1; + } + + return 0; +} + +static int add_key (const char *const username) +{ + assert (pool != NULL); + + lock_t *const new_lock = calloc (1, sizeof (lock_t)); + if ( new_lock == NULL ) { + return 0; + } + + pthread_mutex_init (&new_lock->mutex, NULL); + pthread_mutex_lock (&new_lock->mutex); + new_lock->instances = 1; + + /* -1 for null char */ + strncpy (new_lock->id, username, sizeof (new_lock->id) - 1); + g_hash_table_insert (pool->hash_table, (gpointer) new_lock->id, new_lock); + return 1; +} + +int get_lock (const char *const username) +{ + assert (pool != NULL); + LOCK_CSEC (); + /* + * Global critical section + */ + + lock_t *lck = NULL; + int ret = search_key (username, &lck); + if ( ret == 1 ) { + assert (lck != NULL); + + pthread_mutex_t *const mutex = &lck->mutex; + if ( lck->instances >= UINT_MAX ) { + RETURN_UNLOCK_CSEC (0); + } + ++lck->instances; + + /* Unlock global critical section */ + UNLOCK_CSEC (); + + /* lock */ + pthread_mutex_lock (mutex); + return 1; + } + /* no keys found - add new key to list */ + ret = add_key (username); + RETURN_UNLOCK_CSEC (ret); +} + +int release_lock (const char *const username) +{ + /* Critical section */ + assert (pool != NULL); + LOCK_CSEC (); + + lock_t *lck = NULL; + int ret = search_key (username, &lck); + + /* Also exit of critical section */ + if ( ret == 0 ) { RETURN_UNLOCK_CSEC (ret); } + + assert (lck != NULL); + + --lck->instances; + if ( lck->instances > 0 ) { + pthread_mutex_unlock (&lck->mutex); + RETURN_UNLOCK_CSEC (ret); + } + + g_hash_table_remove (pool->hash_table, (gpointer) username); + + RETURN_UNLOCK_CSEC (ret); +} + +#undef PREALOCATED_LOCKS_NUM +#undef LOCK_CSEC +#undef UNLOCK_CSEC +#undef RETURN_UNLOCK_CSEC diff --git a/src/account/lock.h b/src/account/lock.h new file mode 100644 index 0000000..84b9b0d --- /dev/null +++ b/src/account/lock.h @@ -0,0 +1,27 @@ +#ifndef _LOCK_H +#define _LOCK_H + +#include +#include + +/* Global */ +#define USERNAME_LEN_MAX 33 + +typedef struct lock { + char id [USERNAME_LEN_MAX]; + pthread_mutex_t mutex; + unsigned int instances; +} lock_t; + +typedef struct lock_pool { + GHashTable *hash_table; + pthread_mutex_t csec; +} lock_pool_t; + +int init_lock_pool (void) __attribute__((warn_unused_result)); + +void destroy_lock_pool (void); +int get_lock (const char *const username) __attribute__((nonnull)); +int release_lock (const char *const username) __attribute__((nonnull)); + +#endif /* _LOCK_H */ -- cgit