summaryrefslogtreecommitdiffstats
path: root/src/account
diff options
context:
space:
mode:
authorRobin Hack <rhack@redhat.com>2013-09-10 15:24:55 +0200
committerRobin Hack <rhack@redhat.com>2013-09-10 15:29:52 +0200
commit4df9822196d53a1aaeba3ec0709c86389133bd5a (patch)
treee1607c0d07ed77ecabb458d8e6a73e56ef4eb46c /src/account
parent0097b6401cd129ba818d724e2c5f8625d2437260 (diff)
downloadopenlmi-providers-4df9822196d53a1aaeba3ec0709c86389133bd5a.tar.gz
openlmi-providers-4df9822196d53a1aaeba3ec0709c86389133bd5a.tar.xz
openlmi-providers-4df9822196d53a1aaeba3ec0709c86389133bd5a.zip
Account: Added associative thread locking.
Diffstat (limited to 'src/account')
-rw-r--r--src/account/CMakeLists.txt1
-rw-r--r--src/account/LMI_AccountProvider.c27
-rw-r--r--src/account/lock.c187
-rw-r--r--src/account/lock.h27
4 files changed, 242 insertions, 0 deletions
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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <error.h>
+#include <limits.h>
+#include <glib.h>
+
+#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 <pthread.h>
+#include <glib.h>
+
+/* 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 */