aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xcs/bindings.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xcs/bindings.c')
-rw-r--r--tools/xcs/bindings.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/tools/xcs/bindings.c b/tools/xcs/bindings.c
new file mode 100644
index 0000000000..9b09f51568
--- /dev/null
+++ b/tools/xcs/bindings.c
@@ -0,0 +1,179 @@
+/* bindings.c
+ *
+ * Manage subscriptions for the control interface switch.
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+/* Interfaces:
+ *
+ * xcs_bind (port, type, connection)
+ * - Register connection to receive messages of this type.
+ * xcs_unbind (port, type, connection)
+ * - Remove an existing registration. (Must be an exact match)
+ * xcs_lookup (port, type)
+ * - Return a list of connections matching a registration.
+ *
+ * - All connections have a connection.bindings list of current bindings.
+ * - (port, type) pairs may be wildcarded with -1.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "xcs.h"
+
+
+typedef struct binding_ent_st {
+ connection_t *con;
+ struct binding_ent_st *next;
+} binding_ent_t;
+
+#define BINDING_TABLE_SIZE 1024
+
+static binding_ent_t *binding_table[BINDING_TABLE_SIZE];
+
+#define PORT_WILD(_ent) ((_ent)->port == PORT_WILDCARD)
+#define TYPE_WILD(_ent) ((_ent)->type == TYPE_WILDCARD)
+#define FULLY_WILD(_ent) (PORT_WILD(_ent) && TYPE_WILD(_ent))
+
+#define BINDING_HASH(_key) \
+ ((((_key)->port * 11) ^ (_key)->type) % BINDING_TABLE_SIZE)
+
+
+void init_bindings(void)
+{
+ memset(binding_table, 0, sizeof(binding_table));
+}
+
+static int table_add(binding_ent_t *table[],
+ connection_t *con,
+ binding_key_t *key)
+{
+ binding_ent_t **curs, *ent;
+
+ curs = &table[BINDING_HASH(key)];
+
+ while (*curs != NULL) {
+ if ((*curs)->con == con) {
+ DPRINTF("Tried to add an ent that already existed.\n");
+ goto done;
+ }
+ curs = &(*curs)->next;
+ }
+
+ if (connection_add_binding(con, key) != 0)
+ {
+ DPRINTF("couldn't add binding on connection (%lu)\n", con->id);
+ goto fail;
+ }
+ ent = (binding_ent_t *)malloc(sizeof(binding_ent_t));
+ if (ent == 0) {
+ DPRINTF("couldn't alloc binding ent!\n");
+ goto fail;
+ }
+ ent->con = con;
+ ent->next = NULL;
+ *curs = ent;
+
+done:
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+static inline int binding_has_colliding_hashes(connection_t *con,
+ binding_key_t *key)
+{
+ int hash, count = 0;
+ binding_key_ent_t *ent;
+
+ ent = con->bindings;
+ hash = BINDING_HASH(key);
+
+ while (ent != NULL) {
+ if (BINDING_HASH(&ent->key) == hash) count ++;
+ ent = ent->next;
+ }
+
+ return (count > 1);
+}
+static int table_remove(binding_ent_t *table[],
+ connection_t *con,
+ binding_key_t *key)
+{
+ binding_ent_t **curs, *ent;
+
+ if (!binding_has_colliding_hashes(con, key))
+ {
+
+ curs = &table[BINDING_HASH(key)];
+
+ while ((*curs != NULL) && ((*curs)->con != con))
+ curs = &(*curs)->next;
+
+ if (*curs != NULL) {
+ ent = *curs;
+ *curs = (*curs)->next;
+ free(ent);
+ }
+ }
+
+ connection_remove_binding(con, key);
+
+ return 0;
+}
+
+int xcs_bind(connection_t *con, int port, u16 type)
+{
+ binding_key_t key;
+
+ key.port = port;
+ key.type = type;
+
+ return table_add(binding_table, con, &key);
+}
+
+int xcs_unbind(connection_t *con, int port, u16 type)
+{
+ binding_key_t key;
+
+ key.port = port;
+ key.type = type;
+
+ return table_remove(binding_table, con, &key);
+}
+
+
+static void for_each_binding(binding_ent_t *list, binding_key_t *key,
+ void (*f)(connection_t *, void *), void *arg)
+{
+ while (list != NULL)
+ {
+ if (connection_has_binding(list->con, key))
+ f(list->con, arg);
+ list = list->next;
+ }
+}
+
+void xcs_lookup(int port, u16 type, void (*f)(connection_t *, void *),
+ void *arg)
+{
+ binding_key_t key;
+
+ key.port = port; key.type = type;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = port; key.type = TYPE_WILDCARD;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = PORT_WILDCARD; key.type = type;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = PORT_WILDCARD; key.type = TYPE_WILDCARD;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+}