summaryrefslogtreecommitdiffstats
path: root/ctdb/server/ctdb_cluster_mutex.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /ctdb/server/ctdb_cluster_mutex.c
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ctdb/server/ctdb_cluster_mutex.c')
-rw-r--r--ctdb/server/ctdb_cluster_mutex.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/ctdb/server/ctdb_cluster_mutex.c b/ctdb/server/ctdb_cluster_mutex.c
new file mode 100644
index 0000000..2fbe301
--- /dev/null
+++ b/ctdb/server/ctdb_cluster_mutex.c
@@ -0,0 +1,382 @@
+/*
+ CTDB cluster mutex handling
+
+ Copyright (C) Andrew Tridgell 2007
+ Copyright (C) Ronnie Sahlberg 2007
+ Copyright (C) Martin Schwenke 2016
+
+ 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/network.h"
+#include "system/filesys.h"
+
+#include <tevent.h>
+
+#include "lib/util/debug.h"
+#include "lib/util/time.h"
+#include "lib/util/strv.h"
+#include "lib/util/strv_util.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/blocking.h"
+
+#include "ctdb_private.h"
+
+#include "ctdb_cluster_mutex.h"
+
+struct ctdb_cluster_mutex_handle {
+ struct ctdb_context *ctdb;
+ cluster_mutex_handler_t handler;
+ void *private_data;
+ cluster_mutex_lost_handler_t lost_handler;
+ void *lost_data;
+ int fd[2];
+ struct tevent_timer *te;
+ struct tevent_fd *fde;
+ pid_t child;
+ struct timeval start_time;
+ bool have_response;
+};
+
+static void cluster_mutex_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ctdb_cluster_mutex_handle *h =
+ talloc_get_type(private_data, struct ctdb_cluster_mutex_handle);
+ double latency = timeval_elapsed(&h->start_time);
+
+ if (h->handler != NULL) {
+ h->handler('2', latency, h->private_data);
+ }
+}
+
+
+/* When the handle is freed it causes any child holding the mutex to
+ * be killed, thus freeing the mutex */
+static int cluster_mutex_destructor(struct ctdb_cluster_mutex_handle *h)
+{
+ if (h->fd[0] != -1) {
+ h->fd[0] = -1;
+ }
+ ctdb_kill(h->ctdb, h->child, SIGTERM);
+ return 0;
+}
+
+/* this is called when the client process has completed ctdb_recovery_lock()
+ and has written data back to us through the pipe.
+*/
+static void cluster_mutex_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct ctdb_cluster_mutex_handle *h=
+ talloc_get_type(private_data, struct ctdb_cluster_mutex_handle);
+ double latency = timeval_elapsed(&h->start_time);
+ char c = '0';
+ int ret;
+
+ /* Got response from child process so abort timeout */
+ TALLOC_FREE(h->te);
+
+ ret = sys_read(h->fd[0], &c, 1);
+
+ /* Don't call the handler more than once. It only exists to
+ * process the initial response from the helper. */
+ if (h->have_response) {
+ /* Only deal with EOF due to process exit. Silently
+ * ignore any other output. */
+ if (ret == 0) {
+ if (h->lost_handler != NULL) {
+ h->lost_handler(h->lost_data);
+ }
+ }
+ return;
+ }
+ h->have_response = true;
+
+ /* If the child wrote status then just pass it to the handler.
+ * If no status was written then this is an unexpected error
+ * so pass generic error code to handler. */
+ if (h->handler != NULL) {
+ h->handler(ret == 1 ? c : '3', latency, h->private_data);
+ }
+}
+
+static char cluster_mutex_helper[PATH_MAX+1] = "";
+
+static bool cluster_mutex_helper_args_file(TALLOC_CTX *mem_ctx,
+ const char *argstring,
+ char ***argv)
+{
+ struct stat st;
+ size_t size = sizeof(cluster_mutex_helper);
+ const char *t;
+ char **args = NULL;
+ int ret;
+
+ if (cluster_mutex_helper[0] != '\0') {
+ goto helper_done;
+ }
+
+ t = getenv("CTDB_CLUSTER_MUTEX_HELPER");
+ if (t != NULL) {
+ size_t len;
+
+ len = strlcpy(cluster_mutex_helper, t, size);
+ if (len >= size) {
+ DBG_ERR("error: CTDB_CLUSTER_MUTEX_HELPER too long\n");
+ exit(1);
+ }
+ } else {
+ ret = snprintf(cluster_mutex_helper,
+ size,
+ "%s/%s",
+ CTDB_HELPER_BINDIR,
+ "ctdb_mutex_fcntl_helper");
+ if (ret < 0 || (size_t)ret >= size) {
+ D_ERR("Unable to set cluster mutex helper - "
+ "path too long\n");
+ exit(1);
+ }
+ }
+
+ ret = stat(cluster_mutex_helper, &st);
+ if (ret != 0) {
+ D_ERR("Unable to set cluster mutex helper \"%s\" - %s\n",
+ cluster_mutex_helper,
+ strerror(errno));
+ exit(1);
+ }
+
+ if ((st.st_mode & S_IXUSR) == 0) {
+ D_ERR("Unable to set cluster_mutex helper \"%s\" - "
+ "not executable\n",
+ cluster_mutex_helper);
+ exit(1);
+ }
+
+ D_NOTICE("Set cluster mutex helper to \"%s\"\n", cluster_mutex_helper);
+
+helper_done:
+
+ /* Array includes default helper, file and NULL */
+ args = talloc_array(mem_ctx, char *, 3);
+ if (args == NULL) {
+ DBG_ERR("Memory allocation error\n");
+ return false;
+ }
+
+ args[0] = cluster_mutex_helper;
+
+ args[1] = talloc_strdup(args, argstring);
+ if (args[1] == NULL) {
+ DBG_ERR("Memory allocation error\n");
+ return false;
+ }
+
+ args[2] = NULL;
+
+ *argv = args;
+ return true;
+}
+
+static bool cluster_mutex_helper_args_cmd(TALLOC_CTX *mem_ctx,
+ const char *argstring,
+ char ***argv)
+{
+ int i, ret, n;
+ char **args = NULL;
+ char *strv = NULL;
+ char *t = NULL;
+
+ ret = strv_split(mem_ctx, &strv, argstring, " \t");
+ if (ret != 0) {
+ D_ERR("Unable to parse mutex helper command \"%s\" (%s)\n",
+ argstring,
+ strerror(ret));
+ return false;
+ }
+ n = strv_count(strv);
+ if (n == 0) {
+ D_ERR("Mutex helper command is empty \"%s\"\n", argstring);
+ return false;
+ }
+
+ /* Extra slot for NULL */
+ args = talloc_array(mem_ctx, char *, n + 1);
+ if (args == NULL) {
+ DBG_ERR("Memory allocation error\n");
+ return false;
+ }
+
+ talloc_steal(args, strv);
+
+ t = NULL;
+ for (i = 0 ; i < n; i++) {
+ t = strv_next(strv, t);
+ args[i] = t;
+ }
+
+ args[n] = NULL;
+
+ *argv = args;
+ return true;
+}
+
+static bool cluster_mutex_helper_args(TALLOC_CTX *mem_ctx,
+ const char *argstring,
+ char ***argv)
+{
+ bool ok;
+
+ if (argstring != NULL && argstring[0] == '!') {
+ ok = cluster_mutex_helper_args_cmd(mem_ctx, &argstring[1], argv);
+ } else {
+ ok = cluster_mutex_helper_args_file(mem_ctx, argstring, argv);
+ }
+
+ return ok;
+}
+
+struct ctdb_cluster_mutex_handle *
+ctdb_cluster_mutex(TALLOC_CTX *mem_ctx,
+ struct ctdb_context *ctdb,
+ const char *argstring,
+ int timeout,
+ cluster_mutex_handler_t handler,
+ void *private_data,
+ cluster_mutex_lost_handler_t lost_handler,
+ void *lost_data)
+{
+ struct ctdb_cluster_mutex_handle *h;
+ char **args;
+ sigset_t sigset_term;
+ int ret;
+
+ h = talloc(mem_ctx, struct ctdb_cluster_mutex_handle);
+ if (h == NULL) {
+ DBG_ERR("out of memory\n");
+ return NULL;
+ }
+
+ h->start_time = timeval_current();
+ h->fd[0] = -1;
+ h->fd[1] = -1;
+ h->have_response = false;
+
+ ret = pipe(h->fd);
+ if (ret != 0) {
+ talloc_free(h);
+ DBG_ERR("Failed to open pipe\n");
+ return NULL;
+ }
+ set_close_on_exec(h->fd[0]);
+
+ /* Create arguments for lock helper */
+ if (!cluster_mutex_helper_args(h, argstring, &args)) {
+ close(h->fd[0]);
+ close(h->fd[1]);
+ talloc_free(h);
+ return NULL;
+ }
+
+ sigemptyset(&sigset_term);
+ sigaddset(&sigset_term, SIGTERM);
+ ret = sigprocmask(SIG_BLOCK, &sigset_term, NULL);
+ if (ret != 0) {
+ DBG_WARNING("Failed to block SIGTERM (%d)\n", errno);
+ }
+
+ h->child = ctdb_fork(ctdb);
+ if (h->child == (pid_t)-1) {
+ close(h->fd[0]);
+ close(h->fd[1]);
+ talloc_free(h);
+ ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
+ if (ret != 0) {
+ DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
+ }
+ return NULL;
+ }
+
+ if (h->child == 0) {
+ struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ };
+
+ ret = sigaction(SIGTERM, &sa, NULL);
+ if (ret != 0) {
+ DBG_WARNING("Failed to reset signal handler (%d)\n",
+ errno);
+ }
+
+ ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
+ if (ret != 0) {
+ DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
+ }
+
+ /* Make stdout point to the pipe */
+ close(STDOUT_FILENO);
+ dup2(h->fd[1], STDOUT_FILENO);
+ close(h->fd[1]);
+
+ execv(args[0], args);
+
+ /* Only happens on error */
+ DBG_ERR("execv() failed\n");
+ _exit(1);
+ }
+
+ /* Parent */
+
+ ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
+ if (ret != 0) {
+ DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
+ }
+
+ DBG_DEBUG("Created PIPE FD:%d\n", h->fd[0]);
+ set_close_on_exec(h->fd[0]);
+
+ close(h->fd[1]);
+ h->fd[1] = -1;
+
+ talloc_set_destructor(h, cluster_mutex_destructor);
+
+ if (timeout != 0) {
+ h->te = tevent_add_timer(ctdb->ev, h,
+ timeval_current_ofs(timeout, 0),
+ cluster_mutex_timeout, h);
+ } else {
+ h->te = NULL;
+ }
+
+ h->fde = tevent_add_fd(ctdb->ev, h, h->fd[0], TEVENT_FD_READ,
+ cluster_mutex_handler, (void *)h);
+
+ if (h->fde == NULL) {
+ talloc_free(h);
+ return NULL;
+ }
+ tevent_fd_set_auto_close(h->fde);
+
+ h->ctdb = ctdb;
+ h->handler = handler;
+ h->private_data = private_data;
+ h->lost_handler = lost_handler;
+ h->lost_data = lost_data;
+
+ return h;
+}