summaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
authorPavel Březina <pbrezina@redhat.com>2016-11-10 13:11:41 +0100
committerLukas Slebodnik <lslebodn@redhat.com>2016-12-19 23:28:46 +0100
commita5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e (patch)
treecde9d4432f98a348309755ac2611984ff56430c2 /src/util
parent7162dc780fe9458018c577f6f1638522d74f63b0 (diff)
downloadsssd-a5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e.tar.gz
sssd-a5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e.tar.xz
sssd-a5a3bbb0bbaeb8946c228c2fb7f0cf450595dd3e.zip
utils: add sss_ptr_hash module
We often work with <string, pointer> type of hash table throughout sssd. This module creates and maintains such hash table and makes sure that hash entry is destroyed when original value is freed. Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Diffstat (limited to 'src/util')
-rw-r--r--src/util/sss_ptr_hash.c375
-rw-r--r--src/util/sss_ptr_hash.h117
2 files changed, 492 insertions, 0 deletions
diff --git a/src/util/sss_ptr_hash.c b/src/util/sss_ptr_hash.c
new file mode 100644
index 000000000..e34b9b2c1
--- /dev/null
+++ b/src/util/sss_ptr_hash.c
@@ -0,0 +1,375 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <dhash.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+
+static bool sss_ptr_hash_check_type(void *ptr, const char *type)
+{
+ void *type_ptr;
+
+ type_ptr = talloc_check_name(ptr, type);
+ if (type_ptr == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid data type detected. Expected [%s], got [%s].\n",
+ type, talloc_get_name(ptr));
+ return false;
+ }
+
+ return true;
+}
+
+struct sss_ptr_hash_delete_data {
+ hash_delete_callback *callback;
+ void *pvt;
+};
+
+struct sss_ptr_hash_value {
+ struct sss_ptr_hash_spy *spy;
+ void *ptr;
+};
+
+struct sss_ptr_hash_spy {
+ struct sss_ptr_hash_value *value;
+ hash_table_t *table;
+ const char *key;
+};
+
+static int
+sss_ptr_hash_spy_destructor(struct sss_ptr_hash_spy *spy)
+{
+ spy->value->spy = NULL;
+ spy->value->ptr = NULL;
+
+ /* This results in removing entry from hash table and freeing the value. */
+ sss_ptr_hash_delete(spy->table, spy->key, false);
+ return 0;
+}
+
+static struct sss_ptr_hash_spy *
+sss_ptr_hash_spy_create(TALLOC_CTX *mem_ctx,
+ hash_table_t *table,
+ const char *key,
+ struct sss_ptr_hash_value *value)
+{
+ struct sss_ptr_hash_spy *spy;
+
+ spy = talloc_zero(mem_ctx, struct sss_ptr_hash_spy);
+ if (spy == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ spy->key = talloc_strdup(spy, key);
+ if (spy->key == NULL) {
+ talloc_free(spy);
+ return NULL;
+ }
+
+ spy->table = table;
+ spy->value = value;
+ talloc_set_destructor(spy, sss_ptr_hash_spy_destructor);
+
+ return spy;
+}
+
+static int
+sss_ptr_hash_value_destructor(struct sss_ptr_hash_value *value)
+{
+ if (value->spy != NULL) {
+ /* Disable spy destructor and free it. */
+ talloc_set_destructor(value->spy, NULL);
+ talloc_zfree(value->spy);
+ }
+
+ return 0;
+}
+
+static struct sss_ptr_hash_value *
+sss_ptr_hash_value_create(hash_table_t *table,
+ const char *key,
+ void *talloc_ptr)
+{
+ struct sss_ptr_hash_value *value;
+
+ value = talloc_zero(table, struct sss_ptr_hash_value);
+ if (value == NULL) {
+ return NULL;
+ }
+
+ value->spy = sss_ptr_hash_spy_create(talloc_ptr, table, key, value);
+ if (value->spy == NULL) {
+ talloc_free(value);
+ return NULL;
+ }
+
+ value->ptr = talloc_ptr;
+ talloc_set_destructor(value, sss_ptr_hash_value_destructor);
+
+ return value;
+}
+
+static void
+sss_ptr_hash_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ struct sss_ptr_hash_delete_data *data;
+ struct sss_ptr_hash_value *value;
+ void *ptr;
+
+ data = talloc_get_type(pvt, struct sss_ptr_hash_delete_data);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid data!\n");
+ return;
+ }
+
+ value = talloc_get_type(item->value.ptr, struct sss_ptr_hash_value);
+ if (value == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value!\n");
+ return;
+ }
+
+ ptr = value->ptr;
+
+ /* Free value. */
+ talloc_free(value);
+
+ /* Switch to the input value and call custom callback. */
+ if (data->callback != NULL) {
+ item->value.ptr = ptr;
+ data->callback(item, deltype, data->pvt);
+ }
+}
+
+hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
+ hash_delete_callback *del_cb,
+ void *del_cb_pvt)
+{
+ struct sss_ptr_hash_delete_data *data;
+ hash_table_t *table;
+ errno_t ret;
+
+ data = talloc_zero(mem_ctx, struct sss_ptr_hash_delete_data);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ data->callback = del_cb;
+ data->pvt = del_cb_pvt;
+
+ ret = sss_hash_create_ex(NULL, 10, &table, 0, 0, 0, 0,
+ sss_ptr_hash_delete_cb, data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
+ ret, sss_strerror(ret));
+ talloc_free(data);
+ return NULL;
+ }
+
+ talloc_steal(table, data);
+
+ return table;
+}
+
+errno_t _sss_ptr_hash_add(hash_table_t *table,
+ const char *key,
+ void *talloc_ptr,
+ const char *type,
+ bool override)
+{
+ struct sss_ptr_hash_value *value;
+ hash_value_t table_value;
+ hash_key_t table_key;
+ int hret;
+
+ if (table == NULL || key == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input!\n");
+ return EINVAL;
+ }
+
+ if (!sss_ptr_hash_check_type(talloc_ptr, type)) {
+ return ERR_INVALID_DATA_TYPE;
+ }
+
+ value = sss_ptr_hash_value_create(table, key, talloc_ptr);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, key);
+
+ table_value.type = HASH_VALUE_PTR;
+ table_value.ptr = value;
+
+ if (override == false && hash_has_key(table, &table_key)) {
+ return EEXIST;
+ }
+
+ hret = hash_enter(table, &table_key, &table_value);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add key %s!\n", key);
+ talloc_free(value);
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static struct sss_ptr_hash_value *
+sss_ptr_hash_lookup_internal(hash_table_t *table,
+ const char *key)
+{
+ hash_value_t table_value;
+ hash_key_t table_key;
+ int hret;
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, key);
+
+ hret = hash_lookup(table, &table_key, &table_value);
+ if (hret == HASH_ERROR_KEY_NOT_FOUND) {
+ return NULL;
+ } else if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search hash table [%d]\n", hret);
+ return NULL;
+ }
+
+ /* Check value type. */
+ if (table_value.type != HASH_VALUE_PTR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value type found: %d\n",
+ table_value.type);
+ return NULL;
+ }
+
+ if (!sss_ptr_hash_check_type(table_value.ptr, "struct sss_ptr_hash_value")) {
+ return NULL;
+ }
+
+ return table_value.ptr;
+}
+
+void *_sss_ptr_hash_lookup(hash_table_t *table,
+ const char *key,
+ const char *type)
+{
+ struct sss_ptr_hash_value *value;
+
+ value = sss_ptr_hash_lookup_internal(table, key);
+ if (value == NULL || value->ptr == NULL) {
+ return NULL;
+ }
+
+ if (!sss_ptr_hash_check_type(value->ptr, type)) {
+ return NULL;
+ }
+
+ return value->ptr;
+}
+
+void sss_ptr_hash_delete(hash_table_t *table,
+ const char *key,
+ bool free_value)
+{
+ struct sss_ptr_hash_value *value;
+ hash_key_t table_key;
+ int hret;
+ void *ptr;
+
+ if (table == NULL || key == NULL) {
+ return;
+ }
+
+ value = sss_ptr_hash_lookup_internal(table, key);
+ if (value == NULL) {
+ /* Value not found. */
+ return;
+ }
+
+ ptr = value->ptr;
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, key);
+
+ /* Delete table entry. This will free value and spy in delete callback. */
+ hret = hash_delete(table, &table_key);
+ if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove key from table [%d]\n",
+ hret);
+ }
+
+ /* Also free the original value if requested. */
+ if (free_value) {
+ talloc_free(ptr);
+ }
+
+ return;
+}
+
+void sss_ptr_hash_delete_all(hash_table_t *table,
+ bool free_values)
+{
+ struct sss_ptr_hash_value *value;
+ hash_value_t *values;
+ size_t count;
+ size_t i;
+ int hret;
+ void *ptr;
+
+ if (table == NULL) {
+ return;
+ }
+
+ hret = hash_values(table, &count, &values);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get values [%d]\n", hret);
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ value = values[i].ptr;
+ ptr = value->ptr;
+
+ /* This will remove the entry from hash table and free value. */
+ talloc_free(value->spy);
+
+ if (free_values) {
+ /* Also free the original value. */
+ talloc_free(ptr);
+ }
+ }
+
+ return;
+}
+
+bool sss_ptr_hash_has_key(hash_table_t *table,
+ const char *key)
+{
+ hash_key_t table_key;
+
+ table_key.type = HASH_KEY_STRING;
+ table_key.str = discard_const_p(char, key);
+
+ return hash_has_key(table, &table_key);
+}
diff --git a/src/util/sss_ptr_hash.h b/src/util/sss_ptr_hash.h
new file mode 100644
index 000000000..510b9544f
--- /dev/null
+++ b/src/util/sss_ptr_hash.h
@@ -0,0 +1,117 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_PTR_HASH_H_
+#define _SSS_PTR_HASH_H_
+
+#include <talloc.h>
+#include <dhash.h>
+
+/**
+ * Create a new hash table with string key and talloc pointer value with
+ * possible delete callback.
+ */
+hash_table_t *sss_ptr_hash_create(TALLOC_CTX *mem_ctx,
+ hash_delete_callback *del_cb,
+ void *del_cb_pvt);
+
+/**
+ * Add a new value @talloc_ptr of type @type into the table.
+ *
+ * If the @key already exist in the table and @override is true,
+ * the value is overridden. Otherwise EEXIST error is returned.
+ *
+ * If talloc_ptr is freed the key and value are automatically
+ * removed from the hash table.
+ *
+ * @return EOK If the <@key, @talloc_ptr> pair was inserted.
+ * @return EEXIST If @key already exists and @override is false.
+ * @return Other errno code in case of an error.
+ */
+errno_t _sss_ptr_hash_add(hash_table_t *table,
+ const char *key,
+ void *talloc_ptr,
+ const char *type,
+ bool override);
+
+/**
+ * Add a new value @talloc_ptr of type @type into the table.
+ *
+ * If talloc_ptr is freed the key and value are automatically
+ * removed from the hash table.
+ *
+ * @return EOK If the <@key, @talloc_ptr> pair was inserted.
+ * @return EEXIST If @key already exists.
+ * @return Other errno code in case of an error.
+ */
+#define sss_ptr_hash_add(table, key, talloc_ptr, type) \
+ _sss_ptr_hash_add(table, key, talloc_ptr, #type, false)
+
+/**
+ * Add a new value @talloc_ptr of type @type into the table.
+ *
+ * If the @key already exists in the table, its value is
+ * overridden. If talloc_ptr is freed the key and value
+ * are automatically removed from the hash table.
+ *
+ * @return EOK If the <@key, @talloc_ptr> pair was inserted.
+ * @return Other errno code in case of an error.
+ */
+#define sss_ptr_hash_add_or_override(table, key, talloc_ptr, type) \
+ _sss_ptr_hash_add(table, key, talloc_ptr, #type, true)
+
+void *_sss_ptr_hash_lookup(hash_table_t *table,
+ const char *key,
+ const char *type);
+
+/**
+ * Lookup @key in the table and return its value as typed to @type.
+ * The type of the value must match with @type, otherwise NULL is returned.
+ *
+ * @return talloc_ptr If the value is found as type matches.
+ * @return NULL If the value is not found or if the type is invalid.
+ */
+#define sss_ptr_hash_lookup(table, key, type) \
+ (type *)_sss_ptr_hash_lookup(table, key, #type)
+
+/**
+ * Delete @key from table. If @free_value is true then also the value
+ * associated with @key is freed, otherwise it is left intact.
+ */
+void sss_ptr_hash_delete(hash_table_t *table,
+ const char *key,
+ bool free_value);
+
+/**
+ * Delete all keys from the table. If @free_value sis true then also
+ * the values associated with those keys are reed, otherwise
+ * they are left intact.
+ */
+void sss_ptr_hash_delete_all(hash_table_t *table,
+ bool free_values);
+
+/**
+ * @return true If @key is present in the table.
+ * @return false Otherwise.
+ */
+bool sss_ptr_hash_has_key(hash_table_t *table,
+ const char *key);
+
+#endif /* _SSS_PTR_HASH_H_ */