/* Authors: Pavel Březina 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 . */ #include #include #include #include "sbus/sssd_dbus.h" #include "providers/data_provider/dp_private.h" #include "providers/backend.h" #include "util/dlinklist.h" #include "util/sss_utf8.h" #include "util/util.h" static int dp_sbus_req_item_destructor(struct dp_sbus_req_item *item) { DLIST_REMOVE(item->parent->list, item); return 0; } static int dp_table_value_destructor(struct dp_table_value *value) { struct dp_sbus_req_item *next_item; struct dp_sbus_req_item *item; DEBUG(SSSDBG_TRACE_FUNC, "Removing [%s] from reply table\n", value->key); dp_req_table_del(value->table, value->key); for (item = value->list; item != NULL; item = next_item) { next_item = item->next; talloc_free(item); } return 0; } static struct dp_sbus_req_item * dp_sbus_req_item_new(struct dp_table_value *value, struct sbus_request *sbus_req) { struct dp_sbus_req_item *item; /* Attach to sbus_request so we ensure that this sbus_req is removed * from the list when it is unexpectedly freed, for example when * client connection is dropped. */ item = talloc_zero(sbus_req, struct dp_sbus_req_item); if (item == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n"); return NULL; } item->parent = value; item->sbus_req = sbus_req; talloc_set_destructor(item, dp_sbus_req_item_destructor); return item; } char *dp_req_table_key(TALLOC_CTX *mem_ctx, enum dp_targets target, enum dp_methods method, uint32_t dp_flags, const char *custom_part) { const char *str = custom_part == NULL ? "(null)" : custom_part; return talloc_asprintf(mem_ctx, "%u:%u:%#.4x:%s", target, method, dp_flags, str); } errno_t dp_req_table_init(TALLOC_CTX *mem_ctx, hash_table_t **_table) { return sss_hash_create(mem_ctx, 100, _table); } struct dp_table_value *dp_req_table_lookup(hash_table_t *table, const char *key) { hash_key_t hkey; hash_value_t hvalue; int hret; hkey.type = HASH_KEY_STRING; hkey.str = discard_const_p(char, key); hret = hash_lookup(table, &hkey, &hvalue); 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; } return hvalue.ptr; } static errno_t dp_req_table_new_item(hash_table_t *table, const char *key, struct tevent_req *req, struct sbus_request *sbus_req) { hash_key_t hkey; hash_value_t hvalue; struct dp_table_value *table_value; errno_t ret; int hret; /* Attach it to request. */ table_value = talloc_zero(req, struct dp_table_value); if (table_value == NULL) { return ENOMEM; } table_value->table = table; table_value->key = talloc_strdup(table_value, key); if (table_value->key == NULL) { ret = ENOMEM; goto done; } table_value->req = req; table_value->list = dp_sbus_req_item_new(table_value, sbus_req); if (table_value->list == NULL) { ret = ENOMEM; goto done; } talloc_set_destructor(table_value, dp_table_value_destructor); hkey.type = HASH_KEY_STRING; hkey.str = discard_const_p(char, key); hvalue.type = HASH_VALUE_PTR; hvalue.ptr = table_value; hret = hash_enter(table, &hkey, &hvalue); if (hret != HASH_SUCCESS) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to enter value into hash table " "[%d]\n", hret); ret = EIO; goto done; } ret = EOK; done: if (ret != EOK) { talloc_free(table_value); } return ret; } static errno_t dp_req_table_mod_item(hash_table_t *table, struct dp_table_value *table_value, struct sbus_request *sbus_req) { struct dp_sbus_req_item *item; item = dp_sbus_req_item_new(table_value, sbus_req); if (item == NULL) { return ENOMEM; } DLIST_ADD(table_value->list, item); return EOK; } errno_t dp_req_table_add(hash_table_t *table, const char *key, struct tevent_req *req, struct sbus_request *sbus_req) { struct dp_table_value *table_value; if (sbus_req == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "SBUS request cannot be NULL\n"); return EINVAL; } table_value = dp_req_table_lookup(table, key); if (table_value == NULL) { if (req == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Tevent request cannot be NULL\n"); return EINVAL; } return dp_req_table_new_item(table, key, req, sbus_req); } return dp_req_table_mod_item(table, table_value, sbus_req); } void dp_req_table_del(hash_table_t *table, const char *key) { hash_key_t hkey; int hret; if (table == NULL || key == NULL) { return; } hkey.type = HASH_KEY_STRING; hkey.str = discard_const_p(char, key); hret = hash_delete(table, &hkey); if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove key from table [%d]\n", hret); } return; } void dp_req_table_del_and_free(hash_table_t *table, const char *key) { struct dp_table_value *value; value = dp_req_table_lookup(table, key); if (value == NULL) { /* We're done here. */ return; } dp_req_table_del(table, key); talloc_free(value); return; } bool dp_req_table_has_key(hash_table_t *table, const char *key) { hash_key_t hkey; hkey.type = HASH_KEY_STRING; hkey.str = discard_const_p(char, key); return hash_has_key(table, &hkey); }