diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/sbus/request/sbus_request_hash.c | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/sbus/request/sbus_request_hash.c')
-rw-r--r-- | src/sbus/request/sbus_request_hash.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/sbus/request/sbus_request_hash.c b/src/sbus/request/sbus_request_hash.c new file mode 100644 index 0000000..0ddad03 --- /dev/null +++ b/src/sbus/request/sbus_request_hash.c @@ -0,0 +1,325 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 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 <tevent.h> + +#include "util/util.h" +#include "util/dlinklist.h" +#include "util/sss_ptr_hash.h" +#include "sbus/sbus_request.h" +#include "sbus/sbus_private.h" + +static void +sbus_requests_disable_spies(struct sbus_request_list *item); + +static void +sbus_requests_validate(struct sbus_request_list *list); + +struct sbus_request_spy { + struct sbus_request_list *item; +}; + +static int +sbus_requests_spy_destructor(struct sbus_request_spy *spy) +{ + struct sbus_request_list *item; + + item = spy->item; + + if (item->spy.conn == spy) { + item->spy.conn = NULL; + item->conn = NULL; + } else { + item->spy.req = NULL; + item->req = NULL; + } + + sbus_requests_finish(item, ERR_TERMINATED); + sbus_requests_validate(item); + + return 0; +} + +static struct sbus_request_spy * +sbus_requests_spy_create(TALLOC_CTX *mem_ctx, + struct sbus_request_list *item) +{ + struct sbus_request_spy *spy; + + spy = talloc_zero(mem_ctx, struct sbus_request_spy); + if (spy == NULL) { + return NULL; + } + + spy->item = item; + + talloc_set_destructor(spy, sbus_requests_spy_destructor); + + return spy; +} + +static errno_t +sbus_requests_attach_spies(struct sbus_request_list *item) +{ + item->spy.conn = sbus_requests_spy_create(item->conn, item); + if (item->spy.conn == NULL) { + return ENOMEM; + } + + item->spy.req = sbus_requests_spy_create(item->req, item); + if (item->spy.req == NULL) { + return ENOMEM; + } + + return EOK; +} + +static void +sbus_requests_disable_spies(struct sbus_request_list *item) +{ + if (item->spy.req != NULL) { + talloc_set_destructor(item->spy.req, NULL); + } + + if (item->spy.conn != NULL) { + talloc_set_destructor(item->spy.conn, NULL); + } + + talloc_zfree(item->spy.req); + talloc_zfree(item->spy.conn); +} + +hash_table_t * +sbus_requests_init(TALLOC_CTX *mem_ctx) +{ + return sss_ptr_hash_create(mem_ctx, NULL, NULL); +} + +errno_t +sbus_requests_add(hash_table_t *table, + const char *key, + struct sbus_connection *conn, + struct tevent_req *req, + bool is_dbus, + bool *_key_exists) +{ + TALLOC_CTX *tmp_ctx; + struct sbus_request_list *list; + struct sbus_request_list *item; + bool key_exists = false; + errno_t ret; + + if (key == NULL) { + /* This is ok, since not all request are supposed to be multicasted. + * The caller will continue as this was a new request. + * And it simplifies the code. */ + *_key_exists = false; + return EOK; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + item = talloc_zero(tmp_ctx, struct sbus_request_list); + if (item == NULL) { + ret = ENOMEM; + goto done; + } + + item->req = req; + item->conn = conn; + item->is_dbus = is_dbus; + + ret = sbus_requests_attach_spies(item); + if (ret != EOK) { + goto done; + } + + /* First, check if the key already exist. If yes, check if the list + * is valid and just append the item to the list if so. Otherwise, + * the list is internally deleted and we can create a new one. */ + list = sss_ptr_hash_lookup(table, key, struct sbus_request_list); + if (list != NULL) { + key_exists = true; + DLIST_ADD_END(list, item, struct sbus_request_list *); + DEBUG(SSSDBG_TRACE_ALL, "Chaining request: %s\n", key); + ret = EOK; + goto done; + } + + /* Otherwise create new hash entry and new list. */ + list = item; + ret = sss_ptr_hash_add(table, key, list, struct sbus_request_list); + +done: + if (ret == EOK) { + if (_key_exists != NULL) { + *_key_exists = key_exists; + } + + talloc_steal(table, item); + } + + talloc_free(tmp_ctx); + + return ret; +} + +struct sbus_request_list * +sbus_requests_lookup(hash_table_t *table, + const char *key) +{ + if (key == NULL) { + /* This is ok, since not all request are supposed to be multicasted. + * The caller will have an empty list ot send notification to. + * And it simplifies the code. */ + return NULL; + } + + return sss_ptr_hash_lookup(table, key, struct sbus_request_list); +} + +void +sbus_requests_delete(struct sbus_request_list *list) +{ + struct sbus_request_list *current, *next; + + if (list == NULL) { + return; + } + + /* Find head of the list. */ + while (list->prev != NULL) { + list = list->prev; + } + + /* Freeing the first item will remove the list also from the table. */ + DLIST_FOR_EACH_SAFE(current, next, list) { + sbus_requests_disable_spies(current); + talloc_zfree(current); + } +} + +static void +sbus_requests_validate(struct sbus_request_list *list) +{ + struct sbus_request_list *current, *next; + + /* Find head of the list. */ + while (list->prev != NULL) { + list = list->prev; + } + + /* An item is invalid if either its request or associated connection + * is freed before this sbus request has finished. + * + * The list is invalid only if all items are invalid or if the first + * item that holds the actual request is invalid. If this is the case + * we will remove this list and report it to the caller. + * + * The sbus request is always associated with the first item. If it + * is invalid we must also terminate all other requests. */ + + if (list->is_invalid) { + DLIST_FOR_EACH_SAFE(current, next, list->next) { + if (current->is_invalid) { + continue; + } + + sbus_requests_disable_spies(current); + tevent_req_error(current->req, ERR_TERMINATED); + } + } else { + DLIST_FOR_EACH_SAFE(current, next, list) { + if (!current->is_invalid) { + return; + } + } + } + + sbus_requests_delete(list); +} + +void +sbus_requests_finish(struct sbus_request_list *item, + errno_t error) +{ + if (item == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "Bug: item is NULL\n"); + return; + } + + if (item->is_invalid) { + return; + } + + /* Make sure that spies are disabled and this item is not handled + * anymore. */ + sbus_requests_disable_spies(item); + item->is_invalid = true; + + if (item->req == NULL) { + return; + } + + if (error != EOK) { + tevent_req_error(item->req, error); + return; + } + + tevent_req_done(item->req); + + item->req = NULL; +} + +void +sbus_requests_terminate_all(hash_table_t *table, + errno_t error) +{ + struct sbus_request_list *list; + struct sbus_request_list *item; + hash_value_t *values; + unsigned long int num; + unsigned long int i; + int hret; + + hret = hash_values(table, &num, &values); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get list of active requests " + "[%d]: %s\n", hret, hash_error_string(hret)); + return; + } + + for (i = 0; i < num; i++) { + list = sss_ptr_get_value(&values[i], struct sbus_request_list); + + DLIST_FOR_EACH(item, list) { + sbus_requests_finish(item, error); + } + + sbus_requests_delete(list); + } + + talloc_free(values); +} |