From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- ctdb/tests/src/tmon_ping_test.c | 381 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 ctdb/tests/src/tmon_ping_test.c (limited to 'ctdb/tests/src/tmon_ping_test.c') diff --git a/ctdb/tests/src/tmon_ping_test.c b/ctdb/tests/src/tmon_ping_test.c new file mode 100644 index 0000000..c0c0aae --- /dev/null +++ b/ctdb/tests/src/tmon_ping_test.c @@ -0,0 +1,381 @@ +/* + Test trivial FD monitoring + + Copyright (C) Martin Schwenke, DataDirect Networks 2022 + + 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/network.h" +#include "system/wait.h" + +#include +#include +#include + +#include "lib/util/tevent_unix.h" + +#include "common/tmon.h" + +#include "tests/src/test_backtrace.h" + +struct test_state { + const char *label; + unsigned long async_wait_time; + unsigned long blocking_sleep_time; +}; + +static void test_tmon_ping_done(struct tevent_req *subreq); +static void test_async_wait_done(struct tevent_req *subreq); + +static struct tevent_req *test_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *label, + int fd, + int direction, + unsigned long timeout, + unsigned long interval, + unsigned long async_wait_time, + unsigned long blocking_sleep_time) +{ + struct tevent_req *req, *subreq; + struct test_state *state; + + req = tevent_req_create(mem_ctx, &state, struct test_state); + if (req == NULL) { + return NULL; + } + + state->label = label; + state->async_wait_time = async_wait_time; + state->blocking_sleep_time = blocking_sleep_time; + + subreq = tmon_ping_send(state, + ev, + fd, + direction, + timeout, + interval); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, test_tmon_ping_done, req); + + if (state->async_wait_time != 0) { + fprintf(stderr, + "%s: async wait start %lu\n", + state->label, + state->async_wait_time); + } + subreq = tevent_wakeup_send(state, + ev, + tevent_timeval_current_ofs( + (uint32_t)async_wait_time, 0)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, test_async_wait_done, req); + + return req; +} + +static void test_tmon_ping_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct test_state *state = tevent_req_data(req, struct test_state); + bool status; + int err; + + status = tmon_ping_recv(subreq, &err); + TALLOC_FREE(subreq); + if (!status) { + switch(err) { + case EPIPE: + fprintf(stderr, "%s: pipe closed\n", state->label); + break; + case ETIMEDOUT: + fprintf(stderr, "%s: ping timeout\n", state->label); + break; + default: + fprintf(stderr, "%s: error (%d)\n", state->label, err); + } + tevent_req_error(req, err); + return; + } + + fprintf(stderr, "%s: done\n", state->label); + tevent_req_done(req); +} + +static void test_async_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct test_state *state = tevent_req_data(req, struct test_state); + unsigned int left; + bool status; + + status = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!status) { + fprintf(stderr, + "%s: tevent_wakeup_recv() failed\n", + state->label); + /* Ignore error */ + } + if (state->async_wait_time != 0) { + fprintf(stderr, "%s: async wait end\n", state->label); + } + + if (state->blocking_sleep_time == 0) { + goto done; + } + + fprintf(stderr, + "%s: blocking sleep start %lu\n", + state->label, + state->blocking_sleep_time); + left = sleep((unsigned int)state->blocking_sleep_time); + fprintf(stderr, + "%s: blocking sleep end\n", + state->label); + if (left != 0) { + tevent_req_error(req, EINTR); + return; + } + +done: + tevent_req_done(req); +} + +static bool test_recv(struct tevent_req *req, int *perr) +{ + if (tevent_req_is_unix_error(req, perr)) { + return false; + } + + return true; +} + +static int test_one(bool is_parent, + int sync_fd, + int fd, + int direction, + unsigned long timeout, + unsigned long interval, + unsigned long async_wait_time, + unsigned long blocking_sleep_time) +{ + TALLOC_CTX *mem_ctx; + struct tevent_context *ev; + struct tevent_req *req; + bool status; + char buf[1] = ""; + ssize_t count; + int err; + int ret; + + if (!is_parent) { + count = read(sync_fd, buf, sizeof(buf)); + assert(count == 1); + assert(buf[0] == '\0'); + close(sync_fd); + } + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ev = tevent_context_init(mem_ctx); + if (ev == NULL) { + ret = ENOMEM; + goto done; + } + + req = test_send(mem_ctx, + ev, + is_parent ? "parent" : "child", + fd, + direction, + timeout, + interval, + async_wait_time, + blocking_sleep_time); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + if (is_parent) { + count = write(sync_fd, buf, sizeof(buf)); + assert(count == 1); + } + + status = tevent_req_poll(req, ev); + if (!status) { + ret = EIO; + goto done; + } + + status = test_recv(req, &err); + ret = status ? 0 : err; + +done: + return ret; +} + +static void test(unsigned long parent_timeout, + unsigned long parent_interval, + unsigned long parent_async_wait_time, + unsigned long parent_blocking_sleep_time, + int parent_result, + unsigned long child_timeout, + unsigned long child_interval, + unsigned long child_async_wait_time, + unsigned long child_blocking_sleep_time, + int child_result) +{ + int sync[2]; + int fd[2]; + pid_t pid; + int wstatus; + int ret; + + /* Pipe for synchronisation */ + ret = pipe(sync); + assert(ret == 0); + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + assert(ret == 0); + + pid = fork(); + assert(pid != -1); + + if (pid == 0) { + /* child */ + close(sync[1]); + close(fd[0]); + + ret = test_one(false, + sync[0], + fd[1], + TMON_FD_BOTH, + child_timeout, + child_interval, + child_async_wait_time, + child_blocking_sleep_time); + _exit(ret); + } + + /* Parent */ + close(sync[0]); + close(fd[1]); + + ret = test_one(true, + sync[1], + fd[0], + TMON_FD_BOTH, + parent_timeout, + parent_interval, + parent_async_wait_time, + parent_blocking_sleep_time); + assert(ret == parent_result); + + /* Close to mimic exit, so child status can be checked below */ + close(fd[0]); + + /* Abort if child failed */ + waitpid(pid, &wstatus, 0); + if (WIFEXITED(wstatus)) { + assert(WEXITSTATUS(wstatus) == child_result); + } +} + +struct test_inputs { + unsigned int timeout; + unsigned int interval; + unsigned int async_wait_time; + unsigned int blocking_sleep_time; + int expected_result; +}; + +static void get_test_inputs(const char **args, struct test_inputs *inputs) +{ + if (strcmp(args[0], "false") == 0) { + inputs->interval = 0; + } else if (strcmp(args[0], "true") == 0) { + inputs->interval = 1; + } else { + inputs->interval = strtoul(args[0], NULL, 0); + } + + inputs->timeout = strtoul(args[1], NULL, 0); + inputs->async_wait_time = (unsigned int)strtoul(args[2], NULL, 0); + inputs->blocking_sleep_time = (unsigned int)strtoul(args[3], NULL, 0); + inputs->expected_result = (int)strtoul(args[4], NULL, 0); +} + +static void usage(const char *prog) +{ + fprintf(stderr, + "usage: %s " + "\\\n\t" + " " + " " + " " + " " + " " + "\\\n\t" + " " + " " + " " + " " + " " + "\n", + prog); + exit(1); +} + +int main(int argc, const char **argv) +{ + struct test_inputs parent; + struct test_inputs child; + + if (argc != 11) { + usage(argv[0]); + } + + test_backtrace_setup(); + + get_test_inputs(&argv[1], &parent); + get_test_inputs(&argv[6], &child); + + test(parent.timeout, + parent.interval, + parent.async_wait_time, + parent.blocking_sleep_time, + parent.expected_result, + child.timeout, + child.interval, + child.async_wait_time, + child.blocking_sleep_time, + child.expected_result); + + return 0; +} -- cgit v1.2.3