summaryrefslogtreecommitdiffstats
path: root/src/sbus/router/sbus_router_hash.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/sbus/router/sbus_router_hash.c
parentInitial commit. (diff)
downloadsssd-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/router/sbus_router_hash.c')
-rw-r--r--src/sbus/router/sbus_router_hash.c547
1 files changed, 547 insertions, 0 deletions
diff --git a/src/sbus/router/sbus_router_hash.c b/src/sbus/router/sbus_router_hash.c
new file mode 100644
index 0000000..2d407b2
--- /dev/null
+++ b/src/sbus/router/sbus_router_hash.c
@@ -0,0 +1,547 @@
+/*
+ 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 <dhash.h>
+#include <string.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "sbus/sbus_opath.h"
+#include "sbus/sbus_private.h"
+#include "util/sss_ptr_hash.h"
+
+static struct sbus_interface *
+sbus_interface_list_lookup(struct sbus_interface_list *list,
+ const char *name)
+{
+ struct sbus_interface_list *item;
+
+ DLIST_FOR_EACH(item, list) {
+ if (strcmp(item->interface->name, name) == 0) {
+ return item->interface;
+ }
+ }
+
+ return NULL;
+}
+
+static errno_t
+sbus_interface_list_copy(TALLOC_CTX *mem_ctx,
+ struct sbus_interface_list *list,
+ struct sbus_interface_list **_copy)
+{
+ TALLOC_CTX *list_ctx;
+ struct sbus_interface_list *list_copy;
+ struct sbus_interface_list *item_copy;
+ struct sbus_interface_list *item;
+ struct sbus_interface *iface;
+ errno_t ret;
+
+ if (list == NULL) {
+ *_copy = NULL;
+ return EOK;
+ }
+
+ /* Create a memory context that will be used as a parent for copies. */
+ list_ctx = talloc_new(mem_ctx);
+ if (list_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ /* Start with an empty list. */
+ list_copy = NULL;
+ DLIST_FOR_EACH(item, list) {
+ iface = sbus_interface_list_lookup(list_copy, item->interface->name);
+ if (iface != NULL) {
+ /* This interface already exist in the list. */
+ continue;
+ }
+
+ /* Create a copy of this item and insert it into the list. */
+ item_copy = talloc_zero(list_ctx, struct sbus_interface_list);
+ if (item_copy == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item_copy->interface = item->interface;
+ DLIST_ADD(list_copy, item_copy);
+ }
+
+ *_copy = list_copy;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(list_ctx);
+ }
+
+ return ret;
+}
+
+hash_table_t *
+sbus_router_paths_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+errno_t
+sbus_router_paths_add(hash_table_t *table,
+ const char *path,
+ struct sbus_interface *iface)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_interface_list *list;
+ struct sbus_interface_list *item;
+ errno_t ret;
+
+ 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_interface_list);
+ if (item == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item->interface = sbus_interface_copy(item, iface);
+ if (item->interface == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First, check if the path already exist and just append the interface
+ * to the list if it does (but only if the interface does not exist). */
+ list = sss_ptr_hash_lookup(table, path, struct sbus_interface_list);
+ if (list != NULL) {
+ if (sbus_interface_list_lookup(list, iface->name) != NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Trying to register the same interface"
+ " twice: iface=%s, opath=%s\n", iface->name, path);
+ ret = EEXIST;
+ goto done;
+ }
+
+ DLIST_ADD_END(list, item, struct sbus_interface_list *);
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise create new hash entry and new list. */
+ list = item;
+
+ ret = sss_ptr_hash_add(table, path, list, struct sbus_interface_list);
+
+done:
+ if (ret == EOK) {
+ talloc_steal(table, item);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+/**
+ * First @object_path is looked up in @table, if it is not found it steps up
+ * in the path hierarchy and try to lookup the parent node. This continues
+ * until the root is reached.
+ */
+struct sbus_interface *
+sbus_router_paths_lookup(hash_table_t *table,
+ const char *path,
+ const char *iface_name)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_interface_list *list;
+ struct sbus_interface *iface;
+ const char *lookup_path;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ iface = NULL;
+ lookup_path = path;
+ while (lookup_path != NULL) {
+ list = sss_ptr_hash_lookup(table, lookup_path,
+ struct sbus_interface_list);
+ if (list != NULL) {
+ iface = sbus_interface_list_lookup(list, iface_name);
+ if (iface != NULL) {
+ goto done;
+ }
+ }
+
+ /* We will not free lookup path since it is freed with tmp_ctx
+ * and the object paths are supposed to be small. */
+ lookup_path = sbus_opath_subtree_parent(tmp_ctx, lookup_path);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return iface;
+}
+
+/**
+ * Acquire list of all interfaces that are supported on given object path.
+ */
+errno_t
+sbus_router_paths_supported(TALLOC_CTX *mem_ctx,
+ hash_table_t *table,
+ const char *path,
+ struct sbus_interface_list **_list)
+{
+ TALLOC_CTX *tmp_ctx;
+ TALLOC_CTX *list_ctx;
+ struct sbus_interface_list *list;
+ struct sbus_interface_list *list_copy;
+ struct sbus_interface_list *list_output;
+ const char *lookup_path;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ list_ctx = talloc_new(tmp_ctx);
+ if (list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Start with an empty list. */
+ list_output = NULL;
+ lookup_path = path;
+ while (lookup_path != NULL) {
+ list = sss_ptr_hash_lookup(table, lookup_path,
+ struct sbus_interface_list);
+ if (list != NULL) {
+ ret = sbus_interface_list_copy(list_ctx, list, &list_copy);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DLIST_CONCATENATE(list_output, list_copy,
+ struct sbus_interface_list *);
+ }
+
+ /* We will not free lookup path since it is freed with tmp_ctx
+ * and the object paths are supposed to be small. */
+ lookup_path = sbus_opath_subtree_parent(tmp_ctx, lookup_path);
+ }
+
+ talloc_steal(mem_ctx, list_ctx);
+ *_list = list_output;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+const char **
+sbus_router_paths_nodes(TALLOC_CTX *mem_ctx,
+ hash_table_t *table)
+{
+ const char **paths = NULL;
+ hash_key_t *keys;
+ unsigned long count;
+ unsigned long i, j;
+ char *basepath;
+ errno_t ret;
+ int hret;
+
+ hret = hash_keys(table, &count, &keys);
+ if (hret != HASH_SUCCESS) {
+ return NULL;
+ }
+
+ paths = talloc_zero_array(mem_ctx, const char *, count + 2);
+ if (paths == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0, j = 0; i < count; i++) {
+ /* Do not include subtree paths. The must have node factory. */
+ basepath = keys[i].str;
+ if (sbus_opath_is_subtree(basepath)) {
+ basepath = sbus_opath_subtree_base(paths, basepath);
+ if (basepath == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (sbus_router_paths_exist(table, basepath)) {
+ talloc_free(basepath);
+ continue;
+ }
+ }
+
+ if (strcmp(basepath, "/") == 0) {
+ continue;
+ }
+
+ /* All paths starts with / that is not part of the node name. */
+ paths[j] = basepath + 1;
+ j++;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(keys);
+
+ if (ret != EOK) {
+ talloc_zfree(paths);
+ }
+
+ return paths;
+}
+
+bool
+sbus_router_paths_exist(hash_table_t *table,
+ const char *object_path)
+{
+ return sss_ptr_hash_has_key(table, object_path);
+}
+
+static struct sbus_listener *
+sbus_listener_list_lookup(struct sbus_listener_list *list,
+ struct sbus_listener *a)
+{
+ struct sbus_listener_list *item;
+ struct sbus_listener *b;
+
+ /* We know that interface and signal name already match. We need to check
+ * handlers and object paths. */
+ DLIST_FOR_EACH(item, list) {
+ b = item->listener;
+
+ if (memcmp(&a->handler, &b->handler, sizeof(struct sbus_handler)) != 0) {
+ continue;
+ }
+
+ if (a->object_path == NULL && b->object_path == NULL) {
+ return b;
+ }
+
+ if (a->object_path == NULL && b->object_path != NULL) {
+ continue;
+ }
+
+ if (a->object_path != NULL && b->object_path == NULL) {
+ continue;
+ }
+
+ if (strcmp(a->object_path, b->object_path) != 0) {
+ continue;
+ }
+
+ return b;
+ }
+
+ return NULL;
+}
+
+static void
+sbus_router_listeners_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ struct sbus_connection *conn;
+ char *signal_name;
+ char *interface;
+ char *rule;
+ errno_t ret;
+
+ conn = talloc_get_type(pvt, struct sbus_connection);
+ if (conn->connection == NULL) {
+ return;
+ }
+
+ if (conn->disconnecting) {
+ return;
+ }
+
+ /* If we still have the D-Bus connection available, we try to unregister
+ * the previously registered listener when its removed from table. */
+
+ ret = sbus_router_signal_parse(NULL, item->key.str,
+ &interface, &signal_name);
+ if (ret != EOK) {
+ /* There is nothing we can do. */
+ return;
+ }
+
+ rule = sbus_router_signal_rule(NULL, interface, signal_name);
+ talloc_free(interface);
+ talloc_free(signal_name);
+ if (rule == NULL) {
+ /* There is nothing we can do. */
+ return;
+ }
+
+ dbus_bus_remove_match(conn->connection, rule, NULL);
+
+ talloc_free(rule);
+}
+
+hash_table_t *
+sbus_router_listeners_init(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn)
+{
+ return sss_ptr_hash_create(mem_ctx, sbus_router_listeners_delete_cb, conn);
+}
+
+errno_t
+sbus_router_listeners_add(hash_table_t *table,
+ const char *interface,
+ const char *signal_name,
+ struct sbus_listener *listener,
+ bool *_signal_known)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_listener_list *list;
+ struct sbus_listener_list *item;
+ bool signal_known = false;
+ const char *key;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ key = talloc_asprintf(tmp_ctx, "%s.%s", interface, signal_name);
+ if (key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item = talloc_zero(tmp_ctx, struct sbus_listener_list);
+ if (item == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item->listener = sbus_listener_copy(item, listener);
+ if (item->listener == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First, check if the listener already exist and just append it to the
+ * list if it does (but only if this listener doesn't already exist. */
+ list = sss_ptr_hash_lookup(table, key, struct sbus_listener_list);
+ if (list != NULL) {
+ signal_known = true;
+
+ if (sbus_listener_list_lookup(list, listener) != NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Trying to register the same listener"
+ " twice: iface=%s, signal=%s, path=%s\n",
+ interface, signal_name, (listener->object_path == NULL ?
+ "<null>": listener->object_path));
+ ret = EEXIST;
+ goto done;
+ }
+
+ DLIST_ADD_END(list, item, struct sbus_listener_list *);
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise create new hash entry and new list. */
+ signal_known = false;
+ list = item;
+
+ ret = sss_ptr_hash_add(table, key, list, struct sbus_listener_list);
+
+done:
+ if (ret == EOK) {
+ talloc_steal(table, item);
+ *_signal_known = signal_known;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct sbus_listener_list *
+sbus_router_listeners_lookup(hash_table_t *table,
+ const char *interface,
+ const char *signal_name)
+{
+ struct sbus_listener_list *list;
+ char *key;
+
+ key = talloc_asprintf(NULL, "%s.%s", interface, signal_name);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ list = sss_ptr_hash_lookup(table, key, struct sbus_listener_list);
+ talloc_free(key);
+
+ return list;
+}
+
+hash_table_t *
+sbus_router_nodes_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+errno_t
+sbus_router_nodes_add(hash_table_t *table,
+ struct sbus_node *node)
+{
+ struct sbus_node *copy;
+ errno_t ret;
+
+ copy = sbus_node_copy(table, node);
+ if (copy == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ptr_hash_add(table, copy->path, copy, struct sbus_node);
+ if (ret != EOK) {
+ talloc_free(copy);
+ return ret;
+ }
+
+ return EOK;
+}
+
+struct sbus_node *
+sbus_router_nodes_lookup(hash_table_t *table,
+ const char *path)
+{
+ return sss_ptr_hash_lookup(table, path, struct sbus_node);
+}