summaryrefslogtreecommitdiff
path: root/ctdb
diff options
context:
space:
mode:
authorAmitay Isaacs <amitay@gmail.com>2015-03-16 23:25:27 +1100
committerAmitay Isaacs <amitay@samba.org>2015-10-07 14:53:28 +0200
commit6272ef0d09930ffbff43c6223ea35858d13efffa (patch)
tree9488286f4ede7c78fcfe3561f58e55cf5f3dbbdd /ctdb
parente5592f9fc018b4b22fbdba4f45526210f5f41692 (diff)
downloadsamba-6272ef0d09930ffbff43c6223ea35858d13efffa.tar.gz
ctdb-common: Add srvid abstraction
Attempt to make common code independent and free from ctdb_context. Signed-off-by: Amitay Isaacs <amitay@gmail.com> Reviewed-by: Martin Schwenke <martin@meltin.net>
Diffstat (limited to 'ctdb')
-rw-r--r--ctdb/common/srvid.c269
-rw-r--r--ctdb/common/srvid.h115
-rwxr-xr-xctdb/tests/cunit/srvid_test_001.sh7
-rw-r--r--ctdb/tests/src/srvid_test.c78
-rwxr-xr-xctdb/wscript3
5 files changed, 471 insertions, 1 deletions
diff --git a/ctdb/common/srvid.c b/ctdb/common/srvid.c
new file mode 100644
index 00000000000..f9cd49b4ea3
--- /dev/null
+++ b/ctdb/common/srvid.c
@@ -0,0 +1,269 @@
+/*
+ Message handler database based on srvid
+
+ Copyright (C) Amitay Isaacs 2015
+
+ 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 "replace.h"
+#include "system/filesys.h"
+
+#include <tdb.h>
+
+#include "lib/util/dlinklist.h"
+#include "common/db_hash.h"
+#include "common/srvid.h"
+
+struct srvid_handler_list;
+
+struct srvid_context {
+ struct db_hash_context *dh;
+ struct srvid_handler_list *list;
+};
+
+struct srvid_handler {
+ struct srvid_handler *prev, *next;
+ struct srvid_handler_list *list;
+ srvid_handler_fn handler;
+ void *private_data;
+};
+
+struct srvid_handler_list {
+ struct srvid_handler_list *prev, *next;
+ struct srvid_context *srv;
+ uint64_t srvid;
+ struct srvid_handler *h;
+};
+
+
+/*
+ * Initialise message srvid context and database
+ */
+int srvid_init(TALLOC_CTX *mem_ctx, struct srvid_context **result)
+{
+ struct srvid_context *srv;
+ int ret;
+
+ srv = talloc_zero(mem_ctx, struct srvid_context);
+ if (srv == NULL) {
+ return ENOMEM;
+ }
+
+ ret = db_hash_init(srv, "messagedb", 8192, DB_HASH_SIMPLE, &srv->dh);
+ if (ret != 0) {
+ talloc_free(srv);
+ return ret;
+ }
+
+ *result = srv;
+ return 0;
+}
+
+/*
+ * Wrapper functions to insert/delete/fetch srvid_hander_list
+ */
+
+static int srvid_insert(struct srvid_context *srv, uint64_t srvid,
+ struct srvid_handler_list *list)
+{
+ return db_hash_insert(srv->dh, (uint8_t *)&srvid, sizeof(uint64_t),
+ (uint8_t *)&list, sizeof(list));
+}
+
+static int srvid_delete(struct srvid_context *srv, uint64_t srvid)
+{
+ return db_hash_delete(srv->dh, (uint8_t *)&srvid, sizeof(uint64_t));
+}
+
+static int srvid_fetch_parser(uint8_t *keybuf, size_t keylen,
+ uint8_t *databuf, size_t datalen,
+ void *private_data)
+{
+ struct srvid_handler_list **list =
+ (struct srvid_handler_list **)private_data;
+
+ if (datalen != sizeof(*list)) {
+ return EIO;
+ }
+
+ *list = *(struct srvid_handler_list **)databuf;
+ return 0;
+}
+
+static int srvid_fetch(struct srvid_context *srv, uint64_t srvid,
+ struct srvid_handler_list **list)
+{
+ return db_hash_fetch(srv->dh, (uint8_t *)&srvid, sizeof(uint64_t),
+ srvid_fetch_parser, list);
+}
+
+/*
+ * When a handler is freed, remove it from the list
+ */
+static int srvid_handler_destructor(struct srvid_handler *h)
+{
+ struct srvid_handler_list *list = h->list;
+
+ DLIST_REMOVE(list->h, h);
+ if (list->h == NULL) {
+ talloc_free(list);
+ }
+ return 0;
+}
+
+/*
+ * When a list is freed, remove all handlers and remove db entry
+ */
+static int srvid_handler_list_destructor(struct srvid_handler_list *list)
+{
+ struct srvid_handler *h;
+
+ while (list->h != NULL) {
+ h = list->h;
+ DLIST_REMOVE(list->h, h);
+ TALLOC_FREE(h);
+ }
+
+ srvid_delete(list->srv, list->srvid);
+ DLIST_REMOVE(list->srv->list, list);
+ return 0;
+}
+
+/*
+ * Register a message handler
+ */
+int srvid_register(struct srvid_context *srv, TALLOC_CTX *mem_ctx,
+ uint64_t srvid, srvid_handler_fn handler,
+ void *private_data)
+{
+ struct srvid_handler_list *list;
+ struct srvid_handler *h;
+ int ret;
+
+ if (srv == NULL) {
+ return EINVAL;
+ }
+
+ h = talloc_zero(mem_ctx, struct srvid_handler);
+ if (h == NULL) {
+ return ENOMEM;
+ }
+
+ h->handler = handler;
+ h->private_data = private_data;
+
+ ret = srvid_fetch(srv, srvid, &list);
+ if (ret != 0) {
+ /* srvid not yet registered */
+ list = talloc_zero(srv, struct srvid_handler_list);
+ if (list == NULL) {
+ talloc_free(h);
+ return ENOMEM;
+ }
+
+ list->srv = srv;
+ list->srvid = srvid;
+
+ ret = srvid_insert(srv, srvid, list);
+ if (ret != 0) {
+ talloc_free(h);
+ talloc_free(list);
+ return ret;
+ }
+
+ DLIST_ADD(srv->list, list);
+ talloc_set_destructor(list, srvid_handler_list_destructor);
+ }
+
+ h->list = list;
+ DLIST_ADD(list->h, h);
+ talloc_set_destructor(h, srvid_handler_destructor);
+ return 0;
+}
+
+/*
+ * Deregister a message handler
+ */
+int srvid_deregister(struct srvid_context *srv, uint64_t srvid,
+ void *private_data)
+{
+ struct srvid_handler_list *list;
+ struct srvid_handler *h;
+ int ret;
+
+ ret = srvid_fetch(srv, srvid, &list);
+ if (ret != 0) {
+ return ret;
+ }
+
+ for (h = list->h; h != NULL; h = h->next) {
+ if (h->private_data == private_data) {
+ talloc_free(h);
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+/*
+ * Check if a message handler exists
+ */
+int srvid_exists(struct srvid_context *srv, uint64_t srvid)
+{
+ struct srvid_handler_list *list;
+ int ret;
+
+ ret = srvid_fetch(srv, srvid, &list);
+ if (ret != 0) {
+ return ret;
+ }
+ if (list->h == NULL) {
+ return ENOENT;
+ }
+
+ return 0;
+}
+
+/*
+ * Send a message to registered srvid and srvid_all
+ */
+int srvid_dispatch(struct srvid_context *srv, uint64_t srvid,
+ uint64_t srvid_all, TDB_DATA data)
+{
+ struct srvid_handler_list *list;
+ struct srvid_handler *h;
+ int ret;
+
+ ret = srvid_fetch(srv, srvid, &list);
+ if (ret == 0) {
+ for (h = list->h; h != NULL; h = h->next) {
+ h->handler(srvid, data, h->private_data);
+ }
+ }
+
+ if (srvid_all == 0) {
+ return ret;
+ }
+
+ ret = srvid_fetch(srv, srvid_all, &list);
+ if (ret == 0) {
+ for (h = list->h; h != NULL; h = h->next) {
+ h->handler(srvid, data, h->private_data);
+ }
+ }
+
+ return ret;
+}
diff --git a/ctdb/common/srvid.h b/ctdb/common/srvid.h
new file mode 100644
index 00000000000..f048b5c6cbf
--- /dev/null
+++ b/ctdb/common/srvid.h
@@ -0,0 +1,115 @@
+/*
+ Message handler database based on srvid
+
+ Copyright (C) Amitay Isaacs 2015
+
+ 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 __CTDB_SRVID_H__
+#define __CTDB_SRVID_H__
+
+#include <talloc.h>
+#include <tdb.h>
+
+/**
+ * @file srvid.h
+ *
+ * @brief Database of message handlers based on srvid
+ *
+ * CTDB can be used to send messages between clients across nodes using
+ * CTDB_REQ_MESSAGE. Clients register for mesages based on srvid. CTDB itself
+ * uses a small set of srvid messages. A large range (2^56) of srvid messages
+ * is reserved for Samba.
+ */
+
+/**
+ * @brief Message handler function
+ *
+ * To receive messages for a specific srvid, register a message handler function
+ * for the srvid.
+ */
+typedef void (*srvid_handler_fn)(uint64_t srvid, TDB_DATA data,
+ void *private_data);
+
+/**
+ * @brief Abstract struct to store srvid message handler database
+ */
+struct srvid_context;
+
+/**
+ * @brief Initialize srvid message handler database
+ *
+ * This returns a new srvid message handler database context. Freeing
+ * this context will free all the memory associated with the hash table.
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[out] result The new db_hash_context structure
+ * @return 0 on success, errno on failure
+ */
+int srvid_init(TALLOC_CTX *mem_ctx, struct srvid_context **result);
+
+/**
+ * @brief Register a message handler for a srvid
+ *
+ * The message handler is allocated using the specified talloc context. Freeing
+ * this talloc context, removes the message handler.
+ *
+ * @param[in] srv The srvid message handler database context
+ * @param[in] mem_ctx Talloc memory context for message handler
+ * @param[in] srvid The srvid
+ * @param[in] handler The message handler function for srvid
+ * @param[in] private_data Private data for message handler function
+ * @return 0 on success, errno on failure
+ */
+int srvid_register(struct srvid_context *srv, TALLOC_CTX *mem_ctx,
+ uint64_t srvid, srvid_handler_fn handler,
+ void *private_data);
+
+/**
+ * @brief Unregister a message handler for a srvid
+ *
+ * @param[in] srv The srvid message handler database context
+ * @param[in] srvid The srvid
+ * @param[in] private_data Private data of message handler function
+ * @return 0 on success, errno on failure
+ */
+int srvid_deregister(struct srvid_context *srv, uint64_t srvid,
+ void *private_data);
+
+/**
+ * @brief Check if any message handler is registered for srvid
+ *
+ * @param[in] srv The srvid message handler database context
+ * @param[in] srvid The srvid
+ * @return 0 on success, errno on failure
+ */
+int srvid_exists(struct srvid_context *srv, uint64_t srvid);
+
+/**
+ * @brief Call message handlers for given srvid
+ *
+ * @param[in] srv The srvid message handler database context
+ * @param[in] srvid The srvid
+ * @param[in] srvid_all The srvid that gets all messages
+ * @param[in] data The data passed to each message handler
+ * @return 0 on success, errno on failure
+ *
+ * If srvid_all passed is 0, the message is not sent to message handlers
+ * registered with special srvid to receive all messages.
+ */
+int srvid_dispatch(struct srvid_context *srv, uint64_t srvid,
+ uint64_t srvid_all, TDB_DATA data);
+
+#endif /* __CTDB_SRVID_H__ */
diff --git a/ctdb/tests/cunit/srvid_test_001.sh b/ctdb/tests/cunit/srvid_test_001.sh
new file mode 100755
index 00000000000..ed09535b1ee
--- /dev/null
+++ b/ctdb/tests/cunit/srvid_test_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test srvid_test
diff --git a/ctdb/tests/src/srvid_test.c b/ctdb/tests/src/srvid_test.c
new file mode 100644
index 00000000000..f5e94ac3d03
--- /dev/null
+++ b/ctdb/tests/src/srvid_test.c
@@ -0,0 +1,78 @@
+/*
+ srvid tests
+
+ Copyright (C) Amitay Isaacs 2015
+
+ 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 "replace.h"
+
+#include <assert.h>
+
+#include "common/db_hash.c"
+#include "common/srvid.c"
+
+#define TEST_SRVID 0xFE11223344556677
+
+static void test_handler(uint64_t srvid, TDB_DATA data, void *private_data)
+{
+ int *count = (int *)private_data;
+ (*count)++;
+}
+
+int main(void)
+{
+ struct srvid_context *srv;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ int ret;
+ int count = 0;
+
+ ret = srvid_init(mem_ctx, &srv);
+ assert(ret == 0);
+
+ ret = srvid_register(srv, tmp_ctx, TEST_SRVID, test_handler, &count);
+ assert(ret == 0);
+
+ ret = srvid_exists(srv, TEST_SRVID);
+ assert(ret == 0);
+
+ ret = srvid_dispatch(srv, TEST_SRVID, 0, tdb_null);
+ assert(ret == 0);
+ assert(count == 1);
+
+ ret = srvid_deregister(srv, TEST_SRVID, NULL);
+ assert(ret == ENOENT);
+
+ ret = srvid_deregister(srv, TEST_SRVID, &count);
+ assert(ret == 0);
+
+ ret = srvid_register(srv, tmp_ctx, TEST_SRVID, test_handler, &count);
+ assert(ret == 0);
+
+ talloc_free(tmp_ctx);
+ ret = srvid_exists(srv, TEST_SRVID);
+ assert(ret == ENOENT);
+
+ ret = srvid_dispatch(srv, TEST_SRVID, 0, tdb_null);
+ assert(ret == ENOENT);
+
+ talloc_free(srv);
+ assert(talloc_get_size(mem_ctx) == 0);
+
+ talloc_free(mem_ctx);
+
+ return 0;
+}
diff --git a/ctdb/wscript b/ctdb/wscript
index afd4d0e278c..aaefad3222a 100755
--- a/ctdb/wscript
+++ b/ctdb/wscript
@@ -333,7 +333,7 @@ def build(bld):
bld.SAMBA_SUBSYSTEM('ctdb-util',
source=bld.SUBDIR('common',
- '''db_hash.c'''),
+ '''db_hash.c srvid.c'''),
deps='replace talloc tevent tdb')
bld.SAMBA_SUBSYSTEM('ctdb-client',
@@ -573,6 +573,7 @@ def build(bld):
# Unit tests
ctdb_unit_tests = [
'db_hash_test',
+ 'srvid_test',
]
for target in ctdb_unit_tests: