From cc247678e5f5cd1b5aa88b5b341dc07602482c21 Mon Sep 17 00:00:00 2001 From: Martin Schwenke Date: Thu, 12 Jul 2018 10:44:20 +1000 Subject: [PATCH 1/6] ctdb-event: Fix "ctdb event status" usage message BUG: https://bugzilla.samba.org/show_bug.cgi?id=13551 Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs (cherry picked from commit 56e248de6072063308786ea83282aaecc8d7e62a) --- ctdb/event/event_tool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctdb/event/event_tool.c b/ctdb/event/event_tool.c index 9f755852742..7760c971340 100644 --- a/ctdb/event/event_tool.c +++ b/ctdb/event/event_tool.c @@ -244,7 +244,7 @@ static int event_command_status(TALLOC_CTX *mem_ctx, bool ok; if (argc != 2) { - cmdline_usage(ctx->cmdline, "run"); + cmdline_usage(ctx->cmdline, "status"); return 1; } -- 2.17.1 From 2de2bd2b863137966e2bb3cd8f94d4157f87980e Mon Sep 17 00:00:00 2001 From: Martin Schwenke Date: Thu, 12 Jul 2018 13:20:55 +1000 Subject: [PATCH 2/6] ctdb-common: Factor out basic script abstraction Provides for listing of scripts and chmod enable/disable. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13551 Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs (cherry picked from commit a7a4ee439dc1cf262b4da9fbcb38a2f69c62744c) --- ctdb/common/event_script.c | 246 ++++++++++++++++++++++ ctdb/common/event_script.h | 72 +++++++ ctdb/tests/cunit/event_script_test_001.sh | 125 +++++++++++ ctdb/tests/src/event_script_test.c | 119 +++++++++++ ctdb/wscript | 4 +- 5 files changed, 565 insertions(+), 1 deletion(-) create mode 100644 ctdb/common/event_script.c create mode 100644 ctdb/common/event_script.h create mode 100755 ctdb/tests/cunit/event_script_test_001.sh create mode 100644 ctdb/tests/src/event_script_test.c diff --git a/ctdb/common/event_script.c b/ctdb/common/event_script.c new file mode 100644 index 00000000000..8978d1452c0 --- /dev/null +++ b/ctdb/common/event_script.c @@ -0,0 +1,246 @@ +/* + Low level event script handling + + Copyright (C) Amitay Isaacs 2017 + Copyright (C) Martin Schwenke 2018 + + 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 . +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/dir.h" +#include "system/glob.h" + +#include + +#include "common/event_script.h" + +static int script_filter(const struct dirent *de) +{ + int ret; + + /* Match a script pattern */ + ret = fnmatch("[0-9][0-9].*.script", de->d_name, 0); + if (ret == 0) { + return 1; + } + + return 0; +} + +int event_script_get_list(TALLOC_CTX *mem_ctx, + const char *script_dir, + struct event_script_list **out) +{ + struct dirent **namelist = NULL; + struct event_script_list *script_list = NULL; + size_t ds_len; + int count, ret; + int i; + + count = scandir(script_dir, &namelist, script_filter, alphasort); + if (count == -1) { + ret = errno; + goto done; + } + + script_list = talloc_zero(mem_ctx, struct event_script_list); + if (script_list == NULL) { + goto nomem; + } + + if (count == 0) { + ret = 0; + *out = script_list; + goto done; + } + + script_list->num_scripts = count; + script_list->script = talloc_zero_array(script_list, + struct event_script *, + count); + if (script_list->script == NULL) { + goto nomem; + } + + ds_len = strlen(".script"); + for (i = 0; i < count; i++) { + struct event_script *s; + struct stat statbuf; + + s = talloc_zero(script_list->script, struct event_script); + if (s == NULL) { + goto nomem; + } + + script_list->script[i] = s; + + s->name = talloc_strndup(script_list->script, + namelist[i]->d_name, + strlen(namelist[i]->d_name) - ds_len); + if (s->name == NULL) { + goto nomem; + } + + s->path = talloc_asprintf(script_list->script, + "%s/%s", + script_dir, + namelist[i]->d_name); + if (s->path == NULL) { + goto nomem; + } + + ret = stat(s->path, &statbuf); + if (ret == 0) { + /* + * If ret != 0 this is either a dangling + * symlink or it has just disappeared. Either + * way, it isn't executable. See the note + * below about things that have disappeared. + */ + if (statbuf.st_mode & S_IXUSR) { + s->enabled = true; + } + } + } + + *out = script_list; + return 0; + +nomem: + ret = ENOMEM; + talloc_free(script_list); + +done: + if (namelist != NULL && count != -1) { + for (i=0; i ds_len && + strcmp(&script_name[sn_len - ds_len], dot_script) == 0) { + script_file = script_name; + } else { + ret = snprintf(buf, sizeof(buf), "%s.script", script_name); + if (ret >= sizeof(buf)) { + return ENAMETOOLONG; + } + script_file = buf; + } + + dirp = opendir(script_dir); + if (dirp == NULL) { + return errno; + } + + found = false; + while ((de = readdir(dirp)) != NULL) { + if (strcmp(de->d_name, script_file) == 0) { + /* check for valid script names */ + ret = script_filter(de); + if (ret == 0) { + closedir(dirp); + return EINVAL; + } + + found = true; + found_inode = de->d_ino; + break; + } + } + closedir(dirp); + + if (! found) { + return ENOENT; + } + + ret = snprintf(filename, + sizeof(filename), + "%s/%s", + script_dir, + script_file); + if (ret >= sizeof(filename)) { + return ENAMETOOLONG; + } + + fd = open(filename, O_RDWR); + if (fd == -1) { + ret = errno; + goto done; + } + + ret = fstat(fd, &st); + if (ret != 0) { + ret = errno; + goto done; + } + + /* + * If the directory entry inode number doesn't match the one + * returned by fstat() then this is probably a symlink, so the + * caller should not be calling this function. Note that this + * is a cheap sanity check to catch most programming errors. + * This doesn't cost any extra system calls but can still miss + * the unlikely case where the symlink is to a file on a + * different filesystem with the same inode number as the + * symlink. + */ + if (found && found_inode != st.st_ino) { + ret = EINVAL; + goto done; + } + + if (enable) { + new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH); + } else { + new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH); + } + + ret = fchmod(fd, new_mode); + if (ret != 0) { + ret = errno; + goto done; + } + +done: + if (fd != -1) { + close(fd); + } + return ret; +} diff --git a/ctdb/common/event_script.h b/ctdb/common/event_script.h new file mode 100644 index 00000000000..bf5a8fdf9a2 --- /dev/null +++ b/ctdb/common/event_script.h @@ -0,0 +1,72 @@ +/* + Low level event script handling + + Copyright (C) Amitay Isaacs 2017 + Copyright (C) Martin Schwenke 2018 + + 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 . +*/ + +#ifndef __CTDB_SCRIPT_H__ +#define __CTDB_SCRIPT_H__ + +#include "replace.h" +#include "system/filesys.h" + +#include + +/** + * @file script.h + * + * @brief Script listing and manipulation + */ + + +struct event_script { + char *name; + char *path; + bool enabled; +}; + +struct event_script_list { + unsigned int num_scripts; + struct event_script **script; +}; + + +/** + * @brief Retrieve a list of scripts + * + * @param[in] mem_ctx Talloc memory context + * @param[in] script_dir Directory containing scripts + * @param[out] out List of scripts + * @return 0 on success, errno on failure + */ +int event_script_get_list(TALLOC_CTX *mem_ctx, + const char *script_dir, + struct event_script_list **out); + +/** + * @brief Make a script executable or not executable + * + * @param[in] script_dir Directory containing script + * @param[in] script_name Name of the script to enable + * @param[in] executable True if script should be made executable + * @return 0 on success, errno on failure + */ +int event_script_chmod(const char *script_dir, + const char *script_name, + bool executable); + +#endif /* __CTDB_SCRIPT_H__ */ diff --git a/ctdb/tests/cunit/event_script_test_001.sh b/ctdb/tests/cunit/event_script_test_001.sh new file mode 100755 index 00000000000..9a264122e9a --- /dev/null +++ b/ctdb/tests/cunit/event_script_test_001.sh @@ -0,0 +1,125 @@ +#!/bin/sh + +. "${TEST_SCRIPTS_DIR}/unit.sh" + +scriptdir="${TEST_VAR_DIR}/cunit/scriptdir" +mkdir -p "${scriptdir}" + +test_cleanup "rm -rf ${scriptdir}" + +# Invalid path +invalid="${scriptdir}/notfound" +ok <. +*/ + +#include "replace.h" + +#include +#include + +#include + +#include "common/event_script.c" + +static void usage(const char *prog) +{ + fprintf(stderr, + "Usage: %s list \n", + prog); + fprintf(stderr, + " %s chmod enable \n", + prog); + fprintf(stderr, + " %s chmod diable \n", + prog); +} + +static void do_list(TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + struct event_script_list *script_list = NULL; + int ret, i; + + if (argc != 3) { + usage(argv[0]); + exit(1); + } + + ret = event_script_get_list(mem_ctx, argv[2], &script_list); + if (ret != 0) { + printf("Script list %s failed with result=%d\n", argv[2], ret); + return; + } + + if (script_list == NULL || script_list->num_scripts == 0) { + printf("No scripts found\n"); + return; + } + + for (i=0; i < script_list->num_scripts; i++) { + struct event_script *s = script_list->script[i]; + printf("%s\n", s->name); + } +} + +static void do_chmod(TALLOC_CTX *mem_ctx, + int argc, + const char **argv, + bool enable) +{ + int ret; + + if (argc != 4) { + usage(argv[0]); + exit(1); + } + + ret = event_script_chmod(argv[2], argv[3], enable); + + printf("Script %s %s %s completed with result=%d\n", + argv[1], argv[2], argv[3], ret); +} + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + + if (argc < 3) { + usage(argv[0]); + exit(1); + } + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + fprintf(stderr, "talloc_new() failed\n"); + exit(1); + } + + if (strcmp(argv[1], "list") == 0) { + do_list(mem_ctx, argc, argv); + } else if (strcmp(argv[1], "enable") == 0) { + do_chmod(mem_ctx, argc, argv, true); + } else if (strcmp(argv[1], "disable") == 0) { + do_chmod(mem_ctx, argc, argv, false); + } else { + fprintf(stderr, "Invalid command %s\n", argv[2]); + usage(argv[0]); + } + + talloc_free(mem_ctx); + exit(0); +} diff --git a/ctdb/wscript b/ctdb/wscript index 6e69e499985..05044cebdeb 100644 --- a/ctdb/wscript +++ b/ctdb/wscript @@ -402,7 +402,8 @@ def build(bld): pkt_read.c pkt_write.c comm.c logging.c rb_tree.c tunable.c pidfile.c run_proc.c - hash_count.c run_event.c + hash_count.c + run_event.c event_script.c sock_client.c version.c cmdline.c path.c conf.c line.c '''), @@ -869,6 +870,7 @@ def build(bld): 'cmdline_test', 'conf_test', 'line_test', + 'event_script_test', ] for target in ctdb_unit_tests: -- 2.17.1 From c7d9c11c7a3c5ddaae2bfb5ea747bd891b11f73d Mon Sep 17 00:00:00 2001 From: Martin Schwenke Date: Thu, 12 Jul 2018 13:26:27 +1000 Subject: [PATCH 3/6] ctdb-common: Use script abstraction in run_event BUG: https://bugzilla.samba.org/show_bug.cgi?id=13551 Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs (cherry picked from commit 82e62488027302e541739628210292c2d95717e1) --- ctdb/common/run_event.c | 217 +++++++++----------------------- ctdb/tests/src/run_event_test.c | 1 + 2 files changed, 60 insertions(+), 158 deletions(-) diff --git a/ctdb/common/run_event.c b/ctdb/common/run_event.c index a2155500f28..91b3dd3241a 100644 --- a/ctdb/common/run_event.c +++ b/ctdb/common/run_event.c @@ -31,177 +31,70 @@ #include "common/logging.h" #include "common/run_proc.h" +#include "common/event_script.h" + #include "common/run_event.h" /* * Utility functions */ -static int script_filter(const struct dirent *de) -{ - int ret; - - /* Match a script pattern */ - ret = fnmatch("[0-9][0-9].*.script", de->d_name, 0); - if (ret == 0) { - return 1; - } - - return 0; -} - static int get_script_list(TALLOC_CTX *mem_ctx, const char *script_dir, struct run_event_script_list **out) { - struct dirent **namelist = NULL; + struct event_script_list *s_list; struct run_event_script_list *script_list; - size_t ls; - int count, ret; - int i; + unsigned int i; + int ret; - count = scandir(script_dir, &namelist, script_filter, alphasort); - if (count == -1) { - ret = errno; + ret = event_script_get_list(mem_ctx, script_dir, &s_list); + if (ret != 0) { if (ret == ENOENT) { D_WARNING("event script dir %s removed\n", script_dir); } else { - D_WARNING("scandir() failed on %s, ret=%d\n", + D_WARNING("failed to get script list for %s, ret=%d\n", script_dir, ret); } - *out = NULL; - goto done; + return ret; } - if (count == 0) { + if (s_list->num_scripts == 0) { *out = NULL; - ret = 0; - goto done; + talloc_free(s_list); + return 0; } script_list = talloc_zero(mem_ctx, struct run_event_script_list); if (script_list == NULL) { + talloc_free(s_list); return ENOMEM; } - script_list->num_scripts = count; + script_list->num_scripts = s_list->num_scripts; script_list->script = talloc_zero_array(script_list, struct run_event_script, - count); + script_list->num_scripts); if (script_list->script == NULL) { - ret = ENOMEM; + talloc_free(s_list); talloc_free(script_list); - goto done; - } - - ls = strlen(".script"); - for (i=0; iscript[i]; - - s->name = talloc_strndup(script_list, - namelist[i]->d_name, - strlen(namelist[i]->d_name) - ls); - if (s->name == NULL) { - ret = ENOMEM; - talloc_free(script_list); - goto done; - } - } - - *out = script_list; - ret = 0; - -done: - if (namelist != NULL && count != -1) { - for (i=0; i= sizeof(script_file)) { - return ENAMETOOLONG; - } - - dirp = opendir(script_dir); - if (dirp == NULL) { - return errno; - } - - found = false; - while ((de = readdir(dirp)) != NULL) { - if (strcmp(de->d_name, script_file) == 0) { - - /* check for valid script names */ - ret = script_filter(de); - if (ret == 0) { - closedir(dirp); - return EINVAL; - } - - found = true; - break; - } - } - closedir(dirp); - - if (! found) { - return ENOENT; - } - - filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_file); - if (filename == NULL) { return ENOMEM; } - fd = open(filename, O_RDWR); - if (fd == -1) { - ret = errno; - goto done; - } - - ret = fstat(fd, &st); - if (ret != 0) { - ret = errno; - goto done; - } + for (i = 0; i < s_list->num_scripts; i++) { + struct event_script *s = s_list->script[i]; + struct run_event_script *script = &script_list->script[i]; - if (enable) { - new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH); - } else { - new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH); - } + script->name = talloc_steal(script_list->script, s->name); - ret = fchmod(fd, new_mode); - if (ret != 0) { - ret = errno; - goto done; + if (! s->enabled) { + script->summary = -ENOEXEC; + } } -done: - if (fd != -1) { - close(fd); - } - talloc_free(filename); - return ret; + talloc_free(s_list); + *out = script_list; + return 0; } static int script_args(TALLOC_CTX *mem_ctx, const char *event_str, @@ -392,45 +285,51 @@ int run_event_list(struct run_event_context *run_ctx, TALLOC_CTX *mem_ctx, struct run_event_script_list **output) { + struct event_script_list *s_list; struct run_event_script_list *script_list; int ret, i; - ret = get_script_list(mem_ctx, run_event_script_dir(run_ctx), - &script_list); + ret = event_script_get_list(mem_ctx, + run_event_script_dir(run_ctx), + &s_list); if (ret != 0) { return ret; } - if (script_list == NULL) { + if (s_list->num_scripts == 0) { *output = NULL; + talloc_free(s_list); return 0; } - for (i=0; inum_scripts; i++) { + script_list = talloc_zero(mem_ctx, struct run_event_script_list); + if (script_list == NULL) { + return ENOMEM; + } + + script_list->num_scripts = s_list->num_scripts; + script_list->script = talloc_zero_array(script_list, + struct run_event_script, + script_list->num_scripts); + if (script_list->script == NULL) { + talloc_free(s_list); + talloc_free(script_list); + return ENOMEM; + } + + for (i=0; i < s_list->num_scripts; i++) { + struct event_script *s = s_list->script[i]; struct run_event_script *script = &script_list->script[i]; - struct stat st; - char *path = NULL; - - path = talloc_asprintf(mem_ctx, "%s/%s.script", - run_event_script_dir(run_ctx), - script->name); - if (path == NULL) { - continue; - } - ret = stat(path, &st); - if (ret != 0) { - TALLOC_FREE(path); - continue; - } + script->name = talloc_steal(script_list->script, s->name); - if (! (st.st_mode & S_IXUSR)) { + if (! s->enabled) { script->summary = -ENOEXEC; } - - TALLOC_FREE(path); } + + talloc_free(s_list); *output = script_list; return 0; } @@ -438,15 +337,17 @@ int run_event_list(struct run_event_context *run_ctx, int run_event_script_enable(struct run_event_context *run_ctx, const char *script_name) { - return script_chmod(run_ctx, run_event_script_dir(run_ctx), - script_name, true); + return event_script_chmod(run_event_script_dir(run_ctx), + script_name, + true); } int run_event_script_disable(struct run_event_context *run_ctx, const char *script_name) { - return script_chmod(run_ctx, run_event_script_dir(run_ctx), - script_name, false); + return event_script_chmod(run_event_script_dir(run_ctx), + script_name, + false); } /* diff --git a/ctdb/tests/src/run_event_test.c b/ctdb/tests/src/run_event_test.c index e58d07a9eae..2d0053c459a 100644 --- a/ctdb/tests/src/run_event_test.c +++ b/ctdb/tests/src/run_event_test.c @@ -24,6 +24,7 @@ #include "common/db_hash.c" #include "common/run_proc.c" +#include "common/event_script.c" #include "common/run_event.c" static void usage(const char *prog) -- 2.17.1 From ab5d7b73bf8d3cee3f44ba69f51f8fa5fbdc8b44 Mon Sep 17 00:00:00 2001 From: Martin Schwenke Date: Thu, 12 Jul 2018 13:27:16 +1000 Subject: [PATCH 4/6] ctdb-event: Change event-tool script enable/disable to chmod file directly They no longer go over the socket to eventd to enable and disable scripts. Use the event script abstraction to chmod them directly. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13551 Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs (cherry picked from commit 295826f1b83b6e59d24e4da43b290242c17f44af) --- ctdb/event/event_tool.c | 51 +++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/ctdb/event/event_tool.c b/ctdb/event/event_tool.c index 7760c971340..223e829c343 100644 --- a/ctdb/event/event_tool.c +++ b/ctdb/event/event_tool.c @@ -30,6 +30,7 @@ #include "common/cmdline.h" #include "common/logging.h" #include "common/path.h" +#include "common/event_script.h" #include "event/event_protocol_api.h" #include "event/event.h" @@ -298,44 +299,30 @@ static int event_command_script(TALLOC_CTX *mem_ctx, struct event_tool_context *ctx, const char *component, const char *script, - enum ctdb_event_script_action action) + bool enable) { - struct tevent_req *req; - struct ctdb_event_request_script request_script; - int ret = 0, result = 0; - bool ok; + char *subdir, *etc_dir; + int result = 0; - ret = ctdb_event_init(ctx, ctx->ev, &ctx->eclient); - if (ret != 0) { - D_ERR("Failed to initialize event client, ret=%d\n", ret); - return ret; + subdir = talloc_asprintf(mem_ctx, "events/%s", component); + if (subdir == NULL) { + return ENOMEM; } - request_script.component = component; - request_script.script = script; - request_script.action = action; - - req = ctdb_event_script_send(mem_ctx, - ctx->ev, - ctx->eclient, - &request_script); - if (req == NULL) { - D_ERR("Memory allocation error\n"); - return 1; + etc_dir = path_etcdir_append(mem_ctx, subdir); + if (etc_dir == NULL) { + return ENOMEM; } - tevent_req_poll(req, ctx->ev); - - ok = ctdb_event_script_recv(req, &ret, &result); - if (!ok) { - D_ERR("Failed to %s script, ret=%d\n", - (action == CTDB_EVENT_SCRIPT_DISABLE ? - "disable" : - "enable"), - ret); - return 1; + if (enable) { + result = event_script_chmod(etc_dir, script, true); + } else { + result = event_script_chmod(etc_dir, script, false); } + talloc_free(subdir); + talloc_free(etc_dir); + D_NOTICE("Command script finished with result=%d\n", result); if (result == EINVAL) { @@ -388,7 +375,7 @@ static int event_command_script_enable(TALLOC_CTX *mem_ctx, ctx, argv[0], argv[1], - CTDB_EVENT_SCRIPT_ENABLE); + true); } printf("Script %s is not a file or a link\n", etc_script); @@ -461,7 +448,7 @@ static int event_command_script_disable(TALLOC_CTX *mem_ctx, ctx, argv[0], argv[1], - CTDB_EVENT_SCRIPT_DISABLE); + false); } printf("Script %s is not a file or a link\n", etc_script); -- 2.17.1 From 7f58c61bbc1b86b69b4abdf979f1929bedf8dac9 Mon Sep 17 00:00:00 2001 From: Martin Schwenke Date: Thu, 12 Jul 2018 13:47:51 +1000 Subject: [PATCH 5/6] ctdb-event: Implement event tool "script list" command BUG: https://bugzilla.samba.org/show_bug.cgi?id=13551 Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs (cherry picked from commit 5017325c2ef84b10ccd23328f5d62ac5b246bbb3) --- ctdb/event/event_tool.c | 175 ++++++++++++++++++ .../etc-ctdb/events/data/03.notalink.script | 2 + .../share/events/data/02.disabled.script | 6 + .../eventd/etc-ctdb/share/events/empty/README | 1 + .../share/events/random/01.disabled.script | 3 + .../share/events/random/02.enabled.script | 12 ++ .../share/events/random/README.script | 1 + .../etc-ctdb/share/events/random/a.script | 3 + ctdb/tests/eventd/eventd_001.sh | 5 + ctdb/tests/eventd/eventd_002.sh | 2 + ctdb/tests/eventd/eventd_003.sh | 9 + ctdb/tests/eventd/eventd_009.sh | 116 ++++++++++++ 12 files changed, 335 insertions(+) create mode 100644 ctdb/tests/eventd/etc-ctdb/events/data/03.notalink.script create mode 100755 ctdb/tests/eventd/etc-ctdb/share/events/data/02.disabled.script create mode 100644 ctdb/tests/eventd/etc-ctdb/share/events/empty/README create mode 100644 ctdb/tests/eventd/etc-ctdb/share/events/random/01.disabled.script create mode 100755 ctdb/tests/eventd/etc-ctdb/share/events/random/02.enabled.script create mode 100644 ctdb/tests/eventd/etc-ctdb/share/events/random/README.script create mode 100755 ctdb/tests/eventd/etc-ctdb/share/events/random/a.script diff --git a/ctdb/event/event_tool.c b/ctdb/event/event_tool.c index 223e829c343..4768c678b13 100644 --- a/ctdb/event/event_tool.c +++ b/ctdb/event/event_tool.c @@ -295,6 +295,179 @@ static int event_command_status(TALLOC_CTX *mem_ctx, return ret; } +#define EVENT_SCRIPT_DISABLED ' ' +#define EVENT_SCRIPT_ENABLED '*' + +static int event_command_script_list(TALLOC_CTX *mem_ctx, + int argc, + const char **argv, + void *private_data) +{ + struct event_tool_context *ctx = talloc_get_type_abort( + private_data, struct event_tool_context); + char *subdir = NULL; + char *data_dir = NULL; + char *etc_dir = NULL; + struct event_script_list *data_list = NULL; + struct event_script_list *etc_list = NULL; + unsigned int i, j, matched; + int ret = 0; + + if (argc != 1) { + cmdline_usage(ctx->cmdline, "script list"); + return 1; + } + + subdir = talloc_asprintf(mem_ctx, "events/%s", argv[0]); + if (subdir == NULL) { + ret = ENOMEM; + } + + data_dir = path_datadir_append(mem_ctx, subdir); + if (data_dir == NULL) { + return ENOMEM; + } + + etc_dir = path_etcdir_append(mem_ctx, subdir); + if (etc_dir == NULL) { + return ENOMEM; + } + + /* + * Ignore error on ENOENT for cut down (e.g. fixed/embedded) + * installs that don't use symlinks but just populate etc_dir + * directly + */ + ret = event_script_get_list(mem_ctx, data_dir, &data_list); + if (ret != 0 && ret != ENOENT) { + D_ERR("Command script list finished with result=%d\n", ret); + goto done; + } + + ret = event_script_get_list(mem_ctx, etc_dir, &etc_list); + if (ret != 0) { + D_ERR("Command script list finished with result=%d\n", ret); + goto done; + } + + D_NOTICE("Command script list finished with result=%d\n", ret); + + if (data_list == NULL) { + goto list_enabled_only; + } + + /* + * First list scripts provided by CTDB. Flag those that are + * enabled via a symlink and arrange for them to be excluded + * from the subsequent list of local scripts. + * + * Both lists are sorted, so walk the list of enabled scripts + * only once in this pass. + */ + j = 0; + matched = 0; + for (i = 0; i < data_list->num_scripts; i++) { + struct event_script *d = data_list->script[i]; + char flag = EVENT_SCRIPT_DISABLED; + char buf[PATH_MAX]; + ssize_t len; + + /* Check to see if this script is enabled */ + while (j < etc_list->num_scripts) { + struct event_script *e = etc_list->script[j]; + + ret = strcmp(e->name, d->name); + + if (ret > 0) { + /* + * Enabled name is greater, so needs + * to be considered later: done + */ + break; + } + + if (ret < 0) { + /* Enabled name is less: next */ + j++; + continue; + } + + len = readlink(e->path, buf, sizeof(buf)); + if (len == -1 || len >= sizeof(buf)) { + /* + * Not a link? Disappeared? Invalid + * link target? Something else? + * + * Doesn't match provided script: next, done + */ + j++; + break; + } + + /* readlink() does not NUL-terminate */ + buf[len] = '\0'; + + ret = strcmp(buf, d->path); + if (ret != 0) { + /* Enabled link doesn't match: next, done */ + j++; + break; + } + + /* + * Enabled script's symlink matches our + * script: flag our script as enabled + * + * Also clear the enabled script so it can be + * trivially skipped in the next pass + */ + flag = EVENT_SCRIPT_ENABLED; + TALLOC_FREE(etc_list->script[j]); + j++; + matched++; + break; + } + + printf("%c %s\n", flag, d->name); + } + + /* Print blank line if both provided and local lists are being printed */ + if (data_list->num_scripts > 0 && matched != etc_list->num_scripts) { + printf("\n"); + } + +list_enabled_only: + + /* Now print details of local scripts, after a blank line */ + for (j = 0; j < etc_list->num_scripts; j++) { + struct event_script *e = etc_list->script[j]; + char flag = EVENT_SCRIPT_DISABLED; + + if (e == NULL) { + /* Matched in previous pass: next */ + continue; + } + + /* Script is local: if executable then flag as enabled */ + if (e->enabled) { + flag = EVENT_SCRIPT_ENABLED; + } + + printf("%c %s\n", flag, e->name); + } + + ret = 0; + +done: + talloc_free(subdir); + talloc_free(data_dir); + talloc_free(etc_dir); + talloc_free(data_list); + talloc_free(etc_list); + + return ret; +} + static int event_command_script(TALLOC_CTX *mem_ctx, struct event_tool_context *ctx, const char *component, @@ -463,6 +636,8 @@ struct cmdline_command event_commands[] = { "Run an event", " " }, { "status", event_command_status, "Get status of an event", " " }, + { "script list", event_command_script_list, + "List event scripts", "" }, { "script enable", event_command_script_enable, "Enable an event script", "